问题
Please allow me to make some complaints, maybe it is boringly but I want to describe:"Why did this question will be raised?". I have answered questions is different from others here, here and here last night.
After I get dig into it, I found there are many duplicated logic between Stream and Collector that violates Don't repeat yourself principle, e.g: Stream#map & Collectors#mapping, Stream#filter & Collectors#filtering in jdk-9 and .etc.
But it seems to reasonable since Stream abide by Tell, Don't ask principle/Law of Demeter and Collector abide by Composition over Inheritance principle.
I can only think of a few reasons why Stream operations is duplicated with Collectors as below:
We don't care of how the Stream is created in a big context. in this case Stream operation is more effectively and faster than Collector since it can mapping a Stream to another Stream simply, for example:
consuming(stream.map(...)); consuming(stream.collect(mapping(...,toList())).stream()); void consuming(Stream<?> stream){...}
Collector is more powerful that can composes Collectors together to collecting elements in a stream, However, Stream only providing some useful/highly used operations. for example:
stream.collect(groupingBy( ..., mapping( ..., collectingAndThen(reducing(...), ...) ) ));
Stream operations is more expressiveness than Collector when doing some simpler working, but they are more slower than Collectors since it will creates a new stream for each operation and Stream is more heavier and abstract than Collector. for example:
stream.map(...).collect(collector); stream.collect(mapping(..., collector));
Collector can't applying short-circuiting terminal operation as Stream. for example:
stream.filter(...).findFirst();
Does anyone can come up with other disadvantage/advantage why Stream operations is duplicated with Collectors here? I'd like to re-understand them. Thanks in advance.
回答1:
Chaining a dedicated terminal stream operation might be considered more expressive by those being used to chained method calls rather than the “LISP style” of composed collector factory calls. But it also allows optimized execution strategies for the stream implementation, as it knows the actual operation instead of just seeing a Collector
abstraction.
On the other hand, as you named it yourself, Collector
s can be composed, allowing to perform these operation embedded in another collector, at places where stream operations are not possible anymore. I suppose, this mirroring become apparent only at a late stage of the Java 8 development, which is the reason why some operations lacked their counterpart, like filtering
or flatMapping
, which will be there only in Java 9. So, having two different APIs doing similar things, was not a design decision made at the start of the development.
回答2:
The Collectors
methods that seem to duplicate Stream
methods are offering additional functionality. They make sense when used in combination with other Collector
s.
For example, if we consider Collectors.mapping()
, the most common use is to pass it to a Collectors.groupingBy
Collector
.
Consider this example (taken from the Javadoc):
List<Person> people = ...
Map<City, Set<String>> namesByCity
= people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
mapping(Person::getLastName, toSet())));
mapping
is used here to transform the element type of the value Collection
of each group from Person
to String
.
Without it (and the toSet()
Collector
) the output would be Map<City, List<Person>>
.
Now, you can certainly map
a Stream<Person>
to a Stream<String>
using people.stream().map(Person::getLastName)
, but then you will lose the ability to group these last names by some other property of Person
(Person::getCity
in this example).
来源:https://stackoverflow.com/questions/44258757/why-stream-operations-is-duplicated-with-collectors