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.
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.
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<Player>, now you have aStream<Player>. - Filter out all unwanted elements with a
Predicate<Player>, 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 isCollectors.toList().
This also incorporates two other points:
- Code against interfaces, so code against
List<E>overArrayList<E>. - 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.
Break or return from Java 8 stream forEach? - Stack Overflow
Getting return list from forEach java 8 - Stack Overflow
.forEach() on a list vs .forEach() on a stream. What's the difference?
Why does the List interface have forEach, but not map?
If you need this, you shouldn't use forEach, but one of the other methods available on streams; which one, depends on what your goal is.
For example, if the goal of this loop is to find the first element which matches some predicate:
Optional<SomeObject> result =
someObjects.stream().filter(obj -> some_condition_met).findFirst();
(Note: This will not iterate the whole collection, because streams are lazily evaluated - it will stop at the first object that matches the condition).
If you just want to know if there's an element in the collection for which the condition is true, you could use anyMatch:
boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);
A return in a lambda equals a continue in a for-each, but there is no equivalent to a break. You can just do a return to continue:
someObjects.forEach(obj -> {
if (some_condition_met) {
return;
}
})
What you are looking for is called the map operation:
Thing[] functionedThings = Arrays.stream(things).map(thing -> functionWithReturn(thing)).toArray(Thing[]::new);
This method is used to map an object to another object; quoting the Javadoc, which says it better:
Returns a stream consisting of the results of applying the given function to the elements of this stream.
Note that the Stream is converted back to an array using the toArray(generator) method; the generator used is a function (it is actually a method reference here) returning a new Thing array.
You need map not forEach
List<Thing> functionedThings = Array.stream(things).map(thing -> functionWithReturn(thing)).collect(Collectors.toList());
Or toArray() on the stream directly if you want an array, like Holger said in the comments.
I read that .forEach() on a list can modify the underlying list, so I tried this:
List<Integer> nums = Arrays.asList(1, 2, 3);
nums.forEach(num -> num++);
System.out.println(nums);The printed numbers are 1, 2, 3, so nothing changed! Why not?
I understand that someStream.forEach() is based on functional programming, which doesn't allow side effects, but I still don't really grasp the difference.