【问题标题】:Split a list by the type of element按元素类型拆分列表
【发布时间】:2015-09-02 07:14:22
【问题描述】:

下面的代码sn-p:

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?我确实尝试过返回 Collection《ParentClass》的 Guava Collections2.filter(),而我需要 Collection《ChildClass1》,知道吗?

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

【问题讨论】:

  • 如果您可以使用Iterable 而不是Collection,您可以使用Iterables.filter(ls, ChildClass1.class)

标签: java list guava


【解决方案1】:

使用 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);

不幸的是,未经检查的演员表仍然是必要的。

【讨论】:

    【解决方案2】:

    我认为沿着这条线的东西应该可以工作

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

    【讨论】:

    • 谢谢你的 lambda exp,这只适用于 JDK8,对吧?
    • 是的,但你可以在Guava中做类似的事情
    【解决方案3】:

    “guava”-est 方法如下:

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

    如果您只需要一次迭代,恐怕到目前为止唯一的解决方案就是您在问题中所写的。

    【讨论】:

      【解决方案4】:

      Guava 假设如果您过滤 T 类型的元素,那么您仍然会得到 T 的集合(原始集合的子集合)。它不能以任何方式推断Predicates.instanceOf(SubT.class) 过滤器会导致安全的类型转换,即使它确实会生成SubT 的列表。因此,您需要自己对Collection&lt;ChildClass1&gt; 进行显式转换。或者,更准确地说 - 首先转换为原始列表,然后转换为 Collection&lt;ChildClass1&gt; 以解决编译器类型检查问题。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-20
        • 1970-01-01
        • 2023-03-10
        • 2014-06-02
        相关资源
        最近更新 更多