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

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

Related

Java Stream: How to avoid add null value in Collectors.toList()?

There is some Java code:
List<Call> updatedList = updatingUniquedList
.stream()
.map(s -> {
Call call = callsBufferMap.get(s);
}
return call;
}).collect(Collectors.toList());
How to avoid avoid to add to final list if call variable is null ?
.filter(Objects::nonNull)
before collecting. Or rewrite it to a simple foreach with an if.
Btw, you can do
.map(callsBufferMap::get)
You can use .filter(o -> o != null) after map and before collect.
There are several options you can use:
using nonnull method in stream: .filter(Objects::nonNull)
using removeIf of list: updatedList.removeIf(Objects::isNull);
So for example the lines can look like this:
List<Call> updatedList = updatingUniquedList
.stream()
.map(callsBufferMap::get)
.filter(Objects::nonNull)
.collect(Collectors.toList());
Maybe you can do something like this:
Collectors.filtering(Objects::nonNull, Collectors.toList())

Break from the inner nested stream when no elements are passed through filter

Here is the example of code that I struggle with:
List<CategoryHolder> categories = ...
List<String> categoryNames = categoryIds.stream()
.map(id -> categories.stream()
.filter(category -> category.getId().equals(id))
.findFirst().get().getName())
.collect(Collectors.toList());
So I have a list of CategoryHolder objects consisting of category.id and category.name. I also have a list of category ids. I want to iterate through ids and for each id I want to iterate through the CategoryHolder list and when id from categoryIds list is matched with a CategoryHolder.id I want to return the category.name. So basically I want to map every value from categoryIds to its category.name.
So the problem is when no values are matched, filter doesn't pass any elements through and there is nothing to collect, so I would like to break from the current inner stream, and continue to the next id from categoryIds list. Is there a way to achieve this in stream API?
You can do like:
categories.stream()
.filter(categoryHolder -> categoryIds.stream()
.anyMatch(id->categoryHolder.getId().equals(id)))
.map(CategoryHolder::getName)
.collect(Collectors.toList());
or for better performance you can do:
Map<String,CategoryHolder> map = categories.stream()
.collect(Collectors.toMap(CategoryHolder::getId, Function.identity()));
List<String> names = categoryIds.stream()
.map(id -> map.get(id).getName())
.collect(Collectors.toList());
The problem is with your call to filter where you are doing .findFirst().get().getName()
This would fail in case empty Optional is returned by findFirst().
You can instead rewrite it as follows:
List<String> categoryNames =
categoryIds
.stream()
.map(id -> categories
.stream()
.filter(catgory -> catgory.getId().equals(id))
.findFirst())
.collect(
ArrayList::new,
(list, optional) -> {
optional.ifPresent(categoryHolder -> list.add(categoryHolder.name));
},
(list, list2) -> {}
);

Remove a character from every item of a list in JAVA

I have a string like this "-FOREST&-JUNGLE&DESERT"
This string is split up into words in a list like so: [-FOREST, -JUNGLE ,DESERT]
List<String> pattern = Arrays.asList(s.split("\\s*&\\s*"));
Every word with a minus at the beginning must be in one new list and every word without in another like so:
List<String> allowedtags = pattern
.stream()
.filter(name -> !name.startsWith("-"))
.collect(Collectors.toList());
List<String> unwantedtags = pattern
.stream()
.filter(name -> name.startsWith("-"))
.collect(Collectors.toList());
My problem is that my unwantedtags keeps words with a minus at the beginning, but for all my operations I need to get rid of it. If I do the example below, it will not work if "FOREST" is in taglist as my unwantedtags contains "-FOREST".
if (taglist.stream().anyMatch(unwantedtags::contains)) {
return !IsWhitelist;
}
So how can I fix my unwantedtags list? Maybe in java 8 stream api?
Moreover I have the feeling that on the one hand it is working fine to that point, but on the other hand I get the feeling that this is a lot of code for getting just two lists. Can it be optimized?
You can map each element to strip out the leading minus sign after it has been detected:
List<String> unwantedtags = pattern
.stream()
.filter(name -> name.startsWith("-"))
.map(name -> name.substring(1))
.collect(Collectors.toList());
The code can be made more efficient by constructing and populating the lists explicitly. Then you can process pattern in a single pass instead of two:
List<String> allowedtags = new ArrayList();
List<String> unwantedtags = new ArrayList();
pattern.stream()
.forEach(name -> {
if (name.startsWith("-")) unwantedtags.add(name.substring(1));
else allowedtags.add(name);
}
);
You are missing a single Operation. With the map() Operator you can take the input string and modify it.
List<String> unwantedtags = pattern
.stream()
.filter(name -> name.startsWith("-"))
.map(name -> name.substring(1))
.collect(Collectors.toList());
You can use groupingBy to divide this collection (by name.startsWith("-")):
Map<Boolean, List<String>> collect = pattern.stream()
.collect(Collectors.groupingBy(name -> name.startsWith("-")));
Then you have:
{false=[DESERT], true=[-FOREST, -JUNGLE]}
After that you can do further processing (with unwantedtags):
List<String> unwantedtags = collect
.getOrDefault(true, List.of()).stream()
.map(name -> name.substring(1))
.collect(Collectors.toList());
Or you can do it in one stream but it can look more complicated.
Map<Boolean, List<String>> collectWithMap = pattern.stream()
.collect(Collectors.groupingBy(name -> name.startsWith("-"),
Collectors.mapping(name -> name.startsWith("-") ? name.substring(1) : name,
Collectors.toList())));
result:
{false=[DESERT], true=[FOREST, JUNGLE]}

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

Merging List<T> and List<Optional<T>>

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

Categories

Resources