How can I filter a list using java8 streams and return the found element if it is the only element in the filtered list, otherwise(if there are more which meet the condition, or there is no result that meets the condition) return for example an Optional.empty()
I would need something like this:
Suppose I have a:
List<String> list = Arrays.asList("Apple","Banana","Peach");
then I want:
Optional<String> string = list.stream()
.filter(item -> item.startsWith("A"))
.findOne();
I know I can do it by:
boolean singleElement = list.stream()
.filter(item -> item.startsWith("A"))
.count() == 1;
String string = null;
if(singleElement){
string = list.stream().filter(item -> item.startsWith("A")).iterator().next();
}
but I was wondering if I can do it in a single stream?
Is there any single stream solution?
Java stream provides a method filter() to filter stream elements on the basis of given predicate. Suppose you want to get only even elements of your list then you can do this easily with the help of filter method. This method takes predicate as an argument and returns a stream of consisting of resulted elements.
Using Stream findFirst() Method: The findFirst() method will returns the first element of the stream or an empty if the stream is empty. Approach: Get the stream of elements in which the first element is to be returned. To get the first element, you can directly use the findFirst() method.
The findAny() method returns any element from a Stream, while the findFirst() method returns the first element in a Stream.
The filter() function of the Java stream allows you to narrow down the stream's items based on a criterion. If you only want items that are even on your list, you can use the filter method to do this. This method accepts a predicate as an input and returns a list of elements that are the results of that predicate.
Not very pretty, but you could limit
the stream to 2 elements, collect
those in a List
, and see if that list has exactly one element. This still has more than one line, and has some overhead for creating the list, but that overhead is limited (to a list of size 2) and it does not have to iterate the stream twice, either.
List<String> tmp = list.stream()
.filter(item -> item.startsWith("A"))
.limit(2)
.collect(Collectors.toList());
Optional<String> res = tmp.size() == 1 ? Optional.of(tmp.get(0)) : Optional.empty();
(My other idea was to use reduce((s1, s2) -> null)
after limit(2)
and reduce any two matches to null
, but instead of returning an Optional.empty
this will just raise an Exception, i.e. it does not work, but maybe this triggers some better (working) ideas.)
Update: It seems like while reduce
raises an Exceptions, Collectors.reducing
does not, and instead returns an Optional.empty
as desired, so this also works, as shown in this answer to a very similar question. Still, I'd add limit(2)
to make it stop early:
Optional<String> res = list.stream()
.filter(item -> item.startsWith("A"))
.limit(2)
.collect(Collectors.reducing((s1, s2) -> null));
(If you like this last part, please upvote the original answer.)
You could use google Guava
library's MoreCollectors.onlyElement
as below:
List<String> list = Arrays.asList("Apple", "Banana", "Peach");
String string = null;
try {
string = list.stream()
.filter(item -> item.startsWith("A"))
.collect(MoreCollectors.onlyElement());
} catch (NoSuchElementException | IllegalArgumentException iae) {
System.out.println("zero or more than one elements found.");
}
Optional<String> res = string == null ? Optional.empty() : Optional.of(string);
Notice it throws NoSuchElementException
if there is no element and it throws IllegalArgumentException
if there are more than one elements.
I don't know if this counts as a single operation to you, but you can do :
Arrays.asList("Apple", "Banana", "Peach")
.stream()
.collect(Collectors.collectingAndThen(
Collectors.partitioningBy(
x -> x.startsWith("A")),
map -> {
List<String> list = map.get(Boolean.TRUE);
return list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty();
}));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With