126

I am trying to change some for-each loops to lambda forEach()-methods to discover the possibilities of lambda expressions. The following seems to be possible:

ArrayList<Player> playersOfTeam = new ArrayList<Player>();      
for (Player player : players) {
    if (player.getTeam().equals(teamName)) {
        playersOfTeam.add(player);
    }
}

With lambda forEach()

players.forEach(player->{if (player.getTeam().equals(teamName)) {playersOfTeam.add(player);}});

But the next one doesn't work:

for (Player player : players) {
    if (player.getName().contains(name)) {
        return player;
    }
}

with lambda

players.forEach(player->{if (player.getName().contains(name)) {return player;}});

Is there something wrong in the syntax of the last line or is it impossible to return from forEach() method?

samu
  • 1,936
  • 4
  • 22
  • 26
  • I'm not too familiar with the internals of lambdas yet, but when I ask the question to myself: "What would you be returning from?", my initial suspicion would be that it is not the method. – Gimby May 01 '14 at 11:50
  • 2
    @Gimby Yes, `return` within a statement lambda returns from the lambda itself, not from whatever called the lambda. The terminate a stream early ("short-circuiting") use `findFirst` as shown in [Ian Roberts' answer](http://stackoverflow.com/a/23407107/1441122). – Stuart Marks May 02 '14 at 01:48

5 Answers5

159

The return there is returning from the lambda expression rather than from the containing method. Instead of forEach you need to filter the stream:

players.stream().filter(player -> player.getName().contains(name))
       .findFirst().orElse(null);

Here filter restricts the stream to those items that match the predicate, and findFirst then returns an Optional with the first matching entry.

This looks less efficient than the for-loop approach, but in fact findFirst() can short-circuit - it doesn't generate the entire filtered stream and then extract one element from it, rather it filters only as many elements as it needs to in order to find the first matching one. You could also use findAny() instead of findFirst() if you don't necessarily care about getting the first matching player from the (ordered) stream but simply any matching item. This allows for better efficiency when there's parallelism involved.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Thanks, that's what I was looking for! There seems to be a lot new in Java8 to explore :) – samu May 01 '14 at 12:05
  • 12
    Reasonable, but I suggest that you not use `orElse(null)` on an `Optional`. The main point of `Optional` is to provide a way to indicate the presence or absence of a value instead of overloading null (which leads to NPEs). If you use `optional.orElse(null)` it buys back all of the problems with nulls. I'd use it only if you can't modify the caller and it's really expecting a null. – Stuart Marks May 02 '14 at 01:46
  • 1
    @StuartMarks indeed, modifying the method return type to `Optional` would be a more natural way to fit into the streams paradigm. I was just trying to show how to duplicate the existing behaviour using lambdas. – Ian Roberts May 02 '14 at 07:45
  • for ( Part part : parts ) if ( !part.isEmpty() ) return false; I wonder what is really shorter. And clearer. The java stream api have truly destroyed java language and java environment. Nightmare to work in any java project in 2020. – mjs May 02 '20 at 12:47
20

I suggest you to first try to understand Java 8 in the whole picture, most importantly in your case it will be streams, lambdas and method references.

You should never convert existing code to Java 8 code on a line-by-line basis, you should extract features and convert those.

What I identified in your first case is the following:

  • You want to add elements of an input structure to an output list if they match some predicate.

Let's see how we do that, we can do it with the following:

List<Player> playersOfTeam = players.stream()
    .filter(player -> player.getTeam().equals(teamName))
    .collect(Collectors.toList());

What you do here is:

  1. Turn your input structure into a stream (I am assuming here that it is of type Collection<Player>, now you have a Stream<Player>.
  2. Filter out all unwanted elements with a Predicate<Player>, mapping every player to the boolean true if it is wished to be kept.
  3. Collect the resulting elements in a list, via a Collector, here we can use one of the standard library collectors, which is Collectors.toList().

This also incorporates two other points:

  1. Code against interfaces, so code against List<E> over ArrayList<E>.
  2. Use diamond inference for the type parameter in new ArrayList<>(), you are using Java 8 after all.

Now onto your second point:

You again want to convert something of legacy Java to Java 8 without looking at the bigger picture. This part has already been answered by @IanRoberts, though I think that you need to do players.stream().filter(...)... over what he suggested.

Community
  • 1
  • 1
skiwi
  • 66,971
  • 31
  • 131
  • 216
13

If you want to return a boolean value, then you can use something like this (much faster than filter):

players.stream().anyMatch(player -> player.getName().contains(name));
Sri
  • 4,613
  • 2
  • 39
  • 42
8

This what helped me:

List<RepositoryFile> fileList = response.getRepositoryFileList();
RepositoryFile file1 = fileList.stream().filter(f -> f.getName().contains("my-file.txt")).findFirst().orElse(null);

Taken from Java 8 Finding Specific Element in List with Lambda

ybonda
  • 1,546
  • 25
  • 38
JavaGoPro
  • 232
  • 3
  • 7
2

You can also throw an exception:

Note:

For the sake of readability each step of stream should be listed in new line.

players.stream()
       .filter(player -> player.getName().contains(name))
       .findFirst()
       .orElseThrow(MyCustomRuntimeException::new);

if your logic is loosely "exception driven" such as there is one place in your code that catches all exceptions and decides what to do next. Only use exception driven development when you can avoid littering your code base with multiples try-catch and throwing these exceptions are for very special cases that you expect them and can be handled properly.)

nabster
  • 1,561
  • 2
  • 20
  • 32