我遇到的情况是我正在读取数据库并返回List<String>
,其中每个字符串都被选中并根据某些条件添加到列表中。方法签名是:
public List<String> myMethod(String query, int limit)
第二个参数提供了返回列表大小的上限(设置limit=-1
将删除任何大小限制)。为了避免使这个方法占用大量内存,我编写了一个返回Stream<String>
而不是列表的等效方法。 (注意:我不需要随机访问返回的元素或任何其他特定于列表的功能。)
但是,我对返回Stream<>
持怀疑态度,特别是因为该方法是公开的。让公共方法在Java中返回Stream<>
是否安全?
答案 0 :(得分:12)
不仅安全,而且是Java架构师的recommended。
特别是如果您的数据是基于I / O的,因此在调用myMethod
时尚未在内存中实现,那么返回Stream而不是List是非常明智的。客户端可能只需要使用它的一部分或将其聚合成一些固定大小的数据。因此,您有机会从O(n)内存需求转到O(1)。
请注意,如果并行化对于您的用例也是一个有趣的想法,建议您使用自定义分裂器,其分割策略适应I / O数据源的顺序性质。在这种情况下,我可以推荐a blog post of mine,它提供了这样一个分裂器。
答案 1 :(得分:0)
我认为,默认情况下,应避免在公共方法接口中使用Stream,因为使用Stream会有危险,请参见How to safely consume Java Streams be safely without isFinite() and isOrdered() methods?
基本上,调用您的方法并获取流的客户端必须确保,当您的方法实现更改返回的流的特征时,其算法不会中断(或在集成测试中中断)。这是一件非常困难的事情(因为流特征很容易忘记),并且很容易忘记。
因此,即使您返回的数据尚未实现,并且您想将其留给客户来决定如何实现,我什至只会将Stream视为返回值。但是即使那样,Iterable或Iterator似乎也是更好的选择,因为它们没有流所具有的不必要的并行处理负担,而且防御性程序需要防范。
作为一个示例,当返回一个List时,您的客户知道返回的数据类型是有限且有序的,并且对其进行迭代不会意外地在ForkJoinPool上并行运行,可能会破坏整个应用程序。使用Stream,您必须致电sequential()
来防范这种可能性。
如果数据源在消耗后需要关闭,我更喜欢InputStream
而不是Stream,因为实现者会很好地记住他们需要关闭流(并且静态检查器会提醒他们)。