在通常返回集合的地方返回Streams是理智的事情吗?


19

在开发与任何旧代码无关的API时,我经常发现自己编写的方法纯粹是通过收集结果终止的Streams管道。像这个:

ImmutableSet<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfThing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey)
        .collect(MyCustomCollectors.toImmutableSet());
}

现在,此类的大多数客户端通常将需要Collection(在本例中为ImmutableSet)来搜索元素并对其进行迭代,但是某些客户端可能会受益于拥有Stream,因此可以在此基础上传递更多的操作流,而无需从集合中获取新的流。因此,返回Stream会给客户提供他们如果拥有Collection便拥有的选项超集(毕竟,他们总是可以collect()自己拥有Stream:

Stream<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfthing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey);
        // No collect
}

这种方法对我来说很诱人,因为我看不到它可能存在的任何潜在缺陷。但是,我在任何库中都从未见过这种方法(可能是因为Java 8出现后没有发布太多的库),所以我有点害怕采用它。现有的库类通常在从私有状态派生出某些东西时返回Collections。

如果我决定在Java-8之前的我自己会返回Collection的任何地方返回Stream ,会发生什么不好的事情?还是我可能在这里做一些反模式的事情,而这一切都源于私人国家?

Answers:


14

如果myPrivateThingies是可变的,则在私有状态和流结果之间创建了一个隐藏的依赖关系。如果客户有可能间接导致myPrivateThingies状态改变,那么他在打电话时会得到collect与您最初打算发出的结果不同的结果。

如果myPrivateThingies是不可变的,那么结果将是参照透明的,但是您还需要注意另一个问题:语义垃圾,即保留不再需要的大量内存。假设myPrivateThingies很大,收集流的结果很小。客户端可能在丢弃了对产生流的对象的所有引用之后很长时间才保留该流,但是stream仍然避免myPrivateThingies被垃圾回收。认真收集结果将myPrivateThingies可以释放出来。

实际上,这是在Java 7调用时发生的substring。Oracle认为,由于每次都不复制子字符串而可能节省的效率,偶尔使内存消耗过多的普通用户感到惊讶是不值得的。这并不是说没有旧行为的实际用例(例如,解析器),但是经常急切地收集结果足够快,并且当这种情况发生时,您就没有利弊。

另一方面,返回流使客户端能够选择他们要使用哪种数据结构来保存结果,而不是您为他选择一个。两种选择都可能值得。


4

要考虑的最重要的事情:Stream只能重复一次,而您比a具有更大的灵活性Collection:您可以继续创建更多Streams甚至Iterators来对结果进行额外的重复处理。

因此,如果您不确定该方法的调用者是否将一次且仅使用一次结果,则最好返回a Collection


您的示例代码有一个明显的错误:为什么SocialStatus一个人的概念he


3

我认为没有。您可以使用流执行的操作是对集合可以执行的操作的严格超集,并且通常可以使它们更高效,因此除了不熟悉之外,没有理由不使用它们。“ Lambda表达式是Java 8的入门药物,而Streams是真正的瘾。” (Venkat Subramaniam,Java函数编程

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.