如何在Java 8中使用收集调用?

23

假设我们有这段无聊的代码,我们都不得不使用:

ArrayList<Long> ids = new ArrayList<Long>();
for (MyObj obj : myList){
    ids.add(obj.getId());
}

在切换到Java 8后,我的IDE告诉我可以用collect调用替换这段代码,并自动生成:

ArrayList<Long> ids = myList.stream().map(MyObj::getId).collect(Collectors.toList());

然而它给了我这个错误:

Steam 中的 collect(java.util.stream.Collector) 无法应用于:(java.util.stream.Collector,capture,java.util.List>)

我尝试过将参数进行强制转换,但是它给出了未定义的 AR,而IDE没有提供任何更多的建议。

我想知道在这种情况下如何使用 collect 调用,我找不到任何可以正确指导我的信息。有人能指点一下吗?

2个回答

47
问题在于Collectors.toList方法返回的是List<T>而不是ArrayList,这一点并不令人意外。
List<Long> ids = remove.stream()
        .map(MyObj::getId)
        .collect(Collectors.toList());

以接口为导向的编程。

来自文档:

返回一个Collector,将输入元素累积到一个新的List中。对于返回的List不能保证其类型、可变性、序列化性或线程安全性;如果需要更多控制返回的List,请使用toCollection(Supplier)

重点在于:您甚至无法假设返回的List是可变的,更不用说它是特定类的了。如果您想要一个ArrayList

ArrayList<Long> ids = remove.stream()
        .map(MyObj::getId)
        .collect(Collectors.toCollection(ArrayList::new));

还要注意,Java 8的Stream API通常使用import static,所以需要添加:

import static java.util.stream.Collectors.toCollection;

(我讨厌带星号的import static,它只会污染命名空间并增加混乱。但是,特定的import static,尤其是使用Java 8实用程序类,可以极大地减少冗余代码)

ArrayList<Long> ids = remove.stream()
        .map(MyObj::getId)
        .collect(toCollection(ArrayList::new));

实际上,结果代码并没有比Java8之前的代码更短或更有意义,那么性能呢? - azerafati
4
@Bludream,这并不是关于缩短或者使内容更有意义的问题,而是关于引入Java的新范式——函数式编程。这使得你可以以非常不同的方式来完成之前的Java版本所无法实现的任务。当你需要抽象“行为”而非“数据”时,函数式编程尤其发挥它的作用。此外,上述内容本身可能略长,但却可以作为一行代码;在之前的版本中,这是不可能实现的。 - Boris the Spider
我明白了,但像楼主一样,我也在寻找一些东西来替换我的收集器,因为它们无处不在地散布在我的代码中。 - azerafati
@Bludream 根据您的实际需求,在使用Lambda为集合提供视图时,结合Guava类可能会更好,而不是复制它们。 - Boris the Spider
你能看一下我编辑过的回答吗?有什么意见吗? - azerafati
显示剩余2条评论

2

我经常使用收集器块,在其中创建一个空数组并使用循环填充它,因此我决定需要自己的实用程序类,以避免反复编写相同的代码行,以下是该类:

public class Collections {

    public static <T, O> List<T> collect(Set<O> items, Function<? super O, ? extends T> mapper) {

    return items.stream().map(mapper).collect(Collectors.toCollection(ArrayList::new));
}

并像这样使用它

List<Product> prods = Collections.collect(basket.getOrderItems(), OrderItem::getProduct);

or like this

List<Long> prods = Collections.collect(basket.getOrderItems(), (item)->item.getProduct().getId());

尽管看起来更容易阅读,但在这种情况下,流似乎会慢一些,请参考这里


你可以使用 Item::getProduct。学习正确使用方法引用是学习Java 8的重要部分。添加一个 import static 也可以消除直接引用方法的需要。 - Boris the Spider
是的,谢谢!我按照你说的方法让它工作了,而且我也不喜欢使用“import static”! - azerafati

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