58

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)
Toni Toni Chopper
  • 1,833
  • 2
  • 20
  • 29
user3364192
  • 3,783
  • 2
  • 21
  • 30
  • 11
    Note that `(p->p.isCorrect())` is more correctly written as `(MyClass::isCorrect)`. – Boris the Spider May 09 '16 at 20:48
  • 9
    @BoristheSpider, why is it more correctly? – Andrew Tobilko May 09 '16 at 21:40
  • 12
    @AndrewTobilko a lambda may require creation of a `static` method or, at worst, an anonymous `class`. Using a method reference means that none of this is required. – Boris the Spider May 09 '16 at 21:42
  • 10
    @BoristheSpider: I've just tested, and method references appear to result in anonymous classes exactly as lambdas do. (Specifically, I wrote `System.out.println(((Callable) this::toString).getClass())` and `System.out.println(((Callable) () -> toString()).getClass())`, and they both printed stuff like `class com.example.Foo$$Lambda$1/13451323`.) Can you clarify why you believe them to be different? – ruakh May 09 '16 at 22:45
  • 4
    @ruakh that means very little to nothing. What do you expect `getClass()` to print? `null`? Of course it's going to return _something_ - in this case a synthetic class name. – Boris the Spider May 09 '16 at 22:47
  • 3
    @BoristheSpider: My point was not so much that they both print *something*, as that they both print the *same* thing (aside from different numbers). So if one of them is printing a synthetic class name, then so is the other. – ruakh May 09 '16 at 23:26
  • 1
    @ruakh do both methods end up producing the same .class files? – candied_orange May 10 '16 at 05:23
  • 15
    @Boris the Spider: the runtime-generated classes do not differ. There is only a slight difference in efficiency due to the absence of the generated method in the method reference case, but that slight performance difference does *not* allow to classify either of these alternatives as “more correct”. – Holger May 10 '16 at 10:33
  • 5
    @user3364192: your actual problem is that you are needing to collect into a new collection so often. Since you have the source and know the operation (e.g. filter) and a collection is no end in itself, there is no point in creating these intermediate storage objects so often that the complexity of the API bothers you. Instead, do whatever you do with the resulting collection directly with the stream. Rethink your software design… – Holger May 10 '16 at 10:37

10 Answers10

43

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());
}
M A
  • 71,713
  • 13
  • 134
  • 174
  • 10
    Keep in mind, though, that the predicate is negated (compared to `filter`) and that `removeIf` modifies the original collection and does not return a new one. – stholzm May 09 '16 at 19:45
  • 12
    `copyList.removeIf(filter.negate());` – shmosel May 09 '16 at 21:58
  • 2
    Might be interesting to know that from Java 9, you'll be able to do `collection.stream().collect(filtering(p -> p.isCorrect(), toList())`. This is not really the point of [`filtering`](http://download.java.net/java/jdk9/docs/api/java/util/stream/Collectors.html#filtering-java.util.function.Predicate-java.util.stream.Collector-) collector, but I think I'd rather use this than creating the method. – Jean-François Savard May 10 '16 at 00:44
  • 4
    @Jean-FrançoisSavard, how is that better than `collection.stream().filter(p -> p.isCorrect()).collect(toList())`? – shmosel May 10 '16 at 02:15
  • @shmosel Not better, which is why I wrote *"Might be interesting to know"*. Personally, I find it more readable. – Jean-François Savard May 10 '16 at 02:27
  • 1
    @Jean-FrançoisSavard, your version is technically longer. In what way do you consider it "concise"? – shmosel May 10 '16 at 02:28
  • 2
    @shmosel I'm not gonna go in extended discussion on why I personally find it more readable, it's just like that. I just wanted to point another option for the sake of knowledge sharing, which is perfectly fine in a comment. Never have I said that it would be a better option, or not. – Jean-François Savard May 10 '16 at 02:34
  • @Jean-FrançoisSavard, no problem. Wasn't trying to attack you or anything, just curious. – shmosel May 10 '16 at 02:35
  • 2
    @shmosel to perhaps feed some of your curiosity about my first comment, specially *"This is not really the point of filtering collector"*, a `filtering` collector differ from a standard `filter` operation. For example when collecting a map using `groupingBy` and `filtering`, you can collect empty groups while when using `Stream#filter` they would simply be ignored. But for sure, this have nothing to do with the answers and question here. – Jean-François Savard May 10 '16 at 02:40
  • 6
    When you are hiding the operation inside a utility method ala `getFilteredCollection`, there’s no point in using `removeIf` as you can simply say `return unfiltered.stream().filter(filter) .collect(Collectors.toList());` without a difference for the caller. And its shorter *and* more efficient than copying the entire list first and removing items afterwards. – Holger May 10 '16 at 10:29
  • @Holger You're right, it's much better. I added your suggestion. Thanks. – M A May 10 '16 at 10:58
17

You might like using StreamEx

StreamEx.of(collection).filter(PClass::isCorrect).toList();

This has the advantages of being slightly more brief while keeping immutability.

GuiSim
  • 7,361
  • 6
  • 40
  • 50
10

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);
Mifeet
  • 12,949
  • 5
  • 60
  • 108
Jakub Dziworski
  • 394
  • 3
  • 10
  • 8
    This is no better than using Streams. And it returns an `Iterable`, not a `Collection`, which could be accomplished more concisely with [`Iterables.filter()`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Iterables.html#filter(java.lang.Iterable,%20com.google.common.base.Predicate)). – shmosel May 09 '16 at 22:00
  • 2
    You're right. There is however toList() method which I find way more pleasant than stream's collect(Collectors.toList()). – user3364192 May 09 '16 at 22:02
  • 6
    @user3364192 if you use a static import for `Collectors.*`, the stream version is slightly more concise. – shmosel May 09 '16 at 22:07
  • 36
    @user3364192 not only is this no better, in fact _less concise_, than the `Stream` based solution. But is also requires an external library, and provides much less functionality than a `Stream`. I really cannot see how this could be a better solution... – Boris the Spider May 09 '16 at 22:27
7

If you need a filtered view without modifying the original collection, consider Guava's Collections2.filter().

Mifeet
  • 12,949
  • 5
  • 60
  • 108
shmosel
  • 49,289
  • 6
  • 73
  • 138
  • 5
    @user3364192 note that this is obviously not the same. `filter()` returns a `view` so the `Predicate` will be evaluated on each iteration/access and `get` `size` etc become `O(n)` operations rather than `O(1)` (for an `ArrayList`). The three solutions posed here aren't equivalent - they each have their merits and costs. – Boris the Spider May 09 '16 at 20:39
7

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.

Community
  • 1
  • 1
TheNorthWes
  • 2,661
  • 19
  • 35
  • 2
    I think you would want a `Streamable` interface that defines a `stream()` method, and implement that, not `Stream`. It's analogous to `Iterable` vs. `Iterator`. Both `Stream` and `Iterator` are consumed and not reusable so `*able` interfaces get around that by defining a factory method for each time you want to run it again. – Hank D May 09 '16 at 20:03
  • 1
    What will `StreamableCollection` _do_ with `filter`? If it modifies the underlying collection, that's a problem. If it doesn't, it's a view and that's a problem. I'm not convinced you can sensibly implement this. – Boris the Spider May 09 '16 at 20:41
  • Certainly, this was kind of a quick concept @HankD. Good thoughts and for sure cleans up the pattern. – TheNorthWes May 09 '16 at 21:18
  • @BoristheSpider I think this is why Streams aren't implemented like OP wants. You could kludge up something for their use case, but i am not sure that is a very good idea. – TheNorthWes May 09 '16 at 21:20
7

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.

Rory Hunter
  • 3,425
  • 1
  • 14
  • 16
3

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.

itohro
  • 151
  • 5
1

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 Google Collections (Guava Libraries): ImmutableSet/List/Map and Filtering for a discussion on the technique.

Community
  • 1
  • 1
Hank D
  • 6,271
  • 2
  • 26
  • 35
1

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();
sargue
  • 5,695
  • 3
  • 28
  • 43
0

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]

John McClean
  • 5,225
  • 1
  • 22
  • 30