Reputation: 55
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;
}
Upvotes: 13
Views: 47727
Reputation:
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());
Upvotes: 0
Reputation: 120848
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()));
Upvotes: 9
Reputation: 2773
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());
Upvotes: 1
Reputation: 58
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());
Upvotes: 0
Reputation: 131316
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());
Upvotes: 1
Reputation: 180
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());
Upvotes: 0
Reputation: 17289
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);
});
Upvotes: 0