【问题标题】:Java 8 Lambda - Intersection of Two ListsJava 8 Lambda - 两个列表的交集
【发布时间】:2015-10-19 10:13:40
【问题描述】:

我正在尝试根据某些条件并执行一些步骤来查找两个列表中的intersection。找不到方法(在学习阶段):)

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 相交并做一些事情。

【问题讨论】:

  • 寻找交叉点最有效的方法是使用 Set 或 Map。我建议您通过收集适当的 groupingBy 来构建一个集合。
  • @PeterLawrey,你能帮我用 Lambda 实现同样的目标吗?我正在创建列表只是为了找到交叉点。典型的 Java 编码,我会使用 Map :)

标签: java lambda java-8 intersection


【解决方案1】:

最简单的方法是这样的:

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

【讨论】:

  • 我看过这个例子。我如何将它用于我的用例。由于list1 和list2 属于不同类型,我需要在假设list1.id == list2.fk_id 上比较它们
  • 作为优化,我会先把 list2 变成一个 HashSet。
  • 包含是O(n),所以这是一个O(n^2)的集合交集操作
  • 这个我试过了,但是我觉得::contains使用==而不是equals,结果交集可能是空的。
  • 我们需要为 T 实现 HashCode & equals,以便相交可以正常工作。
【解决方案2】:

我需要在假设 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 是如何实现的。但我觉得这更复杂。

注意:这是 O(N) 使用一组 id 而不是使用匹配的 id 列表 O(N^2)

【讨论】:

  • 不确定 O(N)。由于您需要在第二个列表上执行“包含”,我认为这仍然是 O(N^2)。
  • @HakunaM Set.contains 使用哈希集是 O(1) 摊销(对于树集 O(log N)) Collectors.toSet() 返回一个哈希集。
【解决方案3】:

List&lt;T&gt; intersect = list1.stream().filter(set1::contains).collect(Collectors.toList());

只要 TStringIntegerFloat 等,这将起作用,其中 equals 和 HashCode 方法很简单。但是如果T是自定义对象,我们需要实现HashCode & equals

【讨论】:

  • 这个答案是一年后添加的,除了更冗长且格式更少之外,与投票率最高的答案没有什么不同..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-31
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多