gozluklu_marti
gozluklu_marti

Reputation: 55

Java 8 stream with two Lists

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

Answers (7)

user10367961
user10367961

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

Eugene
Eugene

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

dehasi
dehasi

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

Usman Ali
Usman Ali

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

davidxxx
davidxxx

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

Nigel Nop
Nigel Nop

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

Hadi
Hadi

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

Related Questions