Java 8 - omitting tedious collect method
JavaJava 8Java StreamJava Problem Overview
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)
Java Solutions
Solution 1 - Java
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 - Java
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 - Java
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 - Java
If you need a filtered view without modifying the original collection, consider Guava's Collections2.filter()
.
Solution 5 - Java
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 6 - Java
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.
Solution 7 - Java
If you are open to using a third party library, you could use Eclipse Collections which has rich APIs directly available under collections. Your example can be written as below with Eclipse Collections.
collection.select(p->p.isCorrect)
collection.select(MyClass::isCorrect)
Note: I am a committer for Eclipse Collections.
Solution 8 - Java
You might try this, from the guava library. It seems a little less cluttered than the Stream approach.
ImmutableList.copyOf(Iterables.filter(collection, MyClass::isCorrect));
See https://stackoverflow.com/questions/6176918/google-collections-guava-libraries-immutableset-list-map-and-filtering for a discussion on the technique.
Solution 9 - Java
Yes, there are several libraries that address Java 8's stream verbosity. An incomplete list:
My preference goes with jOOL. I've been using it in my last projects. The others I know but I didn't really use so I can't give you an impression.
Your example with jOOL would be:
Seq.seq(collection).filter(p->p.isCorrect()).toList();
Solution 10 - Java
With cyclops-react you have a number of options.
We can make use of Lazy Extended Collections
CollectionX<String> collection = ListX.of("hello","world");
CollectionX<String> filtered = collection.filter(p->p.isCorrect());
There is support for mutable, immutable and persistent extended collections. Functional operations on collections are lazy (i.e. behave like replayable Streams) and are materialized only on first access.
We can make use of a powerful extened Stream type
ReactiveSeq.fromIterable(collection)
.filter(p->p.isCorrect())
.toList();
[Disclosure I am the lead developer of cyclops-react]