Domanda

While developing my API that is not tied to any legacy code, I often find myself writing methods that are purely Streams pipeline terminated by collecting the results. Like this one:

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

Now, most of the clients of this class will usually need the Collection (in this case, an ImmutableSet) to search elements and iterate over it, but some clients may benefit from having a Stream so they could pipe some more operations on top of that Stream without the need to obtain a new stream from the Collection. So returning a Stream gives the cliens a superset of options they would have if they just had the Collection (after all, they can always collect() the Stream themselves:

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

This approach is tempting for me to try out as I don't see any potential flaws it could have. However, I've never seen this approach in any library (probably becase there weren't many libraries released after the appearance of Java 8), so I'm a bit afraid to adopt it. Existing library classes usually return Collections when they derive something from the private state.

Is there something bad that could happen if I decide to return a Stream wherever my pre-Java-8 self would return a Collection? Or probably am I doing something of an antipattern here with all that deriving from private state?

È stato utile?

Soluzione

If myPrivateThingies is mutable, you've created a hidden dependency between your private state and the stream results. If it's possible for the client to indirectly cause myPrivateThingies to change state, then he's going to get a different result when calling collect than the one you originally intended to give out.

If myPrivateThingies is immutable, then the result will be referentially transparent, but there's one more issue you have to watch out for: semantic garbage, i.e. holding on to large amounts of memory that are no longer needed. Suppose myPrivateThingies is very large and the result of collecting the stream is small. The client may hold on to the stream long after having thrown away all references to the object that produced it, but that stream is still keeping myPrivateThingies from becoming garbage collected. Eagerly collecting the results would allow myPrivateThingies to be freed.

This actually happened prior to Java 7 when calling substring. Oracle decided that the potential efficiency savings from not copying the substring every time isn't worth occasionally surprising the average user with excessive memory consumption. That's not to say there weren't real use cases for the old behavior (e.g. parsers) but often collecting the results eagerly is fast enough, and when that happens you have no pros and a potential con.

On the other hand returning a stream gives the client the ability to choose which data structure they want to use to hold the results, as opposed to you choosing one for him. It may be worth offering both options.

Altri suggerimenti

The most important thing to consider: Streams can only be iterated once, whereas you have more flexibility over a Collection: you can continue to create more Streams or even Iterators to do additional, repetitive processing on the results.

So if you are not sure whether callers of the method are going to use the results once and only once, it's better to return a Collection.


Your sample code has one obvious error: why would a SocialStatus has the concept of a person, he?

In my view, no. The things you can do with streams are a strict superset of the things you can do with collections, and often they can be made more efficient, so there is no reason not to use them except unfamiliarity. "Lambda expressions are the gateway drug to Java 8, but Streams are the real addiction." (Venkat Subramaniam, Functional Programming in Java)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top