Merging List<T> and List<Optional<T>> - java

Given:
List<Integer> integers = new ArrayList<>(Arrays.asList(
10, 12
));
List<Optional<Integer>> optionalIntegers = Arrays.asList(
Optional.of(5),
Optional.empty(),
Optional.of(3),
Optional.of(2),
Optional.empty()
);
List<Integer> unwrappedOptionals = optionalIntegers.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
integers.addAll(unwrappedOptionals);
Is there some better way of unwrapping Optionals, or other way of merging them both into a List<Integer>? It feels extremely wasteful to collect them into a new List before doing addAll().

With new Java-9 Optional.stream() method it can be written this way:
optionalIntegers.stream()
.flatMap(Optional::stream)
.forEach(integers::add);
Prior to Java-9 you can add such method into your own utility class:
public class StreamUtil {
public static <T> Stream<T> fromOptional(Optional<T> opt) {
return opt.isEmpty() ? Stream.empty() : Stream.of(opt.get());
}
}
And use it like this:
optionalIntegers.stream()
.flatMap(StreamUtil::fromOptional)
.forEach(integers::add);

If you don't want to create an intermediate List, add the filtered elements directly to the original integers List using forEach() instead of collect() :
optionalIntegers.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(integers::add);
Or, as Sergey Lagutin suggested, you can use Optional's map() and orElse() methods with a flatMap() :
optionalIntegers.stream()
.flatMap(o -> o.map(Stream::of)
.orElse(Stream.empty()))
.forEach(integers::add);

You could use the other form of ifPresent
ifPresent(Consumer<T>) void
With a simple forEach one could write:
optionalIntegers.stream().forEach(optional -> optional.ifPresent(integers::add));

If you want to merge them into an independent List<Integer> you can use Stream::concat like:
List<Integer> merged = Stream.concat(
integers.stream(),
optionalIntegers.stream().filter(Optional::isPresent).map(Optional::get)
).collect(Collectors.toList());

Related

Use same variable in filter() and map() of a Java 8 stream

To improve performance I want to use the same variable in both filter() and map() of a Java 8 stream.
Example-
list.stream()
.filter(var -> getAnotherObject(var).isPresent())
.map(var -> getAnotherObject(var).get())
.collect(Collectors.toList())
The called method getAnotherObject() looks like-
private Optional<String> getAnotherObject(String var)
In the above scenario I have to call the method getAnotherObject() twice.If I go with a regular for loop then I have to call the method getAnotherObject() only once.
List<String> resultList = new ArrayList<>();
for(String var : list) {
Optional<String> optionalAnotherObject = getAnotherObject(var);
if(optionalAnotherObject.isPresent()) {
String anotherObject = optionalAnotherObject.get();
resultList.add(anotherObject)
}
}
Even with stream I can put all my code in map()-
list.stream()
.map(var -> {
Optional<String> anotherObjectOptional = getAnotherObject(var);
if(anotherObjectOptional.isPresent()) {
return anotherObjectOptional.get();
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
But I believe there must be an elegant way using filter().
You can create a stream like this
list.stream()
.map(YourClass::getAnotherObject)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
YourClass refer to the name of the class where getAnotherObject method is defined
You can use flatMap. Usually this is used to flatten stuff, but here you can
map the element to that element if the optional has a value
map the element to an empty stream if the optional has no value
Like this:
stream
.map(x -> getAnotherObject(x))
.flatMap(x -> x.map(Stream::of).orElse(Stream.of())))

How to get a List<E> from a HashMap<String,List<E>>

I want to extract a List<E> from a Map<String, List<E>> (E is a random Class) using stream().
I want a simple one-line method using java 8's stream.
What I have tried until now :
HashMap<String,List<E>> map = new HashMap<>();
List<E> list = map.values(); // does not compile
list = map.values().stream().collect(Collectors.toList()); // does not compile
map.values() returns a Collection<List<E>> not a List<E>, if you want the latter then you're required to flatten the nested List<E> into a single List<E> as follows:
List<E> result = map.values()
.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Or use forEach
map.forEach((k,v)->list.addAll(v));
or as Aomine commented use this
map.values().forEach(list::addAll);
Here's an alternate way to do it with Java-9 and above:
List<E> result = map.values()
.stream()
.collect(Collectors.flatMapping(List::stream, Collectors.toList()));
In addition to other answers:
List<E> result = map.values()
.stream()
.collect(ArrayList::new, List::addAll, List::addAll);
This could also do the trick.
Simply use :-
map.values().stream().flatMap(List::stream).collect(Collectors.toList());
You can use Collection.stream with flatMap as:
Map<String, List<E>> map = new HashMap<>(); // program to interface
List<E> list = map.values()
.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
or use a non-stream version as:
List<E> list = new ArrayList<>();
map.values().forEach(list::addAll)
You can use Collectors2.flatCollect from Eclipse Collections
List<String> list =
map.values().stream().collect(Collectors2.flatCollect(l -> l, ArrayList::new));
You can also adapt the Map and use the Eclipse Collections MutableMap API.
List<String> list =
Maps.adapt(map).asLazy().flatCollect(l -> l).toList();
The method flatCollect is equivalent to the method flatMap, except flatCollect takes an Iterable instead of a Stream.
Note: I am a committer for Eclipse Collections.

java lambda - how to traverse optional list/stream of optionals

Having an Optional List of Optional's like:
Optional<List<Optional<String>>> optionalList = Optional.of(
Arrays.asList(
Optional.empty(),
Optional.of("ONE"),
Optional.of("TWO")));
How to traverse optionalList to print out the string's ONE and TWO ?
What about having an Optional Stream of Optionals?
Optional<Stream<Optional<String>>> optionalStream = Optional.of(
Stream.of(
Optional.empty(),
Optional.of("ONE"),
Optional.of("TWO")));
Update: Thanks for answers, solution for optionalStream (non nested):
optionalStream
.orElseGet(Stream::empty)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
First, check if the Optional is present. If yes, then stream the list and filter the non-empty ones and print each of them.
optionalList.ifPresent(list -> list.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println));
Almost similar for the stream case too
optionalStream.ifPresent(stream -> stream
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println));
You can indeed stream the Option<String> and filter only non empty values.
Optional<List<Optional<String>>> optionalList = Optional.of(Arrays.asList(Optional.empty(), Optional.of("ONE"), Optional.of("TWO")));
optionalList.orElseGet(ArrayList::new)
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
You can also use Optional.ifPresent() as suggested in another answers :
optionalList.ifPresent(l -> l.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println));
Personally I prefer the first way because it removes a nested level : I find it more pleasant to read.
If you can use Java 9, it can be done like this:
optionalList.ifPresent(list -> list.stream()
.flatMap(Optional::stream)
.forEach(System.out::println));
For a stream of optionals it would be the same, without the first .stream() call.
With Java 8 you don't have the Optional::stream method available so you can do it yourself:
optionalList.ifPresent(list -> list.stream()
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
.forEach(System.out::println));
And for a Stream of Optionals it would look like this:
optionalStream.ifPresent(stream -> stream
.flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
.forEach(System.out::println));
Well ...
Check whether the optional list is present.
Do a "for each" for all elements of the (now present) list.
In each step check whether the optional string is present.
If yes, print it.
A one-liner can do that:
optionalList.ifPresent(list -> list.forEach(s -> s.ifPresent(System.out::println)));
As I see there are two ways, second one look a bit more pretty to me, take a look:
class Scratch {
public static void main(String[] args) {
Optional<String> element1 = Optional.of("test1");
Optional<String> element2 = Optional.empty();
Optional<String> element3 = Optional.of("test2");
Optional<String> element4 = Optional.of("test3");
List<Optional<String>> list = Arrays.asList(element1, element2, element3, element4);
System.out.println(extractStrings1(list));
System.out.println(extractStrings2(list));
}
private static List<String> extractStrings1(List<Optional<String>> list) {
return list.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
private static List<String> extractStrings2(List<Optional<String>> list) {
List<String> result = new ArrayList<>();
list.forEach(element -> element.ifPresent(result::add));
return result;
}
}
optionalList.stream().flatMap(List::stream).filter(Objects::nonNull).forEach(...)

How to use if-else logic in Java 8 stream forEach with another list

I have a situation when the list will be returned
List<String> list2 = list
.stream()
.map(str -> someCondition(str) ? doSomething(str) : doSomethingElse(str))
.collect(Collectors.toList());
doSomething(str) - This action returns List<String>
doSomethingElse(str) -This action returns only String
Can I collect them(List<String> and String) as a common List using .collect? If you have other opinions please let me know. Thanks
Just as if you were writing a method, you need your statement to return a consistent type. At the moment:
str -> someCondition(str) ? doSomething(str) : doSomethingElse(str)
... returns either List<String> or String, so the nearest common superclass is Object. You probably don't want this. I would make it always return List<String>:
Function<String,String> myFunc = str -> someCondition(str)
? doSomething(str)
: Collections.singletonList(doSomethingElse(str));
(I've stored your function in a variable so we don't have to use it inline in later examples. You can still inline it if you prefer.)
Now how you use it depends on what you want to end up with. If you want a List<List<String>>:
List<List<String>> list2 = list
.stream()
.map(myFunc)
.collect(Collectors.toList());
But since you appear to want a List<String>:
List<String> list2 = list
.stream()
.flatMap(myFunc.andThen(List.stream))
.collect(Collectors.toList());
... or if you prefer ...
List<String> list2 = list
.stream()
.map(myFunc)
.flatMap(List::stream)
.collect(Collectors.toList());

For loop including if to parallelStream() expression

Is there a way to parallelize this piece of code:
HashMap<String, Car> cars;
List<Car> snapshotCars = new ArrayList<>();
...
for (final Car car : cars.values()) {
if (car.isTimeInTimeline(curTime)) {
car.updateCalculatedPosition(curTime);
snapshotCars.add(car);
}
}
Update: This is what I tried before asking for assistance:
snapshotCars.addAll(cars.values().parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.collect(Collectors.toList()));
How could I integrate this line? ->
car.updateCalculatedPosition(curTime);
Well, assuming that updateCalculatedPosition does not affect state outside of the Car object on which it runs, it may be safe enough to use peek for this:
List<Car> snapshotCars = cars.values()
.parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.peek(c -> c.updateCalculatedPosition(curTime))
.collect(Collectors.toCollection(ArrayList::new));
I say this is "safe enough" because the collect dictates which elements will be peeked by peek, and these will necessarily be all the items that passed the filter. However, read this answer for the reason why peek should generally be avoided for "significant" operations.
Your peek-free alternative is to first, filter and collect, and then update using the finished collection:
List<Car> snapshotCars = cars.values()
.parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.collect(Collectors.toCollection(ArrayList::new));
snapShotCars.parallelStream()
.forEach(c -> c.updateCalculatedPosition(curTime));
This is safer from an API point of view, but less parallel - you only start updating the positions after you have finished filtering and collecting.
If you want parallelized access to a List you might want to use Collections.synchonizedList to get a thread-safe list:
List<Car> snapshotCars = Collections.synchronizedList(new ArrayList<>());
Then you can use the stream API like so:
cars.values()
.parallelStream()
.filter(car -> car.isTimeInTimeline(curTime))
.forEach(car -> {
car.updateCalculatedPosition(curTime);
snapshotCars.add(car);
});
In addition to RealSkeptic’s answer, you can alternatively use your own collector:
List<Car> snapshotCars = cars.values().parallelStream()
.filter(c -> c.isTimeInTimeline(curTime))
.collect(ArrayList::new,
(l,c) -> { c.updateCalculatedPosition(curTime); l.add(c); },
List::addAll);
Note that .collect(Collectors.toList()) is equivalent (though not necessarily identical) to .collect(Collectors.toCollection(ArrayList::new)) which is equivalent to .collect(ArrayList::new, List::add, List::addAll).
So our custom collector does a similar operation, but replaces the accumulator with a function, which also performs the desired additional operation.

Categories

Resources