Lets say we have this boring piece of code that we all had to use:
ArrayList<Long> ids = new ArrayList<Long>(); for (MyObj obj : myList){ ids.add(obj.getId()); }
After switching to Java 8, my IDE is telling me that I can replace this code with collect call
, and it auto-generates:
ArrayList<Long> ids = myList.stream().map(MyObj::getId).collect(Collectors.toList());
However its giving me this error:
collect(java.util.stream.Collector) in Steam cannot be applied to: (java.util.stream.Collector, capture, java.util.List>)
I tried casting the parameter but its giving me undefined A
and R
, and the IDE isn't giving any more suggestions.
I'm curious as how can you use collect call
in this scenario, and I couldn't find any information that could guide me properly. Can anyone shed a light?
collect() is one of the Java 8's Stream API's terminal methods. It allows us to perform mutable fold operations (repackaging elements to some data structures and applying some additional logic, concatenating them, etc.) on data elements held in a Stream instance.
A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. The features of Java stream are – A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.
The issue is that Collectors.toList
, not surprisingly, returns a List<T>
. Not an ArrayList
.
List<Long> ids = remove.stream() .map(MyObj::getId) .collect(Collectors.toList());
Program to the interface
.
From the documentation:
Returns a
Collector
that accumulates the input elements into a newList
. There are no guarantees on the type, mutability, serializability, or thread-safety of theList
returned; if more control over the returned List is required, usetoCollection(Supplier)
.
Emphasis mine - you cannot even assume that the List
returned is mutable, let alone that it is of a specific class. If you want an ArrayList
:
ArrayList<Long> ids = remove.stream() .map(MyObj::getId) .collect(Collectors.toCollection(ArrayList::new));
Note also, that it is customary to use import static
with the Java 8 Stream
API so adding:
import static java.util.stream.Collectors.toCollection;
(I hate starred import static
, it does nothing but pollute the namespace and add confusion. But selective import static
, especially with the Java 8 utility classes, can greatly reduce redundant code)
Would result in:
ArrayList<Long> ids = remove.stream() .map(MyObj::getId) .collect(toCollection(ArrayList::new));
I use a lot of collector blocks where I create an empty Array and fill it using a loop so I decided I need a utility class of my own not to write the same lines again ad again, here it is:
public class Collections { public static <T, O> List<T> collect(Set<O> items, Function<? super O, ? extends T> mapper) { return items.stream().map(mapper).collect(Collectors.toCollection(ArrayList::new)); }
}
and use it like this
List<Product> prods = Collections.collect(basket.getOrderItems(), OrderItem::getProduct);
or like this
List<Long> prods = Collections.collect(basket.getOrderItems(), (item)->item.getProduct().getId());
Though it might look like much more easier to read, it seems streams are a little slower in these kind of scenarios, look here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With