Java 在流和集合API之间选择

Java 在流和集合API之间选择,java,collections,java-8,java-stream,Java,Collections,Java 8,Java Stream,考虑以下示例,该示例打印列表中的最大元素: List<Integer> list = Arrays.asList(1,4,3,9,7,4,8); list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println); 以上代码不仅更短,而且更清晰易读(在我看来)。我想到了一些类似的例子,比如与findAny结合使用的binarySearch与filter的使用 我知道流可以是一个

考虑以下示例,该示例打印
列表中的最大元素:

List<Integer> list = Arrays.asList(1,4,3,9,7,4,8);           
list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println);
以上代码不仅更短,而且更清晰易读(在我看来)。我想到了一些类似的例子,比如与
findAny
结合使用的
binarySearch
filter
的使用


我知道
可以是一个无限的管道,而不是受JVM可用内存限制的
集合
。这将是我决定是使用
还是
集合
API的标准。选择
而不是
集合
API(如性能)还有其他原因吗。更一般地说,这是选择
Stream
而不是旧API的唯一原因吗?旧API可以以更干净、更短的方式完成任务

流API就像一把瑞士军刀:它允许您通过有效组合工具来执行相当复杂的操作。另一方面,如果你只需要一把螺丝刀,可能独立的螺丝刀会更方便。流API包括许多内容(如
不同的
排序的
、基本操作等),否则需要编写几行代码,并引入中间变量/数据结构和枯燥的循环,以吸引程序员对实际算法的注意。有时,使用流API甚至可以提高顺序代码的性能。例如,考虑一些旧的API:

class Group {
    private Map<String, User> users;

    public List<User> getUsers() {
        return new ArrayList<>(users.values());
    }
}
在这里,它被排序并转换为数组,以传递给另一个碰巧接受数组的方法。在另一个地方,
getUsers()
可以这样使用:

List<User> users = group.getUsers();
for(User user : users) {
    if(user.getAge() < 18) {
        throw new IllegalStateException("Underage user in selected group!");
    }
}
并修改调用方代码。第一个:

someOtherMethod(group.users().sorted().toArray(User[]::new));
第二条:

if(group.users().anyMatch(user -> user.getAge() < 18)) {
    throw new IllegalStateException("Underage user in selected group!");
}
if(group.users().anyMatch(user->user.getAge()<18)){
抛出新的IllegalStateException(“所选组中的未成年用户!”);
}
这样,它不仅更短,而且可能工作得更快,因为我们跳过了中间复制


流API中的另一个概念点是,只要添加
parallel()
步骤,就可以并行化根据指南编写的任何流代码。当然,这不会总是提高性能,但它的帮助比我预期的要多。通常,如果操作按顺序执行,则可以从并行化中获益。无论如何,我们以前从未见过用Java进行并行编程的如此简单的方法。

当然,这取决于具体情况。以您最初的示例为例:

List<Integer> list = Arrays.asList(1,4,3,9,7,4,8);           
list.stream().max(Comparator.naturalOrder()).ifPresent(System.out::println);
这不涉及任何自动装箱。但是如果您的假设是事先有一个
列表
,那么这可能不是一个选项,因此如果您只是对
max
值感兴趣,
Collections.max
可能是一个更简单的选择

但这会导致一个问题,为什么您事先有一个
列表
。也许,这是旧代码(或使用旧思维编写的新代码)的结果,它除了使用装箱和
Collection
s之外别无选择,因为过去没有其他选择

因此,也许你应该先考虑生成集合的源代码,然后再考虑如何使用它(或者,同时考虑两者)

如果您只有一个
集合
,并且只需要一个基于
集合
的简单实现的单终端操作,那么您可以直接使用它,而无需使用
API。API设计师承认了这一想法,因为他们向
集合
API添加了
forEach(…)
等方法,而不是坚持每个人都使用
stream().forEach(…)
。而
Collection.forEach(…)
并不是
Collection.stream().forEach(…)
的简单缩写,事实上,它已经在更抽象的
Iterable
接口上定义,该接口甚至没有
stream()
方法


顺便说一句,您应该了解
Collections.binarySearch
Stream.filter/findAny
之间的区别。前者要求对集合进行排序,如果满足该先决条件,则可能是更好的选择。但是如果集合没有排序,简单的线性搜索比仅使用二进制搜索进行排序更有效,更不用说事实了,二进制搜索只适用于
List
s,而filter/findAny只适用于支持各种源集合的任何流。

您是否只询问此特定方法?如果您唯一感兴趣的是从列表中获取
max
值,那么
Collections
方法就是一种选择,
Optional
vs
NoSuchElementException
行为。如果使用
IntStream
而不是
Stream
则更简单:
ints.max().ifPresent(System.out::println)小心:仅仅使用
parallel()
通常会大大降低性能。除非你有实际的数字,否则不要使用它。你是否建议我今后编写的任何新代码都不要使用
列表或
集合,而是始终使用
?这并不总是那么容易。这就是我试图解释的。如果您的操作包含基本数据类型,并且该操作可以表示为流操作而无需任何装箱,那么这是最好的。如果您的操作由多个步骤或函数的组合组成,那么流API很可能是一个不错的选择。如果数据源是现有集合或数组,并且预期的操作可以表示为对现有(基于集合的)方法的单个调用,请使用该方法。如果要就地修改集合,请停留在集合API上。
someOtherMethod(group.users().sorted().toArray(User[]::new));
if(group.users().anyMatch(user -> user.getAge() < 18)) {
    throw new IllegalStateException("Underage user in selected group!");
}
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);