How return null by using Stream API? - java

So, I have an ArrayList of autogenerated strings. I want to find the first element that contains some character or else if there is no one element that matches this filter, to apply another filter. In another way, I want to return null object.
So I write this lambda expression:
str.stream()
.filter(s -> s.contains("q"))
.findFirst()
.orElseGet(() -> str.stream()
.filter(s -> s.contains("w"))
.findFirst()
.orElseGet(null))
But if there is no one element that matches this two filters I will have NullPointerException. How, can I get something like: return null?

Unless I'm missing something obvious, simply .orElse(null) instead of the last orElseGet. orElseGet accepts a Supplier and you pass a null, calling get() on a null reference, well...

An additional, simpler approach: you can filter on strings containing q or w, then sort to move those containing q first, find first, return null if the optional is empty:
str.stream()
.filter(s -> s.contains("q") || s.contains("w"))
.sorted((s1, s2) -> s1.contains("q") ? -1 : 1 )
.findFirst()
.orElse(null);
.sorted((s1, s2) -> s1.contains("q") ? -1 : 1 ) will move the strings containing "q" first. Since the stream has been filtered only on values containing either q or w, then returning null will only happen no element is retained.

The problem is Optional::orElseGet(null) which accepts a Supplier<T> and throws the NullPointerException.
Use the Optional::orElse(null) which accepts T which is String in your case.
The following code works:
List<String> str = Arrays.asList("a", "b", "c");
String output = str.stream()
.filter(s -> s.contains("q"))
.findFirst()
.orElseGet(() -> str.stream()
.filter(s -> s.contains("w"))
.findFirst()
.orElse(null)); // <-- HERE
System.out.println(output); // Prints null
Alternatively, use the Optional::orElseGet(Supplier<? extends T> other) to return null. The Supplier itself must be not null:
.orElseGet(() -> null));

orElseGet expects a Supplier, you need to pass a value, which is the case for orElse.
I wouldn't use Stream API here, one iteration is enough to solve the problem:
String wString = null;
for (String s : str) {
if (s.contains("q")) return s;
if (s.contains("w") && wString == null) wString = s;
}
return wString;

Related

How can I replace any instances of null with an empty List in my stream?

In my method below, the variable n can be null, and if it is null I want to return an empty list eg something like List.of. How can I amend the below method to ensure that if a null is found, an empty List is returned and not removed? I tried using filter which I've commented out below, but that just removes any instances where null is found.
private List<Account> getNames(MetaData metadata){
return metadata.getNames()
.stream()
.map(n -> createAccount(n, metadata.getType()))
//.filter(n -> n.getFinalName() != null)
.collect(Collectors.toList());
}
private Account createAccount(String n, Metadata metadata){...}
You can use ternary operator
return metadata.getNames()
.stream()
.map(n -> n!=null ? createAccount(n, metadata.getType()) : /* else do something */)
.collect(Collectors.toList());

Use Java8 map over Optional.Empty()

I have a piece of code where am doing something like:
Optional<College> college = Optional.ofNullable(student)
.map(stud -> stud.getCollege())
.get()
.stream()
.filter(college -> Objects.nonNull(college.getCollegeName()))
.findFirst();
Now, while writing an unit test, I got a catch that what if student comes as null?
It would be effectively like:
Optional.empty() // the same as the student is null
.map(stud -> stud.getCollege())
.get()
.stream()
.filter(college -> Objects.nonNull(college.getCollegeName()))
.findFirst();
Which I think is not fine because I am getting Exception
expected<com.src.exceptions.CollegeNotFoundException> but
was<java.util.NoSuchElementException>
#Update
Updating the question details for clarifications
Yes stud.getCollege() returns a list<>
I agree with #Nikolas approach except that you should not return null, returning null at last is against using Optional
What about this one:
Optional<College> optional = Optional.ofNullable(student)
.map(stud -> stud.getCollegeList())
.orElse(Collections.emptyList())
.stream()
.filter(c -> Objects.nonNull(c.getCollegeName()))
.findFirst();
Calling Optional::get with no previous check Optional::isPresent is dangerous because it might produce CollegeNotFoundException. And it is not the way the Optional shall be used. The idea of Optional is mapping/filtering the values and providing a default value if the Optional ends up with no element (empty).
Assuming Student::getCollege returns List<College> having method College::getCollegeName, you can do the following:
College college = Optional.ofNullable(student)
.map(stud -> stud.getCollege())
// if Optional is empty, then use an empty collection
.orElse(Collections.emptyList())
.stream()
.filter(c -> Objects.nonNull(c.getCollegeName()))
.findFirst()
// get the value or else college is null
.orElse(null);
As long as stud.getCollege() returns null, the Optional becomes empty and an empty list will be streamed. And again the same principle is applied: As long as the list is empty, the filter and findFirst are not be called and null is safely returned (or any default value you wish).
Also note that the line .filter(c -> Objects.nonNull(c.getCollegeName())) might also produce NullPointerException as long as there is not guaranteed stud.getCollege() doesn't return a list with a null element (remember the list is not null itself so Optional treats it as a "valuable" item). The safe code actually looks like:
Optional<College> college = Optional.ofNullable(student)
.map(stud -> stud.getCollege())
.orElse(Collections.emptyList())
.stream()
.filter(c -> c != null && c.getCollegeName() != null)
.findFirst();
Actually, I prefer to return either a null-object, null or Optional itself.

Stream if not null and get the first object

I am trying to apply stream on a list which can be null as it is calling a repository method and I want to get the first element from it if it is not null and compare one of its parameter with request value.
Optional.ofNullable(placementRepository.findAllByAccountId(accountId))
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.findFirst()
.get()
.getPlacementDate()
.isAfter(placementRequest.getPlacementDate())
Currently it is failing at .get if the list itself is null though I filtered with nonNull. I want stream the list if the list is not null and get the first element and compare its parameter against other value. I tried with other alternative filters but no luck
I want stream the list if the list is not null and get the first
element and compare its parameter against other value.
You didn't think of the case of empty or null elements in the returned List of the repository. You have to choose a default value or execute the date comparison only if the optional is present.
For example with a default date value :
final LocalDate defaultDateValue = ...;
Optional.ofNullable(placementRepository.findAllByAccountId(accountId))
.flatMapping(l -> l.stream()
.filter(Objects::nonNull)
.findFirst()
.mapping(o -> o.getPlacementDate())
)
.orElse(defaultDateValue)
.isAfter(placementRequest.getPlacementDate())
With a conditional processing :
Optional<LocalDate> opt =
Optional.ofNullable(placementRepository.findAllByAccountId(accountId))
.flatMapping(l -> l.stream()
.filter(Objects::nonNull)
.findFirst()
.mapping(o -> o.getPlacementDate())
)
if (opt.isPresent()){
boolean isAfter = opt.get().isAfter(placementRequest.getPlacementDate());
// ...
}
Optional<T> findFirst();, so in case you have null or empty stream, then this method returns null.
You should use .findFirst().orElseGet(() -> /* NULL object*/)
The thing is that .findFirst() is able to return no result at all and .get() will fail. You can use .ifPresent() or use .orElseGet()
Considering the type of elements in your list is YourPojo, this code snippet should do what you need:
List<YourPojo> list = placementRepository.findAllByAccountId(accountId);
LocalDate date = placementRequest.getPlacementDate();
boolean flag =
Optional.ofNullable(list) // Optional<List<YourPojo>>
.flatMap(Collection::stream) // Stream<YourPojo>
.filter(Objects::nonNull) // Stream<YourPojo>
.findFirst() // Optional<YourPojo>
.map(YourPojo::getPlacementDate) // Optional<LocalDate>
.map(d -> d.isAfter(date)) // Optional<Boolean>
.orElse(false); // Boolean
Note: If you're using Java 11 or above then .map(d -> d.isAfter(date)) can be replaced by .map(not(date::isBefore)). Not sure if it's more readable though.

Java8 filter and return if only element

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

Return empty element from Java 8 map operation

Using Java 8 stream what is the best way to map a List<Integer> when you have no output for the input Integer ?
Simply return null? But now my output list size will be smaller than my input size...
List<Integer> input = Arrays.asList(0,1,2,3);
List<Integer> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return null;
return Optional.of(out);
})
.collect(Collectors.toList());
I don’t get why you (and all answers) make it so complicated. You have a mapping operation and a filtering operation. So the easiest way is to just apply these operation one after another. And unless your method already returns an Optional, there is no need to deal with Optional.
input.stream().map(i -> crazyFunction(i))
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
It may be simplified to
input.stream().map(context::crazyFunction)
.filter(out -> out!=null && !out.equals(0))
.collect(Collectors.toList());
But you seem to have a more theoretical question about what kind of List to generate, one with placeholders for absent values or one with a different size than the input list.
The simple answer is: don’t generate a list. A List is not an end in itself so you should consider for what kind of operation you need this list (or its contents) and apply the operation right as the terminal operation of the stream. Then you have your answer as the operation dictates whether absent values should be filtered out or represented by a special value (and what value that has to be).
It might be a different answer for different operations…
Replace the map call with flatMap. The map operation produces one output value per input value, whereas the flatMap operation produces any number of output values per input value -- include zero.
The most straightforward way is probably to replace the check like so:
List<Integer> output = input.stream()
.flatMap(i -> {
Integer out = crazyFunction(i);
if (out == null || out.equals(0))
return Stream.empty();
else
return Stream.of(out);
})
.collect(Collectors.toList());
A further refactoring could change crazyFunction to have it return an Optional (probably OptionalInt). If you call it from map, the result is a Stream<OptionalInt>. Then you need to flatMap that stream to remove the empty optionals:
List<Integer> output = input.stream()
.map(this::crazyFunctionReturningOptionalInt)
.flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
.collect(toList());
The result of the flatMap is a Stream<Integer> which boxes up the ints, but this is OK since you're going to send them into a List. If you weren't going to box the int values into a List, you could convert the Stream<OptionalInt> to an IntStream using the following:
flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())
For further discussion of dealing with streams of optionals, see this question and its answers.
Simpler variants of #Martin Magakian 's answer:
List<Integer> input = Arrays.asList(0,1,2,3);
List<Optional<Integer>> output =
input.stream()
.map(i -> crazyFunction(i)) // you can also use a method reference here
.map(Optional::ofNullable) // returns empty optional
// if original value is null
.map(optional -> optional.filter(out -> !out.equals(0))) // return empty optional
// if captured value is zero
.collect(Collectors.toList())
;
List<Integer> outputClean =
output.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList())
;
You can wrap the output into an Optional which may or may not contain a non-null value.
With an output: return Optional.of(out);
Without output: return Optional.<Integer>empty();
You have to wrap into an option because an array cannot contain any null value.
List<Integer> input = Arrays.asList(0,1,2,3);
List<Option<Integer>> output = input.stream()
.map(i -> {
Integer out = crazyFunction(i);
if(out == null || out.equals(0))
return Optional.<Integer>empty();
return Optional.of(out);
})
.collect(Collectors.toList());
This will make sure input.size() == output.size().
Later on you can exclude the empty Optional using:
List<Integer> outputClean = output.stream()
.filter(Optional::isPresent)
.map(i -> {
return i.get();
})
.collect(Collectors.toList());

Categories