RFR: 8180352: Add Stream.toList() method
Justin Dekeyser
justin.dekeyser at gmail.com
Wed Nov 4 09:19:23 UTC 2020
Hello,
I think the `collect(toList())` is interesting, as the developer
really knows that the internal operation is collect-like.
I say this because there would be another, much more functional
approach, that would be of the form
`stream.map(List::of).reduce(List.empty, List::concat)`,
(if ever the List has been made with monads in minds... (concat does
not exist, and there is no unit for the empty list monad).)
The toList() obfuscates both the list implementation (as the current
utils actually) but also obfuscates the philosophy of the conversion.
As suggested by the previous mail, collect version looks much more
explicit, because it really suggests an aggregate operation and allows
one to use the exact constructor.
What about trying to make the Stream interface *flexible* to users,
instead of adding new functionalities that we could regret later?
Then, as the language usages and the trends evolve, we could really
see which of the "utils" functions are relevant to add.
For example, we could maybe invent a method of the form (just a
sketch, not an actual well thought proposal)
`
default <S extends Stream<T>> S wrap(Function<Stream<T>, S> wrapper) {
return wrapper.apply(this);
}
`
on Stream<T> interface, in such a way one could use the "natural flow"
of writing while binding its own implementation and, therefore, use
its own shortcuts.
Best regards,
Justin Dekeyser
On Wed, Nov 4, 2020 at 9:59 AM Tagir Valeev <amaembo at gmail.com> wrote:
>
> Hello!
>
> On Wed, Nov 4, 2020 at 2:55 AM Brian Goetz <brian.goetz at oracle.com> wrote:
> > (In fact, we would be entirely justified to change the behavior of
> > Collectors::toList tomorrow, to match the proposed Stream::toList; the
> > spec was crafted to preserve this option. We probably won't, because
> > there are too many programmers who refuse to read the specification, and
> > have baked in the assumption that it returns an ArrayList, and this
> > would likely just be picking a fight for little good reason, regardless
> > of who is right.)
>
> My experience based on writing Stream code as well as reading Stream
> code written by other developers suggest that one of the most popular
> reasons to modify the resulting list is caused by the need to add a
> special value (or a few of them) to the resulting list. We have
> several options:
> 1. List<String> result =
> Stream.concat(xyz.stream().filter(...).map(...),
> Stream.of("special")).collect(toList());
> Canonical, recommended way, no mutability. However concat is really
> ugly, it breaks the fluent style of stream API calls, it's hard to
> read and people just hate it.
>
> 2. List<String> result =
> xyz.stream().filter(...).map(...).collect(toCollection(ArrayList::new));
> result.add("special");
> Mutable result, but much more readable. Also, according to spec. But
> this ArrayList::new is hard to type and makes the code longer.
>
> 3. List<String> result = xyz.stream().filter(...).map(...).collect(toList());
> result.add("special");
> Shorter and readable! Well, violates the spec but works!
>
> What people really need is an easy and readable way to concatenate
> lists and streams, appending new elements, etc. The JDK doesn't offer
> really great options here, so people often end up with external
> utility methods or libraries.
>
> Something like this would be very welcomed (and would reduce the need
> for mutable lists):
> List<String> result =
> xyz.stream().filter(...).map(...).append("special").collect(toList());
>
> As for nullity topic, I really welcome that the proposed toList() is
> null-tolerant but it worth mentioning that existing terminal
> operations like findFirst(), findAny(), min() and max() are already
> null-hostile.
>
> With best regards,
> Tagir Valeev.
More information about the core-libs-dev
mailing list