Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use collect call in Java 8?

Tags:

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?

like image 440
Alexandru Severin Avatar asked Nov 13 '14 09:11

Alexandru Severin


People also ask

What is the use of collect in Java 8?

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.

What is stream () in Java?

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.


2 Answers

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 new List. There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned; if more control over the returned List is required, use toCollection(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)); 
like image 122
Boris the Spider Avatar answered Sep 27 '22 18:09

Boris the Spider


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

like image 25
azerafati Avatar answered Sep 27 '22 19:09

azerafati