0

With java 8, now we can use the java.util.Collection.stream to do some collection basic functions such as filter, map, collect, anyMatch, etc.

The problem is when you have a Collection and you have to use some of this functions, the code gets a little ugly, for example:

List<String> ids = this.getFriends(userId).stream().map(Friend::getFriendUserId).collect(Collectors.toList())

In this case, I have a List<Friend> and I want to have a List<String> that gets populated with the friendId of every item in the first List.

What I think is that maybe the code could be like this:

List<String> ids = this.getFriends(userId).map(Friend::getFriendUserId);

This of course is more readable and also more clean, but invalid as List does not understand the message map. I can't understand what would be the problem of implementing this in Java 8, and why do they decide to force every time to convert to Stream when in some cases we then convert it back to a Collection as in this case.

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
Pablo Matias Gomez
  • 6,614
  • 7
  • 38
  • 72
  • 2
    The main reason is it's dangerous to add (default) methods on a popular interface, since it may break some subtypes in the wild. It is very likely that someone has a subtype of List with its own `map` method; it won't compile under java 8 if `List.map` is added in java 8. – ZhongYu Oct 10 '15 at 17:48
  • 2
    That's why they were very conservative in adding methods to Collections APIs. And still there have been complaints about breakage. – ZhongYu Oct 10 '15 at 17:49
  • @bayou.io well, that's a good and I think you should write it as one. – Pablo Matias Gomez Oct 11 '15 at 17:28

1 Answers1

2

Getting rid of .stream() is very simple. Just update your API to return the streams, not collections. Also name your methods returning streams using just plural nouns. So it's better to replace

public List<Friend> getFriends(String userId) { ... }

With

public Stream<Friend> friends(String userId) { ... }

This scenario is widely used in JDK. Examples: BufferedReader.lines(), Random.ints(), ZipFile.entries(), String.chars() and so on (more to come in JDK-9).

The second thing you may do is to import statically Collectors:

import static java.util.stream.Collectors.*;

This will shorten Collectors.toList() to simple toList():

List<String> ids = this.friends(userId).map(Friend::getFriendUserId).collect(toList());

Which is already much shorter.

Finally you should think whether it's actually necessary to collect the resulting stream into List. Sometimes it's reasonable, but often it's just pre-Java-8 thinking like you need everything in collections. For example, if you want to use this list to iterate over it, why not iterating the stream itself using Stream.forEach()?

Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • 2
    I disagree with the sentiment; `List` is still a very useful concept; we cannot second guess programmer's choice of using List. Even if `Stream` is better in a use case, from some ivory tower point of view, we cannot blame a programmer for using `List` if it models the problem better, from his point of view. After all, a computer language is there to serve the programmer. – ZhongYu Oct 10 '15 at 17:53
  • You have your point there, but I agree with @bayou.io 's comment because I wont change all my model.. – Pablo Matias Gomez Oct 11 '15 at 17:27