1.概述
Java 8 Stream API提供了Java Collections之外的有效替代方案,以呈现或处理结果集。但是,决定何时使用哪个是一个普遍的难题。
在本文中,我们将探索Stream
和Collection
并讨论适合其各自用途的各种方案。
2. Collection
与Stream
Java Collection
List
, Set
和Map
类的数据结构,提供了有效的机制来存储和处理数据。
但是,Stream API可用于对数据执行各种操作,而无需中间存储。因此, Stream
工作方式类似于直接从底层存储(如集合和I / O资源)访问数据。
此外,这些集合主要与提供对数据的访问以及修改数据的方式有关。另一方面,流与有效地传输数据有关。
尽管Java允许轻松地从Collection
转换为Stream
,反之亦然,但要知道哪种是呈现/处理结果集的最佳机制是很方便的。
例如,我们可以使用stream
和parallelStream
方法Collection
转换为Stream
public Stream<String> userNames() {
ArrayList<String> userNameSource = new ArrayList<>();
userNameSource.add("john");
userNameSource.add("smith");
userNameSource.add("tom");
return userNames.stream();
}
同样,我们可以使用Stream API collect
Stream
转换为Collection
public List<String> userNameList() {
return userNames().collect(Collectors.toList());
}
在这里,我们使用Collectors.toList()
方法Stream
转换为List
同样,我们可以将Stream
转换为Set
或Map
:
public static Set<String> userNameSet() {
return userNames().collect(Collectors.toSet());
}
public static Map<String, String> userNameMap() {
return userNames().collect(Collectors.toMap(u1 -> u1.toString(), u1 -> u1.toString()));
}
3.何时返回Stream
?
3.1 相关的成本高
Stream API可以随时随地懒惰地执行和过滤结果,这是降低实现成本的最有效方法。
例如,Java NIO readAllLines
Files
所有行,JVM必须为此文件的所有行将其内容保存在内存中。因此,此方法在返回行列表时涉及较高的实现成本。
但是, Files
类还提供了lines
方法,该方法返回一个Stream
,我们可以使用它来渲染所有行,甚至可以使用limit
方法更好地限制结果集的大小-两者都可以延迟执行:
Files.lines(path).limit(10).collect(toList());
另外,在forEach
类的终端操作之前Stream
不会执行中间操作:
userNames().filter(i -> i.length() >= 4).forEach(System.out::println);
因此, Stream
避免了与过早实现相关的成本。
3.2 大结果或无限结果
Stream
的设计目的是为了获得更好的性能,无论结果是大还是无限。 Stream
用于此类用例始终是一个好主意。
同样,在结果无限的情况下,我们通常不处理整个结果集。因此,Stream API的内置功能(例如filter
和limit
证明在处理所需结果集时非常方便,使Stream
成为首选。
3.3 灵活性
Stream
允许在以任何形式或顺序处理结果时非常灵活。
当我们不想对使用者强制执行一致的结果集时, Stream
此外,当我们想为消费者提供急需的灵活性时Stream
例如,我们可以使用Stream API上可用的各种操作来过滤/排序/限制结果:
public static Stream<String> filterUserNames() {
return userNames().filter(i -> i.length() >= 4);
}
public static Stream<String> sortUserNames() {
return userNames().sorted();
}
public static Stream<String> limitUserNames() {
return userNames().limit(3);
}
3.4 功能行为
Stream
功能正常。当以不同方式处理时,不允许对源进行任何修改。因此,呈现不可变结果集是首选。
例如,让我们filter
并limit
Stream
收到的一组结果:
userNames().filter(i -> i.length() >= 4).limit(3).forEach(System.out::println);
在此,每次对Stream
filter
和limit
类的操作都会返回一个新的Stream
并且不会修改userNames
方法提供Stream
4.何时返回一个Collection
?
4.1 物化成本低
在渲染或处理涉及物化成本较低的结果时,我们可以选择流中的集合。
换句话说,Java从一开始就通过计算所有元素来急切地Collection
因此, Collection
在实现时会对堆内存造成很大压力。
因此,我们应该考虑使用Collection
来呈现一个结果集,该结果集不会对实现它的堆内存造成太大的压力。
4.2 固定格式
我们可以使用Collection
来为用户强制执行一致的结果集。例如, Collection
如TreeSet
和TreeMap
返回自然排序的结果。
换句话说,使用Collection
,我们可以确保每个使用者以相同的顺序接收和处理相同的结果集。
4.3 可重复使用的结果
Collection
的形式返回时,可以轻松地遍历多次。但是, Stream
就认为它已消耗掉,并在重用时IllegalStateException
public static void tryStreamTraversal() {
Stream<String> userNameStream = userNames();
userNameStream.forEach(System.out::println);
try {
userNameStream.forEach(System.out::println);
} catch(IllegalStateException e) {
System.out.println("stream has already been operated upon or closed");
}
}
因此,当很明显消费者将遍历结果多次时Collection
4.4 修改
与Stream
Collection
允许修改元素,例如在结果源中添加或删除元素。因此,我们可以考虑使用集合来返回结果集,以允许使用者进行修改。
例如,我们可以使用add
/ remove
方法ArrayList
userNameList().add("bob");
userNameList().add("pepper");
userNameList().remove(2);
类似地,诸如put
和remove
类的方法允许在地图上进行修改:
Map<String, String> userNameMap = userNameMap();
userNameMap.put("bob", "bob");
userNameMap.remove("alfred");
4.5 内存中结果
另外,当以集合形式出现的物化结果已经存在于内存中时, Collection
是一个显而易见的选择。
5.结论
在本文中,我们比较了Stream
和Collection
并研究了适合他们的各种方案。
我们可以得出结论, Stream
是呈现大型或无限结果集的理想选择,它具有诸如延迟初始化,急需的灵活性和功能行为之类的优点。
但是,当我们要求结果的形式一致时,或者当涉及到较低的成本实现时,我们应该选择Stream
Collection
。
0 评论