有时您在 Java 中有一个混合集合。一个简单的例子是具有List<Number>,其中所述列表可以包含Integer,Float,Long和Double实例。如何轻松过滤掉List<Integer>或List<Double>?我将演示如何使用经典 Java 执行此操作,在 Java 16 中使用模式匹配 instanceof,并在Eclipse Collections 中使用两种不同的方法。我还使用 JDK 17 EA 和 Switch 中的模式匹配添加了一个预览解决方案。
解决方案:经典 Java
使用标准 Java foreach 循环解决问题的一种方法。
@Test public void classicJava() { List<Number> numbers = List.of(1, 2L, 3.0, 4.0f); List<Integer> integers = new ArrayList<>(); List<Long> longs = new ArrayList<>(); List<Double> doubles = new ArrayList<>(); List<Float> floats = new ArrayList<>(); for (Number each : numbers) { if (each instanceof Integer) { integers.add((Integer) each); } else if (each instanceof Long) { longs.add((Long) each); } else if (each instanceof Double) { doubles.add((Double) each); } else if (each instanceof Float) { floats.add((Float) each); } }
Assertions.assertEquals(List.of(1), integers); Assertions.assertEquals(List.of(2L), longs); Assertions.assertEquals(List.of(3.0), doubles); Assertions.assertEquals(List.of(4.0f), floats);
|
这种方法的好处是我们只对数字列表迭代一次。缺点是它非常冗长,并且需要强制转换。
解决方案:Java 16 — instanceof 的模式匹配
@Test public void patternMatchingForInstanceOfJava16() { List<Number> numbers = List.of(1, 2L, 3.0, 4.0f); List<Integer> integers = new ArrayList<>(); List<Long> longs = new ArrayList<>(); List<Double> doubles = new ArrayList<>(); List<Float> floats = new ArrayList<>(); numbers.forEach(number -> { if (number instanceof Integer each) { integers.add(each); } else if (number instanceof Long each) { longs.add(each); } else if (number instanceof Float each) { floats.add(each); } else if (number instanceof Double each) { doubles.add(each); } }); Assertions.assertEquals(List.of(1), integers); Assertions.assertEquals(List.of(2L), longs); Assertions.assertEquals(List.of(3.0), doubles); Assertions.assertEquals(List.of(4.0f), floats); }
|
这样做的好处是我们只迭代一次数字列表,并且没有不安全的转换。缺点是它仍然很冗长。这将在 Java 17 中通过模式匹配进行切换。
解决方案:Eclipse Collections selectInstanceOf@Test public void selectInstancesOf() { MutableList<Number> numbers = Lists.mutable.with(1, 2L, 3.0, 4.0f); MutableList<Integer> integers = numbers.selectInstancesOf(Integer.class); MutableList<Long> longs = numbers.selectInstancesOf(Long.class); MutableList<Double> doubles = numbers.selectInstancesOf(Double.class); MutableList<Float> floats = numbers.selectInstancesOf(Float.class);
Assertions.assertEquals(Lists.mutable.with(1), integers); Assertions.assertEquals(Lists.mutable.with(2L), longs); Assertions.assertEquals(Lists.mutable.with(3.0), doubles); Assertions.assertEquals(Lists.mutable.with(4.0f), floats); }
|
这种方法的优点是非常简洁。缺点是它需要对数字列表进行多次迭代。
解决方案:Eclipse Collections CaseProcedure
@Test public void caseProcedure() { MutableList<Number> numbers = Lists.mutable.with(1, 2L, 3.0, 4.0f); MutableList<Integer> integers = Lists.mutable.empty(); MutableList<Long> longs = Lists.mutable.empty(); MutableList<Double> doubles = Lists.mutable.empty(); MutableList<Float> floats = Lists.mutable.empty();
numbers.forEach(new CaseProcedure<>() .addCase(Integer.class::isInstance, each -> integers.add((Integer) each)) .addCase(Float.class::isInstance, each -> floats.add((Float) each)) .addCase(Long.class::isInstance, each -> longs.add((Long) each)) .addCase(Double.class::isInstance, each -> doubles.add((Double) each)));
Assertions.assertEquals(Lists.mutable.with(1), integers); Assertions.assertEquals(Lists.mutable.with(2L), longs); Assertions.assertEquals(Lists.mutable.with(3.0), doubles); Assertions.assertEquals(Lists.mutable.with(4.0f), floats); }
|
这种方法的优点是它相对简洁,只需要遍历数字列表一次。缺点是它需要显式和不安全的强制转换。
解决方案:Java 17 — Switch 中的模式匹配
当 Java 17 发布时,我们可以解决在 switch 中使用模式匹配的问题。
@Test public void patternMatchingForSwitchJava17() { List<Number> numbers = List.of(1, 2L, 3.0, 4.0f); List<Integer> integers = new ArrayList<>(); List<Long> longs = new ArrayList<>(); List<Double> doubles = new ArrayList<>(); List<Float> floats = new ArrayList<>(); numbers.forEach(number -> { switch(number) { case Integer each -> integers.add(each); case Float each -> floats.add(each); case Long each -> longs.add(each); case Double each -> doubles.add(each); default -> { break; } }; }); Assertions.assertEquals(List.of(1), integers); Assertions.assertEquals(List.of(2L), longs); Assertions.assertEquals(List.of(3.0), doubles); Assertions.assertEquals(List.of(4.0f), floats); }
|
这种方法的优点是简洁、清晰和单遍迭代。我想不出任何缺点。
实验:Java 17 — Switch、Var 和 Eclipse 集合中的记录、模式匹配注入
这纯粹是我在安装并运行 Java 17 EA 后添加的思想实验。我想看看在 Switch 中使用 Java 记录和 Eclipse Collections injectInto 和 Pattern Matching 可以做什么。
@Test public void recordsPatternMatchingInSwitchAndInjectInto() { record Numbers(List<Integer> integers, List<Long> longs, List<Double> doubles, List<Float> floats) { public Numbers() { this(mList(), mList(), mList(), mList()); }
Numbers filter(Number number) { switch(number) { case Integer each -> integers.add(each); case Float each -> floats.add(each); case Long each -> longs.add(each); case Double each -> doubles.add(each); default -> { break; } } return this; } }
var numbers = Lists.mutable.with(1, 2L, 3.0, 4.0f); var filtered = numbers.injectInto(new Numbers(), Numbers::filter);
Assertions.assertEquals(List.of(1), filtered.integers); Assertions.assertEquals(List.of(2L), filtered.longs); Assertions.assertEquals(List.of(3.0), filtered.doubles); Assertions.assertEquals(List.of(4.0f), filtered.floats); }
|
一些想法
根据类型过滤混合 Java 集合可能会很痛苦,但今天作为 Java 开发人员,我们有一些选择。Eclipse Collections 提供了当今最简洁的选项。该selectInstancesOf方法结合了过滤和更具体的返回类型,缺点是需要对每个子类型进行单独的迭代。CaseProcedure提供了当今最简洁、最高效的选项,但缺点是仍然需要cast。当 Java 17 与 Switch 中的模式匹配一起发布时,可用选项将真正得到改进。Java 17 将在发布时提供最简洁和高性能的选项。