按元素类型拆分列表

3

以下是代码片段:

List<ParentClass> ls = new ArrayList<ParentClass>();
ls.add(new ChildClass1());
ls.add(new ChildClass2());
ls.add(new ChildClass1());
ls.add(new ChildClass2());
ls.add(new ChildClass2());
ls.add(new ChildClass1());
ls.add(new ChildClass2());

List<ChildClass1> sub1 = new ArrayList<ChildClass1>();
List<ChildClass2> sub2 = new ArrayList<ChildClass2>();
for (ParentClass p : ls) {
    if(p instanceof ChildClass1){
        sub1.add((ChildClass1)p);
    } else {
        sub2.add((ChildClass2)p);
    }
}       

System.out.println(sub1);
System.out.println(sub2);

有没有一种优雅的方法来获取sub1和sub2?我尝试过使用Guava Collections2.filter(),但它返回了Collection《ParentClass》,而我需要的是Collection《ChildClass1》。有什么想法吗?
Collection<ParentClass> sub1= Collections2.filter(ls, Predicates.instanceOf(ChildClass1.class))

如果您可以使用Iterable而不是Collection,则可以直接使用Iterables.filter(ls, ChildClass1.class) - Louis Wasserman
4个回答

2
使用Guava和单次遍历,您可以执行索引操作:
ImmutableListMultimap<Class<?>, ? extends ParentClass> index = FluentIterable.from(ls)
        .index(Object::getClass);
List<ChildClass1> sub1 = (List<ChildClass1>) index.get(ChildClass1.class);
List<ChildClass2> sub2 = (List<ChildClass2>) index.get(ChildClass2.class);

在Java-8之前,将Object::getClass替换为匿名类:
ImmutableListMultimap<Class<?>, ? extends ParentClass> index = FluentIterable.from(ls)
        .index(new Function<ParentClass, Class<?>>() {
            @Override
            public Class<?> apply(ParentClass o) {
                return o.getClass();
            }
        });

流API的等效方式如下:

Map<?, List<ParentClass>> map = ls.stream().collect(Collectors.groupingBy(Object::getClass));
List<ChildClass1> sub1 = (List<ChildClass1>) (List<?>)map.get(ChildClass1.class);
List<ChildClass2> sub2 = (List<ChildClass2>) (List<?>)map.get(ChildClass2.class);

很不幸,未经检查的类型转换仍然是必要的。


1
我认为沿着这条线路应该可以工作。
Collection<ChildClass1> sub1 = ls.stream()
                                 .filter (x -> x instanceof ChildClass1)
                                 .map ( x-> (ChildClass1) x)
                                 .collect(Collectors.asList());

谢谢您提供这个Lambda表达式,这只在JDK8上工作,对吗? - gfytd
是的,但是你可以在Guava中做类似的事情。 - Rob Audenaerde

1
“Guava”最好的方法是以下方式:
List<ParentClass> ls = ... ;
FluentIterable<ParentClass> it = FluentIterable.from(ls);

List<ChildClass1> sub1 = it.filter(ChildClass1.class).toList();
List<ChildClass2> sub2 = it.filter(ChildClass2.class).toList();

请注意,这会导致两个不同的迭代(或者实际上是你调用toList()的次数)。
如果您只需要一次迭代,恐怕目前唯一的解决方案就是您在问题中所写的。

0

Guava假设如果您正在过滤类型为T的元素,则结果仍将得到T的集合(原始集合的子集)。它无法通过任何方式推断出Predicates.instanceOf(SubT.class)过滤器会导致安全类型转换,即使它确实会产生SubT列表。因此,您需要自己进行显式转换为Collection<ChildClass1>。或者,更精确地说-首先将其强制转换为原始列表,然后再将其转换为Collection<ChildClass1>以绕过编译器类型检查。

Collection<ChildClass1> sub1= (Collection<ChildClass1>) (Collection) Collections2.filter(ls, Predicates.instanceOf(ChildClass1.class))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接