Java 8 Lambda - 两个列表的交集

47

我正在尝试基于某些条件查找两个列表的交集,并执行一些步骤。目前还没有找到方法(在学习阶段):)

我正在尝试基于某些条件查找两个列表的交集,并执行一些步骤。目前还没有找到方法(在学习阶段):)

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();
        }
    }
}

正如您所看到的,逻辑很简单。

根据某些过滤条件从订单中获取所有项目list,并与另一个list交集,然后执行一些操作。


4
找到交集最有效的方法是使用Set或Map。我建议您通过收集适当的groupingBy来构建一个Set。 - Peter Lawrey
@PeterLawrey,你能帮我用Lambda实现同样的功能吗?我只是为了找到交集而创建列表。在典型的Java编码中,我会使用Map :) - RaceBase
3个回答

143

最简单的方法是这样的:

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

8
我已经看到这个例子。我该如何将其用于我的情况?因为 list1list2 是不同类型的,并且我需要在假设 list1.id == list2.fk_id 的情况下进行比较。 - RaceBase
8
作为优化,我会首先将list2转换成一个HashSet。 - Peter Lawrey
6
包含操作的时间复杂度为O(n),因此这是一个O(n^2)的集合交集操作。 - fairidox
1
我已经尝试过这个,但我认为应该使用==而不是equals来使用::contains,因为结果可能为空。 - Maxi Wu
3
为了使交集正常工作,我们需要为 T 实现 HashCode 和 equals 方法。 - ben rhouma moez
我宁愿使用Set<T>来消除重复项。如果需要,您还可以添加.filter(Objects::nonNull)以避免空值。 - Saša

14

我需要将它们进行比较,假设list1.id == list2.fk_id

首先建立一个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();
        });

通过使用reduce函数,您可以避免使用对数组对象的引用。例如,请查看Collectors.averagingDouble的实现方式。但我发现这更加复杂。

注意:通过使用ID集合而不是使用匹配ID列表,这是O(N)。后者将是O(N²)。


2
不确定 O(N)。由于您需要在第二个列表上执行“包含”,我认为这仍然是O(N^2)。 - HakunaM
2
@HakunaM 使用哈希集合的 Set.contains 的时间复杂度为 O(1) 摊销(对于树集合为 O(log N))。Collectors.toSet() 返回一个哈希集合。 - Peter Lawrey

-8

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

只要 T 是一种简单的类型,如 StringIntegerFloat 等类型,其 equals 和 HashCode 方法都是简单明了的,上面这段代码就可以正常工作。但如果 T 是一个自定义对象,则需要实现 HashCode 和 Equals 方法。


12
这个答案比得到最高投票的答案多了一年,除了更啰嗦、格式不太一样外,含义并没有任何区别。 - harschware

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接