47

I am trying to find intersection of two lists based on some condition and doing some steps. Couldn't find a way to do it (in learning stage) :)

Double totalAmount = 0.00d;
Double discount = 0.00d;


List<OrderLineEntry> orderLineEntryList = orderEntry.getOrderReleases().stream()
    .flatMap(orderReleaseEntry -> orderReleaseEntry.getOrderLines().stream())
    .filter(orderLineEntry -> orderLineEntry.getStatus().equals("PP") || orderLineEntry.getStatus().equals("PD"))
    .collect(Collectors.toList());

for (OrderLineEntry orderLineEntry : orderLineEntryList) {
    for (SplitLineEntry splitLineEntry : splitReleaseEntry.getLineEntries()) {
        if (splitLineEntry.getOrderLineId().equals(orderLineEntry.getId()) && splitLineEntry.getStatusCode() != "PX") {
            totalAmount += orderLineEntry.getFinalAmount();
            couponDiscount += orderLineEntry.getCouponDiscount() == null ? 0.00d : orderLineEntry.getCouponDiscount();
        }
    }
}

As you see, the logic is simple

Get All items from order based on some filter list and intersect with another list and do some stuff.

SQB
  • 3,926
  • 2
  • 28
  • 49
RaceBase
  • 18,428
  • 47
  • 141
  • 202
  • 4
    The most efficient way to find an intersection is to use a Set or a Map. I suggest you build a set by collecting an appropriate groupingBy. – Peter Lawrey Jul 28 '15 at 17:41
  • @PeterLawrey, Can you help me achieve the same with Lambda. I am creating list just to find intersection. Typical Java coding, I would use `Map` :) – RaceBase Jul 29 '15 at 04:23

3 Answers3

143

The simplest approach is this:

List<T> intersect = list1.stream()
                         .filter(list2::contains)
                         .collect(Collectors.toList());
Makoto
  • 104,088
  • 27
  • 192
  • 230
Silas Reinagel
  • 4,155
  • 1
  • 21
  • 28
  • 8
    I have seen this example. How do i use this for my use case. Since `list1` and list2 are of different types and I need to compare them on assume `list1.id == list2.fk_id` – RaceBase Jul 29 '15 at 04:24
  • 8
    As an optimisation I would turn list2 into a HashSet first. – Peter Lawrey Jul 29 '15 at 04:59
  • 6
    contains is O(n), so this is a O(n^2) set intersection operation – fairidox Aug 15 '16 at 21:39
  • 1
    I have tried this, but I think ::contains use == instead of equals, the result intersection may be empty. – Maxi Wu Dec 21 '16 at 09:06
  • 3
    We need to implement HashCode & equals for T, in order to intersect works fine. – ben rhouma moez Nov 07 '17 at 09:46
  • I would rather use Set as the outcome to eliminate duplicates. You could also add `.filter(Objects::nonNull)` to avoid null values if you want. – Saša Oct 18 '19 at 10:20
14

I need to compare them on assume list1.id == list2.fk_id

First build up a set of the fk_id;

Set<Integer> orderLineEntrSet = orderEntry.getOrderReleases().stream()
    .flatMap(orderReleaseEntry ->
orderReleaseEntry.getOrderLines().stream())
    .filter(orderLineEntry -> { 
            String s = orderLineEntry.getStatus(); 
            return "PP".equals(s) || "PD".equals(s); 
    })
    .map(e -> e.getId())
    .collect(Collectors.toSet());

double[] totalAmount = { 0.0 };
double[] couponDiscount = { 0.0 };
orderLineEntryList.stream()
    .flatMap(sre -> sre.getLineEntries().stream())
    .filter(ole -> orderLineEntrySet.contains(ole.getOrderLineId())
    .filter(ole -> !"PX".equals(ole.getStatusCode()))
    .forEach(ole -> {
            totalAmount[0] += ole.getFinalAmount();
            if (ole.getCouponDiscount() != null)
                couponDiscount[0] += ole.getCouponDiscount();
        });

You can avoid using a reference to an array object by using reduce function. e.g. see how Collectors.averagingDouble is implemented. But I find this more complicated.

Note: this is O(N) by using a set of ids rather than using a list of matching ids which would be O(N^2)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 2
    Not sure about the O(N). Since you need to do a 'contains' on the second list, I'd think this is still O(N^2). – HakunaM Jan 05 '17 at 18:27
  • 2
    @HakunaM Set.contains using a hash set is O(1) amortized (and O(log N) for a tree set) Collectors.toSet() returns a hash set. – Peter Lawrey Jan 06 '17 at 14:58
-8

List<T> intersect = list1.stream().filter(set1::contains).collect(Collectors.toList());

This will work as long as T is a String, Integer, Float, etc. where the equals and HashCode methods are straightforward. But if the T is a custom object, we need to implement HashCode & equals

Erik
  • 503
  • 1
  • 7
  • 26
psekar
  • 89
  • 1
  • 6
  • 12
    this answer, added a year later, is no different than top voted answer other than being more wordy and less formatted.. – harschware Nov 16 '17 at 04:37