I need to pick the first result based on 3 checks which need to be executed in sequence. i.e if there is no object that meets criterion 1, then we look for any object meeting criterion 2 and so on. Here is my working code
MyClass result = myObjects.stream()
.filter(s -> s.meetsCriterion1())
.findFirst()
.orElseGet(() -> {
return myObjects.stream()
.filter(s -> s.meetsCriterion2())
.findFirst()
.orElseGet(() -> {
return myObjects.stream()
.filter(s -> s.meetsCriterion3())
.findFirst()
.orElseGet(() -> {
return null;
});
});
});
can we improve this code? I am not sure if there is a way to reuse the first stream to evaluate all the criterion.
I would separate the list of criteria from the logic:
List<Predicate<MyClass>> criteria = Arrays.asList(
MyCass::meetsCriterion1,
MyCass::meetsCriterion2,
MyCass::meetsCriterion3
);
MyClass result = criteria.stream()
.flatMap(c -> myObjects.stream().filter(c).limit(1))
.findFirst()
.orElse(null); // questionable. consider redesigning to avoid null.
Related
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());
I am trying to convert an iterative block of code in Java 8 to functional. The functional approach is unable to find the matching message in the set shared.
List<Optional<Message>> allMessages = new ArrayList<>();
Set<Status> allStatuses = getAllStatuses();
//Iterative : Working
Set<StatusMessage> set = new HashSet<>(STATUS_MESSAGE.values());
for (StatusMessage statusMessage : set) {
for (Status status : statusMessage.getStatusAndInfo().keySet()) {
Optional<Message> message = MessageBuilder.createMessage(allStatuses, status, this::createMessage);
if (message.isPresent()) {
allMessages.add(message);
break;
}
}
}
//Functional : Not working - Never adds anything to the
//map even when matching status is present
STATUS_MESSAGE.values().stream()
.distinct()
.map(statusMessage -> statusMessage.getStatusAndInfo().keySet())
.flatMap(Collection::stream)
.map(key -> MessageBuilder.createMessage(allStatuses, key, this::createMessage))
.anyMatch(allMessages::add);
The MessageBuilder.createMessage looks like this:
Optional<Status> matchingStatus = statuses.stream()
.filter(matchingStatus::equals)
.findFirst();
System.out.println("Found : " + matchingStatus.toString());
return matchingStatus.flatMap(creator);
Also, for debugging purposes, how can I see what is happening at each step of the stream? The stack in the debugger in intellij wasn't showing anything in the stream.
This should do it:
STATUS_MESSAGE.values().stream()
.distinct()
.forEach(statusMessage ->
statusMessage.getStatusAndInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.findFirst()
.ifPresent(allMessages::add)
);
UPDATE
To build the result list using toList instead of adding to a list:
List<Optional<Message>> allMessages = STATUS_MESSAGE.values().stream()
.distinct()
.flatMap(statusMessage ->
statusMessage.getStatusAndInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.limit(1)
)
.collect(Collectors.toList());
This should be a comment, but it's too long...
Seems like your MessageBuilder.createMessage method is overcomplicated.
Check below a simplified and more readable version of the same logic:
if (allStatuses.contains(status)) {
System.out.println("Found : " + status.toString());
return creator.apply(status);
}
return Optional.empty();
You should not use forEach for accumulating operations, so this should be more idiomatic:
Function<StatusInfo, Optional<Message>> messageForStatus = statusInfo ->
statusInfo().keySet().stream()
.map(status -> MessageBuilder.createMessage(allStatuses, status, this::createMessage))
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());
allMessages = STATUS_MESSAGE.values().stream()
.distinct()
.map(StatusMessage::getStatusAndInfo)
.map(messageForStatus)
.filter(Optional::isPresent)
.collect(toList());
As a side note, you have too many optionals, you may want to consider unwrapping some earlier, as a list of optionals may just as well be the list of only the present values.
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());
Based on some sports results data, I have a Fixture object which has getHome() and getAway() method. I'd like to shorten this method which I've written to only use a single lambda function (instead of creating a new list and two lambdas), is this possible?
private Collection<FixtureResult> finalResults(Team team) {
List<FixtureResult>finalResults = new ArrayList<>();
List<FixtureResult> homeResults = resultList.stream().filter(fixture ->
fixture.getHome().equals(team))
.collect(toList());
List<FixtureResult> awayResults = resultList.stream().filter(fixture ->
fixture.getAway().equals(team))
.collect(toList());
finalResults.addAll(homeResults);
finalResults.addAll(awayResults);
return finalResults;
}
Simple enough
resultList.stream()
.filter(fixture -> fixture.getHome().equals(team) || fixture.getAway().equals(team)))
.collect(toList());
EDIT: This is on the assumption that order does not matter to you. If your final list needs to have home result and then away, have a look at Elliott Frisch's answer.
If you wan to get fancy with lambdas:
Predicate<FixtureResult> isHome = fr -> fr.getHome().equals(team)
Predicate<FixtureResult> isAway = fr -> fr.getAway().equals(team)
resultList.stream()
.filter(isHome.or(isAway))
.collect(toList()));
You could even extract the compose predicate to test it in isolation, with no streams involved, which is good for more complex predicates:
Predicate<FixtureResult> isHomeOrAway = isHome.or(isAway)
assertTrue(isHomeOrAway(homeFixture));
...
Assuming the order doesn't matter, you can do it on one line. Like,
private Collection<FixtureResult> finalResults(Team team) {
return resultList.stream()
.filter(fixture -> fixture.getHome().equals(team)
|| fixture.getAway().equals(team))
.collect(toList());
}
If the order matters (home results and then away), you can do it with a single List like
private Collection<FixtureResult> finalResults(Team team) {
List<FixtureResult> al = new ArrayList<>(resultList.stream()
.filter(fixture -> fixture.getHome().equals(team)).collect(toList()));
al.addAll(resultList.stream()
.filter(fixture -> fixture.getAway().equals(team)).collect(toList()));
return al;
}
You can simply create a conditions concatenations or can concatenate multiple filter call
Conditions concatenations
myList.stream()
.filter(element -> (condition1 && condition2 && condition3))
Multiple filter call
myList.stream()
.filter(element -> condition1)
.filter(element -> condition2)
.filter(element -> condition3)
You can do the following
someStream.filter(((Predicate<SomeClass>) someObject-> someCondition).or(someObject-> someOtherCondition))
Or you can define your own "or" function that won't cause such a deep hierarchy
#SuppressWarnings("unchecked")
<R> Predicate<R> or(Predicate<R> ...predicates) {
return r -> Arrays.stream(predicates).anyMatch(p -> p.test(r));
}
That gives you a cleaner interface without casting and the nesting
.filter(or(
yourObject -> {
return false;
},
yourObject -> {
return false;
},
yourObject -> {
return false;
},
yourObject -> {
return false;
}
))
I have List with objects
List<FrameworkAdminLeftMenu> menu = getMenuFromDB();
each FrameworkAdminLeftMenu object has method
public Set<FrameworkAdminLeftMenuCategories> getFrameworkAdminLeftMenuCategorieses() {
return this.frameworkAdminLeftMenuCategorieses;
}
and method
public String getCssClassName() {
return this.cssClassName;
}
each FrameworkAdminLeftMenuCategories object has method
public Integer getId() {
return this.id;
}
How can I filter all List and Set to get FrameworkAdminLeftMenuCategories object by getId(1) ?
For example, something like
List<FrameworkAdminLeftMenu> collect = menu.stream()
.filter(
f -> f
.getCssClassName()
.contains("adm-content")
)
.collect(Collectors.toList());
List<FrameworkAdminLeftMenuCategories> categs = collect
.stream()
.filter(
f -> f.
getFrameworkAdminLeftMenuCategorieses()
.stream()
.filter(c -> c.getId() == 1)
)
.collect(Collectors.toList());
If I understand the question correctly, you want to aggregate the categories from all the sets and filter the ones with the right ID. In this case, you should use flatMap.
Try something like this (untested, obviously):
List<FrameworkAdminLeftMenuCategories> categs = menu.stream()
.filter(f -> f.getCssClassName().contains("adm-content"))
.flatMap(f -> f.getFrameworkAdminLeftMenuCategorieses().stream())
.filter(c -> c.getId() == 1)
.collect(Collectors.toList());