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.