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.
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.
Return from lambda forEach() in java - Stack Overflow
Return List from forEach using Stream in java - Stack Overflow
How to get a list output from forEach loop in Java 8 Streams - Stack Overflow
.forEach() on a list vs .forEach() on a stream. What's the difference?
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.
With streams you generally want to avoid creating empty lists and then adding items. Streams should use functional idioms and avoid side effects as much as possible. It's better to work with the stream as a whole and then "collect" the results into a list at the end.
List<C> value = getResponses().stream()
.flatMap(r -> getValues(r).stream())
.collect(Collectors.toList());
I am trying to convert below for-loop to forEach method with help Stream function using Java 1.8.
You shouldn't use a stream along with forEach simply to accumulate into a predefined list as there will be side effects (which should be avoided when dealing with streams), rather go with the stream approach suggested by John Kugelman if you want to perform it with streams or using the forEach method it can also be done as:
List<A> value = new ArrayList<>();
responseList.forEach(response -> value.addAll(getValues(response))));
Instead of using a forEach just use streams from the beginning:
List<PersonWrapper> wrapperList = jrList.stream()
.flatMap(jr -> seniorList.stream()
.filter(sr -> jr.getName().equals(sr.getName()))
.map(sr -> new PersonWrapper(jr, sr))
)
.collect(Collectors.toList());
By using flatMap you can flatten a stream of streams (Stream<Stream<PersonWrapper>>) into a single stream (Stream<PersonWrapper>)
If you can't instantiate wrapperList by yourself or really need to append to it. You can alter above snippet to following:
List<PersonWrapper> wrapperList = new ArrayList<>();
jrList.stream()
.flatMap(jr -> seniorList.stream()
.filter(sr -> jr.getName().equals(sr.getName()))
.map(sr -> new PersonWrapper(jr, sr))
)
.forEach(wrapperList::add);
While Lino's answer is certainly correct. I would argue that if a given person object in jrList can only ever have one corresponding match in seniorList maximum, in other words, if it's a 1-1 relationship then you can improve upon the solution given by Lino by finding the first match as follows:
List<PersonWrapper> resultSet = jrList.stream()
.map(p -> seniorList.stream()
.filter(sr -> p.getName().equals(sr.getName()))
.findFirst()
.map(q -> new PersonWrapper(p, q))
.get())
.collect(Collectors.toList());
or if there is no guarantee that each person in jrList will have a corresponding match in seniorList then change the above query to:
List<PersonWrapper> resultSet = jrList.stream()
.map(p -> seniorList.stream()
.filter(sr -> p.getName().equals(sr.getName()))
.findFirst()
.map(q -> new PersonWrapper(p, q))
.orElse(null))
.filter(Objects::nonNull)
.collect(Collectors.toList());
The difference is that now instead of calling get() on the result of findFirst() we provide a default with orElse in case findFirst cannot find the corresponding value and then we filter the null values out in the subsequent intermediate operation as they are not needed.
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.
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;
}
})
Alternative 2 looks best to me in this case
1. Readability
- Alternative 2 has less number of lines
- Alternative 2 read more close to
return a list containing number divisible by 6 from numList, while forEach approach means add number divisible by 6 from numList tosecondInts filter(i -> i % 6 == 0)is straight forward and
require some time for human brain to process.if(i % 6 != 0) { return; }
2. Performance
From Stream.toList()
Implementation Note: Most instances of Stream will override this method and provide an implementation that is highly optimized compared to the implementation in this interface.
We benefit from optimization from JDK by using Stream API.
And in this case, using forEach and adding element one by one will be slower, especially when the list is large. It is because ArrayList will need to extend it capacity whenever the list full, while Stream implementation ImmutableCollections.listFromTrustedArrayNullsAllowed just store the result array into ListN.
One more point to note about parallelism:
From Stream#forEach
The behavior of this operation is explicitly nondeterministic. For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism. For any given element, the action may be performed at whatever time and in whatever thread the library chooses. If the action accesses shared state, it is responsible for providing the required synchronization.
numbersList.stream().parallel()
.forEach(i -> {
if(i % 6 != 0) {
return;
}
secondInts.add(i);
});
Will provide unexpected result, while
List<Integer> third = numbersList.stream().parallel()
.filter(i -> i % 6 == 0).sorted().forEach()
.toList();
is totally fine.
3. Flexibility
Imagine you want the filtered list to be sorted, in forEach approach, you can do it like:
numbersList.stream().sorted().
.forEach(i -> {
if(i % 6 != 0) {
return;
}
secondInts.add(i);
});
Which is much slower compared to
numbersList.stream()
.filter(i -> i % 6 == 0)
.sorted()
.toList();
As we need to sort the whole numbersList instead of filtered.
Or if you want to limit your result to 10 elements, it is not straight forward to do so with forEach, but just as simple as adding limit(10) when using stream.
4. Less error prone
Stream API usually return Immutable object by default.
From Stream.toList()
Implementation Requirements: The implementation in this interface returns a List produced as if by the following: Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())))
Meaning that the returned list is immutable by default. Some advantages of immutability are:
- You can safely pass the list around to different method without worrying the list is modified.
- Immutable list are thread safe.
Read Pros. / Cons. of Immutability vs. Mutability for further discussion.
I don’t think it’s an issue of advantages. Each mechanism has a specific purpose.
.forEach() returns void so it doesn’t have an output. The intent is that the elements that the forEach iterates through are not modified. The data in the elements are used for some sort of calculation. I find that forEach is used much less than map. It’s a terminal point in a pipeline.
.filter() takes a stream as input and emits a filtered stream as output. It is for filtering.
.map() is like forEach but it emits a stream of modified objects. It allows the same modification to be done on each each element so that it can be saved, filtered or manipulated further.
.toList is a handy shortcut to turn a stream into a list. Using forEach(List::add) where a toList() will do the work is a terrible idea. You’re preventing Java from bulking the activity.