I have a list of objects say car. I want to filter this list based on some parameter using Java 8. But if the parameter is null, it throws NullPointerException. How to filter out null values?
Current code is as follows
requiredCars = cars.stream().filter(c -> c.getName().startsWith("M"));
This throws NullPointerException if getName() returns null.
In this particular example, I think #Tagir is 100% correct get it into one filter and do the two checks. I wouldn't use Optional.ofNullable the Optional stuff is really for return types not to be doing logic... but really neither here nor there.
I wanted to point out that java.util.Objects has a nice method for this in a broad case, so you can do this:
cars.stream()
.filter(Objects::nonNull)
Which will clear out your null objects. For anyone not familiar, that's the short-hand for the following:
cars.stream()
.filter(car -> Objects.nonNull(car))
To partially answer the question at hand to return the list of car names that starts with "M":
cars.stream()
.filter(car -> Objects.nonNull(car))
.map(car -> car.getName())
.filter(carName -> Objects.nonNull(carName))
.filter(carName -> carName.startsWith("M"))
.collect(Collectors.toList());
Once you get used to the shorthand lambdas you could also do this:
cars.stream()
.filter(Objects::nonNull)
.map(Car::getName) // Assume the class name for car is Car
.filter(Objects::nonNull)
.filter(carName -> carName.startsWith("M"))
.collect(Collectors.toList());
Unfortunately once you .map(Car::getName) you'll only be returning the list of names, not the cars. So less beautiful but fully answers the question:
cars.stream()
.filter(car -> Objects.nonNull(car))
.filter(car -> Objects.nonNull(car.getName()))
.filter(car -> car.getName().startsWith("M"))
.collect(Collectors.toList());
You just need to filter the cars that have a null name:
requiredCars = cars.stream()
.filter(c -> c.getName() != null)
.filter(c -> c.getName().startsWith("M"));
The proposed answers are great. Just would like to suggest an improvement to handle the case of null list using Optional.ofNullable, new feature in Java 8:
List<String> carsFiltered = Optional.ofNullable(cars)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
So, the full answer will be:
List<String> carsFiltered = Optional.ofNullable(cars)
.orElseGet(Collections::emptyList)
.stream()
.filter(Objects::nonNull) //filtering car object that are null
.map(Car::getName) //now it's a stream of Strings
.filter(Objects::nonNull) //filtering null in Strings
.filter(name -> name.startsWith("M"))
.collect(Collectors.toList()); //back to List of Strings
You can do this in single filter step:
requiredCars = cars.stream().filter(c -> c.getName() != null && c.getName().startsWith("M"));
If you don't want to call getName() several times (for example, it's expensive call), you can do this:
requiredCars = cars.stream().filter(c -> {
String name = c.getName();
return name != null && name.startsWith("M");
});
Or in more sophisticated way:
requiredCars = cars.stream().filter(c ->
Optional.ofNullable(c.getName()).filter(name -> name.startsWith("M")).isPresent());
Leveraging the power of java.util.Optional#map():
List<Car> requiredCars = cars.stream()
.filter (car ->
Optional.ofNullable(car)
.map(Car::getName)
.map(name -> name.startsWith("M"))
.orElse(false) // what to do if either car or getName() yields null? false will filter out the element
)
.collect(Collectors.toList())
;
you can use this
List<Car> requiredCars = cars.stream()
.filter (t-> t!= null && StringUtils.startsWith(t.getName(),"M"))
.collect(Collectors.toList());
Related
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 am getting list of objects from API
public Optional<List<Employee>> getEmployeeData (String deptId){
List<Employee> employee = departmentClient.employeeData(deptId);
//Based on some condition I am filtering employee list but before that I want to check for null for list.
return Optional.ofNullable(employee).orElse(Collections.emptyList())
.stream()
.filter(Objects::nonNull)
.filter(e -> e.getType != null)
.collect(Collectors.toList());
}
But I think as method return type is Optional<> this is giving error. How can I check null for the List before the Stream and return as an Optional<List<..>>
You returned List<Employee> while your method signature is Optional<List<Employee>>
Try this one:
return employee != null ? Optional.of(employee.stream()
.filter(Objects::nonNull)
.filter(e -> e.getType != null)
.collect(Collectors.toList())) : Optional.ofNullable(Collections.emptyList());
Your solution doesn't work because the result of Optional is List and you collect it through the Stream pipelines back to the List.
Using Java 8 you can wrap all your solution inside Optional or better use the advantage of the Collectors instead:
Optional<List<Employee>> o = Optional
.ofNullable(employees) // employees can be null, right?
.orElse(Collections.emptyList()) // ... if so, then empty List
.stream() // Stream<Employee>
.filter(Objects::nonNull) // Stream<Employee> filtered as non-nulls
.filter(e -> e.getType() != null) // Stream<Employee> with non-null field
.collect(Collectors.collectingAndThen(
Collectors.toList(), // Collected to List<Employee>
Optional::of)); // Collected to Optional<List<Employee>>
The Collectors::collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) method behaves as usual Collector providing a subsequent mapping function taking the collected result. In our case, we simply wrap the List into Optional to be returned.
Collector downstream collects to List<Employee>
Function finisher maps List<Employee> to Optional<List<Employee>>
With Java 9 and higher using Optional::stream, the beginning might be little different:
Optional<List<Employee>> o = Optional
.ofNullable(employees) // null-safe employees
.stream() // Stream<List<Employees>>
.flatMap(List::stream) // Stream<Employees>
.filter(Objects::nonNull)
......
And yet another option:
return Optional.ofNullable(employee)
.map(list -> list.stream()
.filter(Objects::nonNull)
.filter(e -> e.getType() != null)
.collect(Collectors.toList()));
The lambda inside .map(...) is executed only if the emploee list is not null, otherwise an empty Optional is returned.
I have a method takes 2 lists as parameters and as you can see in the method body I want to do some filtering and returning the result to the caller. I wanted to convert this code to the Java 8 stream with lambda expressions but I couldn't figure that out. I ended up creating more than one stream for this and it beats the purpose of this refactoring (IMHO). What I wanted to know is that how I do, in a simple way, refactor this into just one stream?
public Set<CustomerTrack> getCustomerTracks(List<CusomerTrack> tracks, List<Customer> customers) {
Set<CustomerTrack> tracksToSave = new HashSet<>();
for (Customer customer : customers) {
if (customer.getTrack() == null) {
continue;
}
Long allowedTrackId = customer.getTrack().getId();
for (CustomerTrack track : tracks) {
if (Long.valueOf(track.getId()).equals(allowedTrackId)) {
tracksToSave.add(track);
}
}
}
return tracksToSave;
}
Seems that this is what you are after:
customers.stream()
.filter(c -> c.getTrack() != null)
.map(c -> c.getTrack().getId())
.flatMap(id -> tracks.stream().filter(track -> Long.valueOf(track.getId()).equals(id)))
.collect(Collectors.toSet());
Just note that for each id you are iterating the entire list of tracks; this has O(n*m) complexity. This is generally see as bad and you can improve it.
To make it better you would first create a HashSet of ids from Customer; having that HashSet you can now call contains on it with the ids you are interested in, since contains has a time complexity of O(1) (it's really called amortized complexity of O(1)). So now your complexity becomes O(n) + O(1), but since O(1) is a constant, it's really O(n) - much better that what you had before. In code:
Set<Long> set = customers.stream()
.filter(c -> c.getTrack() != null)
.map(c -> c.getTrack().getId())
.collect(Collectors.toSet());
Set<CusomerTrack> tracksToSave = tracks.stream()
.filter(track -> set.contains(track.getId())
.collect(Collectors.toSet()));
An additional way favoring method reference usage :
Set<Track> tracks =
customers.stream()
.map(Customer::getTrack) // customer to track
.filter(Objects::nonNull) // keep non null track
.map(Track::getId) // track to trackId
.flatMap(trackId -> tracks.stream() // collect tracks matching with trackId
.filter(t-> Long.valueOf(t.getId()).equals(trackId))
)
.collect(toSet());
Firstly you can create a Set of allowed Ids:
Set<Long> collect = customers.stream()
.filter(customer -> customer.getTrack() != null)
.map(customer -> customer.getTrack().getId())
.collect(Collectors.toSet());
Then you can filler your track collection
Set<CusomerTrack> tracksToSave = tracks.stream()
.filter(track -> collect.contains(Long.valueOf(track.getId())))
.collect(Collectors.toSet());
Try this one
customers.stream()
.filter(customer -> customer.getTrack() != null)
.map(c -> c.getTrack().getId())
.forEach(allowedTrackId -> {
tracks.stream()
.filter(track -> Long.valueOf(track.getId()).equals(allowedTrackId))
.forEach(tracksToSave::add);
});
The important Operator here is flatMap
Set<CustomerTrack> tracksToSave = customers.stream()
.map(Customer::getTrack)
.filter(track -> track != null)
.flatMap(track -> {
tracks.stream.filter(it -> Long.valueOf(it.getId()).equals(track.getId())))
.collect(Collectors.toSet());
You need to filter the null values first and then filter it with the list of customerTrack.
Hope this answer helps you.
return customers.stream().map(cust-> cust.track).filter(track -> track != null).
collect(Collectors.toList())
.stream().filter(track-> customerTracks.stream()
.anyMatch(ele -> ele.getId() ==
track.getId())).collect(Collectors.toSet());
You could try something like this
customers
.stream()
.map(Customer::getTrack)
.filter(Objects::nonNull)
.map(CustomerTrack::getId)
.flatMap(trackId -> tracks
.stream()
.filter(track -> Long.valueOf(track.getId()).equals(trackId)))
.collect(Collectors.toSet());
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;
Update
Ok, I think I know how to work with streams now. The code in the old post is a mess and I'm not proud of it. So, thank you for you help for directing me in the right direction. I wrote a supplier class, which provides me with items and used static filters and mapper functions:
final TradeSupplier tradeSupplier = new TradeSupplier();
Stream.generate(tradeSupplier).map(TradeSupplier::getPrice)
.map(TradeSupplier::getTradePartner)
.map(TradeSupplier::getTradeInfo)
.filter(TradeSupplier::validateInfo)
.map(TradeSupplier::getPartnerAssetId)
.filter(TradeSupplier::validatePartnerAssetId).forEach(t -> {
if (trade.sendTrade(t)) {
tradeSupplier.finishedItem();
TradeCache.save(t);
}
});
With this design, I don't need flatMap, because it's just an one by one mapping. Additional information is filed into the item, which is just in the stream
I hope, this code is better than the code below... What do you think?
I'm appreciative for any help to improve my understanding of streams :)
Old post
I'm looking for help for the "new" stream api of java 8: first I get a list of items, for every item I collect a list of strings and after that, i want to combine the string with their corresponding item:
input:
item1
item2
wanted output:
item1; string1
item1; string2
item2; string1
item2; string2
item2; string3
Is the following code the right way to use this api?
Code (with stream api)
// input is a list of items
analyst.getInRange(wantMinValue, wantMaxValue)
.stream()
.filter(i -> !haveItem.getName().contains(i.getName())
|| (!haveItem.getModel().contains(i.getModel()) && haveItem
.getQuality() > i.getQuality()))
// get extra information and add it to a list (key: item; value: string)
.collect(Collectors.toMap(s -> s, s -> lounge.getItemId(s)))
.entrySet()
.stream()
// delete all null and empty strings
.filter(e -> e.getValue() != null && !e.getValue().isEmpty())
// for every entry in list, create an list and add to result
.forEach(
e -> {
lounge.getListOfValue(e.getValue(), 1)
.stream()
.filter(s -> s != null && !s.isEmpty())
.map(s -> lounge.getStringFromOldString(s))
.filter(s -> s != null && !s.isEmpty())
.collect(
Collectors
.toCollection(HashSet::new))
// add list to resulting list
.forEach(
s -> {
result.add(new TradeLink(s,
haveItem, e.getKey()));
});
});
First thing: .filter(s -> s != null && !s.isEmpty())
Don't include these things unless these are actually things that can happen. Are empty strings or null strings actually going to come up in your application? (If so, that probably reflects a design flaw in the first place. It may be better to let your application crash, because nulls generally shouldn't be in your program in ways like this.)
Second: don't do the mutable thing you're doing here:
.forEach(
e -> {
lounge.getListOfValue(e.getValue(), 1)
.stream()
.filter(s -> s != null && !s.isEmpty())
.map(s -> lounge.getStringFromOldString(s))
.filter(s -> s != null && !s.isEmpty())
.collect(
Collectors
.toCollection(HashSet::new))
// add list to resulting list
.forEach(
s -> {
result.add(new TradeLink(s,
haveItem, e.getKey()));
});
});
Instead, do something like:
.flatMap(e ->
lounge.getListOfValue(e.getValue(), 1)
.stream()
.map(lounge::getStringFromOldString)
.distinct()
.map(s -> new TradeLink(s, haveItem, e.getKey()))
.collect(toList())