考虑以下示例,在List
中打印最大元素:
List<Integer> list = Arrays.asList(1,4,3,9,7,4,8);
list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println);
使用Collections.max
方法也可以实现相同的目标:
System.out.println(Collections.max(list));
以上代码不仅更短,而且阅读更清晰(在我看来)。我想到了类似的例子,例如binarySearch
vs filter
与findAny
一起使用。
我理解Stream
可以是无限管道,而不是受{JVM可用内存限制的Collection
。这将是我决定是使用Stream
还是Collections
API的标准。是否有任何其他原因可以选择Stream
Collections
API(例如效果)。更一般地说,这是选择Stream
而不是旧API的唯一原因,它可以更清洁,更短的方式完成工作吗?
答案 0 :(得分:7)
Stream API就像瑞士军刀:它可以让您通过有效地组合工具来完成相当复杂的操作。另一方面,如果你只需要一把螺丝刀,那么独立螺丝刀可能会更方便。 Stream API包含许多内容(如distinct
,sorted
,基本操作等),否则需要您编写多行并引入中间变量/数据结构和无聊循环,从实际中吸引程序员的注意力算法。有时使用Stream API即使对于顺序代码也可以提高性能。例如,考虑一些旧的API:
class Group {
private Map<String, User> users;
public List<User> getUsers() {
return new ArrayList<>(users.values());
}
}
这里我们要返回该组的所有用户。 API设计者决定返回List
。但它可以通过各种方式在室外使用:
List<User> users = group.getUsers();
Collections.sort(users);
someOtherMethod(users.toArray(new User[users.size]));
这里它被排序并转换为数组以传递给碰巧接受数组的其他方法。在另一个地方getUsers()
可以像这样使用:
List<User> users = group.getUsers();
for(User user : users) {
if(user.getAge() < 18) {
throw new IllegalStateException("Underage user in selected group!");
}
}
这里我们只想找到符合某些条件的用户。在这两种情况下,实际上都不需要复制到中间ArrayList
。当我们迁移到Java 8时,我们可以将getUsers()
方法替换为users()
:
public Stream<User> users() {
return users.values().stream();
}
修改来电者代码。第一个:
someOtherMethod(group.users().sorted().toArray(User[]::new));
第二个:
if(group.users().anyMatch(user -> user.getAge() < 18)) {
throw new IllegalStateException("Underage user in selected group!");
}
这样它不仅更短,而且可以更快地工作,因为我们跳过了中间复制。
Stream API中的另一个概念点是,只需添加parallel()
步骤即可并行化根据指南编写的任何流代码。当然,这并不总能提升性能,但它比我预期的更有帮助。通常,如果对0.1ms or longer顺序执行操作,则可以从并行化中受益。无论如何,我们还没有看到过如此简单的方法在Java中进行并行编程。
答案 1 :(得分:3)
当然,它总是取决于具体情况。以您为例:
List<Integer> list = Arrays.asList(1,4,3,9,7,4,8);
list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println);
如果您想有效地执行相同的事情,可以使用
IntStream.of(1,4,3,9,7,4,8).max().ifPresent(System.out::println);
不涉及任何自动装箱。但是,如果您的假设是预先设置List<Integer>
,那么这可能不是一个选项,因此如果您只对max
值感兴趣,Collections.max
可能是更简单的选择。
但这会导致你事先得到List<Integer>
的问题。也许,这是旧代码(或使用旧思维编写的新代码)的结果,除了使用装箱和Collection
之外别无选择,因为过去没有其他选择?
所以也许你应该考虑产生这个集合的来源,然后再费心去消费它(或者好吧,同时考虑两者)。
如果您只拥有Collection
并且您需要的只是一个基于Collection
的简单实现的单一终端操作,您可以直接使用它而不必担心Stream
API。 API设计人员承认了这一想法,因为他们向forEach(…)
API添加了Collection
等方法,而不是坚持使用stream().forEach(…)
的每个人。 Collection.forEach(…)
并不是Collection.stream().forEach(…)
的简单简写,事实上,它已经在更抽象的Iterable
接口上定义,甚至没有stream()
方法。< / p>
顺便说一下,您应该了解Collections.binarySearch
和Stream.filter/findAny
之间的区别。前者要求集合排序,如果满足该先决条件,可能是更好的选择。但是如果集合没有排序,简单的线性搜索比单独使用二进制搜索排序更有效,更不用说二进制搜索仅在过滤/ findAny时与List
一起使用的事实。适用于支持各种源集合的任何流。