ChatGPT解决这个技术问题 Extra ChatGPT

Return from lambda forEach() in java

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?

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 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.

I
Ian Roberts

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.


Thanks, that's what I was looking for! There seems to be a lot new in Java8 to explore :)
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.
@StuartMarks indeed, modifying the method return type to Optional<Player> 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.
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.
C
Community

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:

Turn your input structure into a stream (I am assuming here that it is of type Collection, now you have a Stream. Filter out all unwanted elements with a Predicate, mapping every player to the boolean true if it is wished to be kept. 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:

Code against interfaces, so code against List over ArrayList. 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.


S
Sri

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));

y
ybonda

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


n
nabster

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.)