按元素类型拆分列表

时间:2015-09-02 07:14:23

标签: java list guava

对于下面的代码段:

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))

4 个答案:

答案 0 :(得分: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();
            }
        });

Stream 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 :(得分:1)

我觉得这一行应该有用

Collection<ChildClass1> sub1 = ls.stream()
                                 .filter (x -> x instanceof ChildClass1)
                                 .map ( x-> (ChildClass1) x)
                                 .collect(Collectors.asList());

答案 2 :(得分:1)

“番石榴” - 这样做的方法如下:

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()的次数)。

如果您只需要一次迭代,我担心到目前为止唯一的解决方案正是您在问题中所写的内容。

答案 3 :(得分: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))