假设我的应用程序中有多个类使用的方法。此方法采用Stream
作为参数,并应用终端操作forEach
以便将数据写入文件,如下所示:
public File writeStreamToTempFile(Stream stream) {
stream.forEach(item -> {
//some code to write item to a file
}
}
此方法有多个调用方,在某些方法中,我需要转换数据,例如使用map函数,如下所示:
public void exportAnimalsData() {
Stream<Animal> animalStream = //fetch data from a DB
animals.filter(a -> a.type.equals("dog"))
.map(a -> //Do something useful to transform the Dogs);
writeStreamToTempFile(animalStream);
}
并非writeStreamToTempFile
方法的所有调用方都需要对流执行其他操作。
所以我的问题是:
以不同的方法将操作应用于流是否是错误的做法?
我在某处读到Stream永远都不应该是方法的返回类型(调用者不知道该方法是否已经使用了该流),它也适用于方法的参数吗?
我应该只应用同一方法中所需的所有操作,还是可以将中间操作以不同的方法附加到同一流中?
答案 0 :(得分:8)
我读到某处Stream永远都不应该是方法的返回类型
不确定在哪里读到Stream
永远都不应该是方法的返回类型。 (您可以通过链接更新您的问题吗?)。
相反,Stream API的设计者之一Brian Goetz显然认为并非如此:
https://stackoverflow.com/a/24679745/340088
(调用者不知道该方法是否已经使用了流)
如果呼叫者获得Stream
,则隐式理解该流可用。如果返回已消耗的Stream
,则类似于返回封闭的Socket
或封闭的InputStream
。虽然在语法上可能,但这只是不好的编码。并不意味着您不应该仅仅因为某些错误的编码器偶尔返回错误状态的代码而就从方法返回Socket
或InputStream
。
相反,旨在返回单子风格的对象。 Stream
,Optional
,CompletableFuture
和所有功能接口(Function
,Consumer
,Operator
等)均应返回这样就可以将更多功能样式的操作附加到它们上,而无需现场执行实际功能。
您向我发布的示例非常合理。您对Stream
进行了修饰修饰,并对管道进行了其他操作。您可以使用逻辑来决定将哪些操作添加到管道中,并且没有理由不以小方法正确地封装它们。
此外,如果流中包含数十万个元素的流非常大,那么如果返回一个集合,那么您将在内存中创建一个巨大的集合,这会产生将它们全部添加的成本,然后又将它们再次流式传输到将它们写入文件。没什么意义吗?
答案 1 :(得分:0)
我应该只应用同一方法中所需的所有操作还是 可以将中间操作附加到同一流中 不同的方法?
从技术上讲,它不一定是相同的流/对象。例如,在流上调用诸如map()
之类的操作将创建另一个流。
事实上,我认为虽然您没有使用终端操作来消耗流,但我不明白为什么在它上进行创建或获取预期的流然后传递给期待流。