Finding string in optional list of lists - java

I have a list of 'Blocks' that could be null. Each of the blocks contains a list of names that also could be null. I'm trying to find if any of the names in all the blocks is the word "blurb".
I have the following code which does work:
private boolean containsText(List<Blocks> blocks) {
return Optional.ofNullable(blocks)
.map(Collection::stream)
.map(o -> o.map(Blocks::getNames))
.map(e -> e.anyMatch(names -> names != null && names.contains("blurb")))
.orElse(false);
}
But since getNames could return null I have to check for it in the next statement. I could wrap it in another Optional at that point, but then I would end up with an
Optional<Stream<Optional<List<String>>>>
Using names -> names != null
seems cleaner? Or is there a way to simplify this?

With Java-9 you can use Stream.ofNullable as follows:
public boolean containsText(List<Blocks> blocks) {
return Stream.ofNullable(blocks)
.flatMap(b -> b.stream().map(Blocks::getNames).filter(Objects::nonNull))
.anyMatch(s -> s.contains("blurb"));
}

Another simpler variant without the use of Optional would just be :
private boolean containsText(List<Blocks> blocks) {
return blocks != null && blocks.stream()
.map(Blocks::getNames)
.filter(Objects::nonNull)
.anyMatch(list -> list.contains("blurb"));
}
Note: One of your problems is the design where you say, "I have a list of 'Blocks' that could be null", you should fix it to return an empty list for such representation and then get rid of the null check in the above code.

I would not recommend using Optional for null check you can use ternary operator to check blocks is null and simply return false if it is null, else stream the blocks list and check for string
return Objects.isNull(blocks) ? false : blocks.stream()
.map(Blocks::getNames)
.filter(Objects::nonNull)
.anyMatch(list->list.contains("blurb"));
You can also add simple null for existing code
private boolean containsText(List<Blocks> blocks) {
return Optional.ofNullable(blocks)
.map(Collection::stream)
.map(o -> o.map(Blocks::getNames).filter(Objects::nonNull))
.map(e -> e.anyMatch(names -> names.contains("blurb")))
.orElse(false);
}

Related

Filter out null values in Java 8 stream filter expression so exception is not thrown

I have a Java 8 stream expression that has 3 filters and works fine.
I want to guard against null pointer exceptions within the filters for most of the values.
This is the expression:
if(!purchasedTripSegments.isEmpty()) {
filteredList = purchasedTripSegments.stream()
.filter(segment -> PurchasedVendorType.RAIL.equals(segment.getVendorType()))
.filter(distinctByKeys(segment -> Arrays.asList(segment.getBillingMethod(),
segment.getOrigin().getNumberCode(), segment.getDestination().getNumberCode(),
segment.getStopOff().getStopOffLocation().getNumberCode())))
.filter(segment -> segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_LOCAL) ||
(segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_RULE) &&
segment.getDestination().getNumberCode() !=
segment.getStopOff().getStopOffLocation().getNumberCode()))
.collect(Collectors.toList());
}
So the VendorType cannot be null.
So the first filter will be fine.
The 2nd and 3rd filters can have nulls.
The objects (Origin, Destination, StopOff, StopOffLocation) can be null.
And the values (BillingMethod, NumberCode) can be null.
Is there a way to ignore the filter if any of the values in the filter are nulls?
I tried adding .filter(Objects::nonNull)
I have a test case that has a null destination object and the NullPointerException is thrown.
UPDATE
I updated the billingMethod. But I am not clear on how to use Optional to avoid the null checks.
Optional<List<PurchasedTripSegment>> filteredList = Optional.ofNullable(new ArrayList<>());
if(!purchasedTripSegments.isEmpty()) {
filteredList = purchasedTripSegments.stream()
.filter(segment -> PurchasedVendorType.RAIL.equals(segment.getVendorType()))
.filter(distinctByKeys(segment -> Arrays.asList(segment.getBillingMethod(),
segment.getOrigin().getNumberCode(),
segment.getDestination().getNumberCode(),
segment.getStopOff().getStopOffLocation().getNumberCode())))
.filter(segment -> BILLING_METHOD_LOCAL.equals(segment.getBillingMethod())
|| (BILLING_METHOD_RULE.equals(segment.getBillingMethod()) &&
segment.getDestination().getNumberCode() !=
segment.getStopOff().getStopOffLocation().getNumberCode()))
.collect(Collectors.toList());
}
I'm not sure how to apply the changes you suggested to my filter. I tried adding as written but the map() was not recognized.
The middle filter would be the most difficult.
How to check the objects and values for each segment?
UPDATE
As per the comment below implementing a Utility method using Optional.
private Optional<Integer> getDestinationCode(PurchasedCostTripSegment purchasedCostTripSegment) {
return Optional.ofNullable(purchasedCostTripSegment.getDestination()) // empty for 'null'
.map(Destination::getNumberCode);
}
I do a null check for the incoming parameter.
I get an error that the method getNumberCode is not recognized.
The attributes such as the billingMethod, whenever it is possibly null inside the List, it should still work for comparison to get distinct values.
On the other hand, comparing them with some other String constant can be solved in the manner the user FilipRistic suggested.
But, when it is about objects which could be possibly null and you want to access the inner attributes further down safely, you can make use of Optional and chain the accessors. For a sample amongst those, while you want to access the numberCode of your destination which could possibly be null, you can have an accessor in PurchasedTripSegment class to expose this:
Optional<Integer> getDestinationCode() {
return Optional.ofNullable(this.getDestination()) // empty for 'null'
.map(Node::getNumberCode);
}
With similar changes for other accessors, your overall code would update and change to something like:
filteredList = purchasedTripSegments.stream()
.filter(segment -> PurchasedVendorType.RAIL.equals(segment.getVendorType()))
.filter(distinctByKey(segment -> Arrays.asList(segment.getBillingMethod(),
segment.getOriginCode(), segment.getDestinationCode(),
segment.getStopOffLocationCode())))
.filter(segment -> segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_LOCAL) ||
(segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_RULE) &&
segment.getDestinationCode().equals(segment.getStopOffLocationCode())))
.collect(Collectors.toList());
No there isn't any way for filter to know that since it doesn't know in which way you will use element inside Predicate, your only solution is to perform the check for null yourself.
Note that you can avoid check in cases where you are comparing to constant that you know isn't null, instead of writing:
segment.getBillingMethod().equalsIgnoreCase(BILLING_METHOD_LOCAL)
You could write it like this:
BILLING_METHOD_LOCAL.equalsIgnoreCase(segment.getBillingMethod())
This will avoid NPE but it only helps you in few cases not all of them, for other cases you will have to perform check or maybe refactor to return type Optional and your condition could look something like this:
segment.getDestination()
.flatMap(d -> segment.getStopOff()
.map(s -> s.getStopOffLocation)
.filter(s -> s.getNumberCode() == d.getNumberCode()) )
.isPresent();

Java 8 stream flatMap allow null check for each list in the nested list?

I have a nested list to loop through in pre Java 8. My example is very similar to loop through nested list in java 8 which is a great example to follow then I realized that I need to check for null for each list. Plz refer to the below example. If the last condition is met then return true by short-circuiting.
However I am not sure how to check null for each list using list.stream().flatMap().
for(A a : ListA) {
if(a.getListB() != null && !a.getListB().isEmpty()) {
for(B b : a.getListB()) {
if(b.getListC() != null && !p.getListC().isEmpty()) {
for(C c : b.getListC()) {
return (c.name.equalsIgnoreCase("john"));
}
}
}
}
}
This is kind of gross but it works. You essentially check if listB is not null and create a Stream of B. Then filter through Stream of B and check if ListC is null and if not map to a Stream of C. Then just simply check if any of C match the argument.
boolean found = listA.stream()
.filter(a -> a.getListB() != null)
.flatMap(a -> a.getListB().stream())
.filter(b -> b.getListC() != null)
.flatMap(b -> b.getListC().stream())
.anyMatch(c -> c.name.equalsIgnoreCase("john"));

Adding a log when encounter null object in parallel stream

Not sure about the syntax here, new to lambda expressions and the sort.
itemStream.parallel().filter(Objects::nonNull).forEach(
(item) -> randomMethod(item));
What I had previously in a for loop was a check for null and then a warning if encountered a null item
log.warn("Skipping a null item!");
How do I log when it encounters a null object (while still filtering) with the stream based approach?
You can use Stream::peek:
itemStream.parallel()
.peek(item -> { if (item == null) log.warn("Skipping a null item!"); })
.filter(Objects::nonNull)
.forEach(item -> randomMethod(item));
You can also use a method reference for randomMethod (here I assume it's a member of this):
itemStream.parallel()
.peek(item -> { if (item == null) log.warn("Skipping a null item!"); })
.filter(Objects::nonNull)
.forEach(this::randomMethod);
After filtering null items you can't find them!
Simply use this:
itemStream.parallel().forEach((item)-> {
if(item==null)
log.warn("Skipping a null item!");
else
randomMethod(item)
});
With .filter(Objects::nonNull) you're filtering out all the null elements and only retaining the non-null elements which means in the forEach you won't receive any null elements never mind logging it. instead, you want:
itemStream.parallel()
.forEach(item -> {
if(Objects.isNull(item)) { ... } else { ... }; // or if(item == null) ...
});

Java 8 streams nonNull on properties of objects

I have the following snippet that collects specific objects which have a name string property that contains a specific filter filterName.
List<Foo> filteredFoo= fooList.stream()
.filter(Objects::nonNull)
.filter(myFoo -> {
if (Strings.isNullOrEmpty(myFoo.getName()))
return false;
return myFoo.getName().contains(filterName);
}
).collect(Collectors.toList());
It works as expected but I was wondering whether is there a more elegant way to write the if-statement in a functional way and check for empty or null properties in a nicer fashion than having the conditional block in the filter.
Replace second filter with following:
.filter(myFoo -> Optional.ofNullable(myFoo.getName())
.filter(n -> n.contains(filterName))
.isPresent())
or even:
.filter(myFoo -> {
String name = myFoo.getName();
return name != null && name.contains(filterName)
})
Go for the functional style, for the result expression:
.filter(foo -> foo.getName() != null && foo.getName().contains(filterName))
Splitting would not bring more simplicity:
.filter(foo -> foo.getName() != null)
.filter(foo -> foo.getName().contains(filterName))
Using predicates on Foo::getName (Objects::isNull) is senseless complicated too, just in order to spare a variable.
If filterName is not empty itself, Strings.isEmptyOrNull is not needed.
If you have access to Foo Class then move the if conditions to a method isSameName and use filter as below
filter(myFoo -> {return myFoo.isSameName(filterName);})

Java 8 Stream String Null Or Empty Filter

I've got Google Guava inside Stream:
this.map.entrySet().stream()
.filter(entity -> !Strings.isNullOrEmpty(entity.getValue()))
.map(obj -> String.format("%s=%s", obj.getKey(), obj.getValue()))
.collect(Collectors.joining(","))
As you see there is a statement !String.isNullOrEmpty(entity) inside the filter function.
I don't want to use Guava anymore in the project, so I just want to replace it simply by:
string == null || string.length() == 0;
How can I do it more elegant?
In java 11 there is a new method Predicate::not.
So you can filter out empty string :
list.stream()
.filter(Objects::nonNull)
.filter(Predicate.not(String::isEmpty))
You can write your own predicate:
final Predicate<Map.Entry<?, String>> valueNotNullOrEmpty
= e -> e.getValue() != null && !e.getValue().isEmpty();
Then just use valueNotNullOrEmpty as your filter argument.
If you prefer to use commons-lang3, StringUtils has
isEmpty()
isNotEmpty()
isBlank()
isNotBlank()
These methods can be used in filters as method references:
this.stringList.stream().filter(StringUtils::isNotBlank);
or as lambdas:
this.stringList.stream().filter(s -> StringUtils.isNotBlank(s));
You can create your own Strings class with your own predicate:
public class Strings {
public static boolean isNotNullOrEmpty (String str) {
return str != null && !str.isEmpty();
}
}
Then in your code:
.filter(Strings::isNotNullOrEmpty)
But as #fge mentionned, you can't use that on a Map.Entry<?,?>...
You can break down the filter into two steps:
this.map.entrySet().stream()
.filter(entity -> entity.getValue() != null)
.filter(entity -> !entity.getValue().isEmpty())
.map(obj -> String.format("%s=%s", obj.getKey(), obj.getValue()))
.collect(Collectors.joining(","))
On a side note, most Map.Entry.toString() implementations do exactly what you're doing in map(), so you could theoretically just do map(Map.Entry::toString). But I wouldn't rely on that unless you're producing a toString() or something that doesn't require documented or deterministic behavior.
Also, I know you want to abandon Guava, but here's a solution that might make you reconsider:
Joiner.on(',').withKeyValueSeparator("=")
.join(Maps.filterValues(map, Predicates.not(Strings::isNullOrEmpty)));
It works for me: list.stream().filter(el-> el != null && !el.toString().trim().isEmpty()).collect(Collectors.toList());

Categories

Resources