199

How to get first element that matches a criteria in a stream? I've tried this but doesn't work

this.stops.stream().filter(Stop s-> s.getStation().getName().equals(name));

That criteria is not working, the filter method is invoked in an other class than Stop.

public class Train {

private final String name;
private final SortedSet<Stop> stops;

public Train(String name) {
    this.name = name;
    this.stops = new TreeSet<Stop>();
}

public void addStop(Stop stop) {
    this.stops.add(stop);
}

public Stop getFirstStation() {
    return this.getStops().first();
}

public Stop getLastStation() {
    return this.getStops().last();
}

public SortedSet<Stop> getStops() {
    return stops;
}

public SortedSet<Stop> getStopsAfter(String name) {


    // return this.stops.subSet(, toElement);
    return null;
}
}


import java.util.ArrayList;
import java.util.List;

public class Station {
private final String name;
private final List<Stop> stops;

public Station(String name) {
    this.name = name;
    this.stops = new ArrayList<Stop>();

}

public String getName() {
    return name;
}

}
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
user2147674
  • 2,319
  • 2
  • 14
  • 17

3 Answers3

364

This might be what you are looking for:

yourStream
    .filter(/* your criteria */)
    .findFirst()
    .get();

And better, if there's a possibility of matching no element, in which case get() will throw a NPE. So use:

yourStream
    .filter(/* your criteria */)
    .findFirst()
    .orElse(null); /* You could also create a default object here */


An example:
public static void main(String[] args) {
    class Stop {
        private final String stationName;
        private final int    passengerCount;

        Stop(final String stationName, final int passengerCount) {
            this.stationName    = stationName;
            this.passengerCount = passengerCount;
        }
    }

    List<Stop> stops = new LinkedList<>();

    stops.add(new Stop("Station1", 250));
    stops.add(new Stop("Station2", 275));
    stops.add(new Stop("Station3", 390));
    stops.add(new Stop("Station2", 210));
    stops.add(new Stop("Station1", 190));

    Stop firstStopAtStation1 = stops.stream()
            .filter(e -> e.stationName.equals("Station1"))
            .findFirst()
            .orElse(null);

    System.out.printf("At the first stop at Station1 there were %d passengers in the train.", firstStopAtStation1.passengerCount);
}

Output is:

At the first stop at Station1 there were 250 passengers in the train.
Andrew Fielden
  • 3,751
  • 3
  • 31
  • 47
ifloop
  • 8,079
  • 2
  • 26
  • 35
  • Can you give me an example for Criteria, please? It should represent something like for(Stop s:listofstops){ if(s.name.equals("Linz") return r } – user2147674 Apr 08 '14 at 14:48
  • it s not working. I get an error "no such local var s" – user2147674 Apr 08 '14 at 14:50
  • 1
    Stops is another class, the method filter is invokaded in Train but i want to walk through all Stop elements of the SortedSet stops – user2147674 Apr 08 '14 at 14:54
  • @user2147674 I think that if your lambda has one argument that is *not* in parentheses, you can't put a type on it. Try `(Stop s)->...`. – ajb Apr 08 '14 at 14:54
  • @user2147674 edited my post, added an example. But as [Luiggi Mendoza](http://stackoverflow.com/users/1065197/luiggi-mendoza) said, you already have your own critera in your question. – ifloop Apr 08 '14 at 14:55
  • It's not working. Is it not possible run through all Objects of a SortedSet with Streams? – user2147674 Apr 08 '14 at 14:57
  • @user2147674 Please edit your question, add some lines of code showing your set. – ifloop Apr 08 '14 at 14:59
  • @user2147674 your have to put `(` `)` around your `Stop s` in your lambda. – ifloop Apr 08 '14 at 15:03
  • @user2147674 I edited my post, adjusted the example a bit to your classes. Maybe this helps? – ifloop Apr 08 '14 at 15:15
  • And if the first match is first in the stream and there are many items to stream over and/or the criteria is expensive then this is an expensive operation. There should be a stream.filterFirst(predicate). I'll stick to for loop with inner return. – Skystrider Oct 02 '15 at 18:36
  • 2
    Turns out I am wrong - lazy streams prevent the inefficiency: http://stackoverflow.com/questions/23696317/java-8-find-first-element-by-predicate – Skystrider Oct 02 '15 at 18:39
  • What should I do if in my case findFirst can return null? I receive a java.util.NoSuchElementException: No value present – alexpfx Feb 14 '16 at 23:58
  • 4
    @alexpfx you can use `.findFirst().orElse(yourBackUpGoesHere);`. That can also be _null_ `.findFirst().orElse(null);` – ifloop Feb 17 '16 at 12:15
  • This will NOT stop on the first match, it will filter through all of the objects even after it found a match – Michael P Oct 07 '16 at 16:29
  • @MichaelP is there any chance that you specify 'this'? Is it about my post or any of my comments? – ifloop Nov 04 '16 at 20:54
  • @ifLoop Does you answer cause the filter parameter function to be called on every single element in the collection? – Dylanthepiguy Mar 16 '17 at 21:02
  • @Dylanthepiguy Every element until the first element passes the filter (think of it as an if-Statement inside a loop and in the if-branch is a break after your business logic) – ifloop Apr 20 '17 at 09:18
  • I was wondering, if there is no station with the name `Station 1`, then the `filter` function returns an empty stream. In that case will it generate a `NullPointerException` at `get().getPassengerCount() ` ? – iammrmehul Jan 24 '19 at 11:49
  • 3
    @iammrmehul No. `findFirst()` returns an Optional object ([JavaDoc](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#findFirst--)), which might be empty. In this case the call to `get()` will throw the NPE. To prevent that from happening, use `orElse()` instead of `get()` and provide a fallback object (like `orElse(new Station("dummy", -1)`), or store the result of `findFirst()` in a variable and check it with `isEmpty()` before calling `get()` – ifloop Jan 24 '19 at 12:44
  • Problem with this is that filter will be evaluated on every element of the set, not just the first that maches. It's not a logical issue, but potentially a performance sink – Ben Feb 01 '21 at 11:49
10

When you write a lambda expression, the argument list to the left of -> can be either a parenthesized argument list (possibly empty), or a single identifier without any parentheses. But in the second form, the identifier cannot be declared with a type name. Thus:

this.stops.stream().filter(Stop s-> s.getStation().getName().equals(name));

is incorrect syntax; but

this.stops.stream().filter((Stop s)-> s.getStation().getName().equals(name));

is correct. Or:

this.stops.stream().filter(s -> s.getStation().getName().equals(name));

is also correct if the compiler has enough information to figure out the types.

ajb
  • 31,309
  • 3
  • 58
  • 84
  • With the second one, I get a message "create local var" s – user2147674 Apr 08 '14 at 15:04
  • @user2147674 Is that an error message? Or is the compiler just informing you that it's creating a new sort-of "local variable" `s` to use with the lambda? It doesn't really look like an error to me, but I apparently am not using the same compiler as you are. – ajb Apr 08 '14 at 15:07
  • 1
    @user2147674 That's pretty strange. I'm able to use the second example (with `findFirst().get()` applied after `filter`), and I don't get any errors. The third example works for me too. – ajb Apr 08 '14 at 15:16
  • @ajb perhaps I am never the first so I am not able to see anything like "first" here – Prabhat Gaur May 05 '23 at 12:53
9

I think this is the best way:

this.stops.stream().filter(s -> Objects.equals(s.getStation().getName(), this.name)).findFirst().orElse(null);
Martin Volek
  • 315
  • 2
  • 13