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
vsNoSuchElementException
行为。如果使用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);