I am unable to understand how to remove the below null check by using Java 8 Optional
for (A objA : listOfObjectsA) {
if (objA.getStringField() == null) continue;
// some code to do if not null
}
if "some code to do if not null" only operates on objA.getStringField() then you can do:
listOfObjectsA.stream()
.map(A::getStringField)
.filter(Objects::nonNull)
.forEach(e -> ...);
However, if you still want to have access to the A elements then as the other answers have shown you have no choice but to perform an explicit objA.getStringField() != null:
listOfObjectsA.stream()
.filter(a -> a.getStringField() != null)
.forEach(a -> ...);
You don't have to use Optionals for that. You can filter the null elements using a Stream:
listOfObjectsA.stream()
.filter(objA -> objA.getStringField() != null)
.forEach (... do something ...);
To use Optional you can write Optional.ofNullable(objA.getStringField()).isPresent(), but that seems pointless.
You could replace your code with Java8 style with streams and Optional. But I presonally think that in this concrete case, this is not better that POJO (which is much more simplier and readable):
Optional.ofNullable(listOfObjectsA).orElse(Collections.emptyList()).stream()
.filter(Objects::nonNull)
.filter(objA -> Objects.nonNull(objA.getStringField()))
.forEach(objA -> {
// some code to do if not null
});
If you're sure, that given list cannot be null and each element objA cannot be null as well, then you can definitely avoid some useless checks in this case:
listOfObjectsA.stream()
.filter(objA -> Objects.nonNull(objA.getStringField()))
.forEach(objA -> {
// some code to do if not null
});
Ideally without optional, you can do
listOfObjectsA.stream().filter(objA -> objA.getStringField() != null)
.forEach(objA -> { /* some code to do if not null*/ });
Related
I have code like this:
public void processList(List<String> list) {
for (String item : list) {
Object obj = getObjectForString(item);
if (obj != null) {
doSomethingWithObject(obj);
} else {
System.err.println("Object was null for " + item);
}
}
}
Ideally I would like to streamline this and avoid the null check using list.stream().map( *blah, blah, blah* ), and doSomethingWithObject if the object is not null, but log the error otherwise (by using the orElse method on an optional). I'm not super savvy with this Java 8 functionality and not sure if there is a nice, slick way to do what I want here or not. Suggestions?
Edit to add a failed attempt at this:
list.stream()
.map(p -> getObjectForString(p))
.map(Optional::ofNullable)
.forEach(
p -> p.ifPresentOrElse(
r -> doSomethingWithObject(r),
() -> System.err.println("Object was null")
));
Even if that code behaved the way I want, it still doesn't append the String from the original list to the error message as I would like it to. But maybe that's too much complexity to try to accomplish with streams like this.
we should propagate the item even after conversion. The slick way is using tuple or pair.
I used Tuple from vavr functional library to do the same. And below is the code for your reference
list.stream()
.map(p -> Tuple.of(p, getObjectForString(p)).map2(Optional::ofNullable))
.forEach(p -> p._2.ifPresentOrElse(
r -> doSomethingWithObject(r),
() -> System.err.println("Object was null" + p._1))
);
Another approach would be to collect the items in to separate 2 buckets/partitions based on if the item had an associated object or not. After that, process the 2 buckets as required:
final Boolean HAS_OBJECT = Boolean.FALSE;
Map<Boolean, List<String>> partitionedMap = list.stream()
.collect(Collectors.partitioningBy(item -> !Objects.isNull(getObjectForString(item))));
partitionedMap.get(HAS_OBJECT).stream()
.map(item -> getObjectForString(item))
.forEach(obj -> doSomethingWithObject(obj));
partitionedMap.get(!HAS_OBJECT)
.forEach(item -> System.err.println("Object was null for " + item));
Even though the below method does not avoid a null check as you wanted in your question, this is just another way to achieve the same result. (Only benefit is that it saves 1-2 lines of code!).
The below code uses Runnable (takes no arguments and returns nothing as well) along with Java 8's Function.
NOTE : I would still recommend the normal for loop :-), as I believe that the below might look fancy, but the for loop is more easy to understand in this particular case.
Function<String, Runnable> func = item -> {
Object obj = getObjectForString(item);
return (obj != null) ? ( () -> doSomethingWithObject(obj))
: ( () -> System.err.println("Object was null for " + item));
};
list.stream().map(func).forEach(Runnable::run);
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.
I need to do a series of null checks ( nested null-checks ) to get an array of strings like below
String[] test;
if(CollectionUtils.isNotEmpty(checkList)){
if(MapUtils.isNotEmpty(checkList.get(0))){
if(StringUtils.isNotBlank(checkList.get(0).get("filename"))){
test = checkList.get(0).get("filename").split("_");
}
}
}
Is there a better way, maybe using Java8 Optional, to perform these kind of nested checks? I unsuccessfully tried to use Optional with flatmap / map.
You could use a long chain of Optional and Stream operations to transform the input step by step into the output. Something like this (untested):
String[] test = Optional.ofNullable(checkList)
.map(Collection::stream)
.orElseGet(Stream::empty)
.findFirst()
.map(m -> m.get("filename"))
.filter(f -> !f.trim().isEmpty())
.map(f -> f.split("_"))
.orElse(null);
I'd strongly encourage you to stop using null lists and maps. It's a lot better to use empty collections rather than null collections, that way you don't have to have null checks all over the place. Furthermore, don't allow empty or blank strings into your collections; filter them out or replace them with null early, as soon as you're converting user input into in-memory objects. You don't want to have to insert calls to trim() and isBlank() and the like all over the place.
If you did that you could simplify to:
String[] test = checkList.stream()
.findFirst()
.map(m -> m.get("filename"))
.map(f -> f.split("_"))
.orElse(null);
Much nicer, no?
Don't nest the ifs, but just unwrap and invert them:
String[] defaultValue = // let this be what ever you want
if(checkList == null || checkList.isEmpty()) {
return defaultValue;
}
Map<String, String> map = checkList.get(0);
if(map == null || map.isEmpty()) {
return defaultValue;
}
String string = map.get("filename");
if(string == null || string.trim().isEmpty()) {
return defaultValue;
}
return string.split("_");
Though this only works when you wrap this extraction logic in a method:
public static String[] unwrap(List<Map<String, String>> checkList) {
...
}
If checkList is null, it will throw null pointer exception on CollectionUtils.isNotEmpty(checkList). Also use in-built empty checker.
Better you should code
if (null != checkList && !checkList.isEmpty()
&& null != checkList.get(0) && !checkList.get(0).isEmpty()
&& StringUtils.isNotBlank(checkList.get(0).get("filename"))) {
test = checkList.get(0).get("filename").split("_");
}
I'm trying to reach a lambda expression avoiding doing this:
for (OrderEntity o: onEntryL) {
for(GeoFenceEventEntity g: o.getGeoFenceEvent()){
if(null != g.getEndAt() && g.getDynamoGeofenceType().equalsIgnoreCase("WAREHOUSE")){
//all of them, get data
}
}
}
And on Lambda trying something like this (with errors):
List<OrderEntity> chargingL = onEntryL.stream()
.map(o->o.getGeoFenceEvent().stream()
.map(g->null != g.getEndAt() && g.getDynamoGeofenceType().equalsIgnoreCase("WAREHOUSE"))
.collect(Collectors.toList()));
Appreciate any help, regards.
OK, update for comment. Assuming you take the OrderEntry if any GeoFenceEventEntity meets your conditions then you can use
List<OrderEntity> chargingL = onEntryL
.stream()
.filter(o -> o.getGeoFenceEvent().stream().anyMatch(g -> null != g.getEndAt() && g.getDynamoGeofenceType().equalsIgnoreCase("WAREHOUSE")))
.collect(Collectors.toList());
I think you want flatMap with filter.
onEntryL.stream()
.map(OrderEntity::getGeoFenceEvent)
.flatMap(e -> e.stream().filter(g -> null != g.getEndAt() && g.getDynamoGeofenceType().equalsIgnoreCase("WAREHOUSE")))
.flatMap(g -> g.getData().stream())
.collect(Collectors.toList());
I'm new to lambdas and filtering streams and stuff in Java 8. I'm having a very hard time with the syntax of converting this to a Java 8 style. Not even convinced it's possible.
private List<RecordWrapperDto>filterRecordWrapperDtos(List<RecordWrapperDto> recordWrapperDtos){
List<RecordWrapperDto> filteredRecordWrapperDtos = new ArrayList<>();
Long recordId = null;
for(RecordWrapperDto rwDto : recordWrapperDtos) {
if(rwDto.getRecordWrapperId() != null && (recordId == null || recordId.equals(rwDto.getRecordId()))) {
filteredRecordWrapperDtos.add(rwDto);
recordId = rwDto.getRecordId();
}
}
return filteredRecordWrapperDtos;
}
Here is one of my attempts (wrong of course):
if(CollectionUtils.isNotEmpty(recordWrapperDtos)) {
filteredRecordWrapperDtos = recordWrapperDtos.stream().filter(c -> {
return (c.getRecordWrapperId() != null && (recordId == null || recordId.equals(c.getRecordId())));
}).collect(Collectors.toList());
}
Tho me a bone? One of the problems seems to be that there's state maintained between each loop. Is it even possible to do that? Also in each example I've see, if you had more than one parameter, e.g. (a,b) -> { stuff }, it was the same type where "a" was one item and "b" was the next item in the collection... what if it's not? Can I use that to pass something in? If so, I'm not finding examples.
Based on your code, you can simplify you algorithm to this:
take the first item that has a getRecordWrapperId not null
take the record id
filter the stream according to this record id
Like this:
Long recordId = recordWrapperDtos
.stream()
.filter(c -> c.getRecordWrapperId() != null)
.map(RecordWrapperDto::getRecordId)
.findFirst()
.orElse(null);
return recordWrapperDtos
.stream()
.filter(c -> c.getRecordWrapperId() != null)
.filter(c -> recordId.equals(c.getRecordId()))
.collect(Collectors.toList());
The sad part is to stream twice to get the non-null recordWrapperId first.