Java 8 - omitting tedious collect method
Java 8 stream api is very nice feature and I absolutely like it. One thing that get's on my nerves is that 90% of the time I want to have input as a collection and output as collections. The consequence is I have to call stream()
and collect()
method all the time:
collection.stream().filter(p->p.isCorrect()).collect(Collectors.toList());
Is there any java api that would let me skip the stream and directly operate on collections (like linq
in c#?):
collection.filter(p->p.isCorrect)
Solution 1:
Yes, using Collection#removeIf(Predicate)
:
Removes all of the elements of this collection that satisfy the given predicate.
Note that it will change the given collection, not return a new one. But you can create a copy of the collection and modify that. Also note that the predicate needs to be negated to act as a filter:
public static <E> Collection<E> getFilteredCollection(Collection<E> unfiltered,
Predicate<? super E> filter) {
List<E> copyList = new ArrayList<>(unfiltered);
// removeIf takes the negation of filter
copyList.removeIf(e -> { return !filter.test(e);});
return copyList;
}
But as @Holger suggests in the comments, if you choose to define this utility method in your code and use it everywhere you need to get a filtered collection, then just delegate the call to the collect
method in that utility. Your caller code will then be more concise.
public static <E> Collection<E> getFilteredCollection(Collection<E> unfiltered,
Predicate<? super E> filter) {
return unfiltered.stream()
.filter(filter)
.collect(Collectors.toList());
}
Solution 2:
You might like using StreamEx
StreamEx.of(collection).filter(PClass::isCorrect).toList();
This has the advantages of being slightly more brief while keeping immutability.
Solution 3:
If you want to operate on collections Guava's FluentIterable is a way to go!
Example (get id's of 10 first vip customers):
FluentIterable
.from(customers)
.filter(customer -> customer.isVIP())
.transform(Client::getId)
.limit(10);
Solution 4:
Streams had a well defined architecture going in, which you can read a lot about. You might want to read about that before you start down this road.
But why not implement a collection, that implements a similar stream interface that wraps up that code for you?
public class StreamableCollection implements Collection, Stream {
...
}
Then you could do some tricky assumptions for your use case. You could still open a stream from the collections interface, but you could also jump straight in and then on the inside of that handle the opening of the stream I suppose.
streamableCollection cs = new streamableCollection();
cs.filter();
cs.stream();
Your IDE will hop you right to implementing everything... just pass everything back to the default implementations.
Solution 5:
I also think the Stream API is good, but verbose for short operations. I've used these utility methods in a few projects:
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Functions {
public static <T,V> List<V> map(final List<T> in, final Function<T, V> function) {
return in == null ? null : map(in.stream(), function);
}
public static <T,V> List<V> map(final Stream<T> in, final Function<T, V> function) {
return in == null ? null : in
.map(function)
.collect(Collectors.toList());
}
public static <T> List<T> filter(final List<T> in, final Predicate<T> predicate) {
return in == null ? null : filter(in.stream(), predicate);
}
public static <T> List<T> filter(final Stream<T> in, final Predicate<T> predicate) {
return in == null ? null : in
.filter(predicate)
.collect(Collectors.toList());
}
}
This lets me do e.g.
List<String> wrapped = Functions.map(myList, each -> "[" + each + "]");
Normally I static import the method as well.