使用lambda表达式是否会提高性能?

12

我需要检查两个列表是否有共同的元素。我想到了两种方法:

方法 01: 循环

private boolean func01 (List<String> list1, List<String> list2) {
    for (String group : list1) {
        for (String funcGroup : list2) {
            if (group.equals(funcGroup)) {
                return true;
            }
        }
    }
    return false;
}

方法 02:Lambda

private boolean func02 (List<String> list1, List<String> list2) {
    return list1.stream().filter(list2::contains).findAny().isPresent();
}

在我看来,我认为第一种方法更易读。我需要了解的是,比较这两种方法是否存在任何差异或优势?


5
一般来说,第一种版本效率会更高,但差别未必很大。不过你也可以通过用“if (list2.contains(group)) { return true; }”替换内部循环来简化第一个版本。 - Louis Wasserman
2
似乎return list1.stream().anyMatch(list2::contains);没问题。真正的问题在于使用Set而不是List将会非常值得。这样做所带来的益处巨大,甚至创建一个由列表填充的集合都是值得的。 - Joop Eggen
3个回答

4

方法一优化:

你不需要使用两个循环,当你找到匹配项时可以立即返回,这样你就可以停止遍历列表 - (例如,如果第一个元素就匹配成功,你只需进行1次迭代,最坏情况下,匹配项在列表的最后一个元素,你必须遍历整个列表才能找到该匹配项)

private boolean func01 (List<String> list1, List<String> list2) {
        for (String group : list1) {
            if (list2.contains(group)) return true;
        }

        return false;
    }

lambda等效优化:

  • findAny().isPresent() - 获取与谓词匹配的元素的可选项,并检查Optional是否存在 - 这相当于anyMatch(),因为两个表达式都返回boolean

  • filter()将始终遍历整个列表

  • anyMatch()具有短路行为-这意味着它会在第一个匹配项上停止

因此,您可以将其重写为:

private boolean func02 (List<String> list1, List<String> list2) {
  return list1.stream().anyMatch(list2::contains);
}
回答你的问题 - 两种方法在性能上没有显著差异,但需要考虑以下几点:
  • 为集合创建流会略微增加开销。

  • 流操作可以并行运行(list1.stream().parallel().anyMatch(list2::contains))。例如,在这种情况下,anyMatch()在同一流上的并行线程中运行,将定期检查前面的线程是否找到匹配项,并停止遍历集合而不继续遍历整个集合。因此,在理论上对于非常大的输入列表,您应该使用并行流获得更好的结果。


尽管内部仍然使用for-each,但它包含equals...如果您使用基于哈希的东西,情况会更有趣,其中包含将优先于再次循环... - Eugene
是的,它内部使用了for循环,但是与在您自己的代码中内联包含的内容相比,使用list.contains更易读,对吧? - hovanessyan
就像我之前所说,如果你正在比较基于哈希的内容,那么使用“contains”更好。在你的特定情况下,我同意这种做法。 - Eugene

4
要回答您的直接问题,如果您想知道使用Lambda表达式是否会带来性能提升,您应该创建一个微基准测试并进行测量。
然而,我想指出的是,您的第二个解决方案不仅使用了Lambda表达式(实际上是方法引用),而且还使用了Stream。一般来说,基于流的解决方案需要更长时间,因为需要运行流管道所需的所有基础设施。然而,大多数情况下,这些解决方案的扩展性更好。
现在,针对您的具体问题,检查两个列表中是否存在共同元素的最佳方法是使用自Java 1.5以来可用的 Collections.disjoint 方法:
return !Collections.disjoint(list1, list2);

2

例如,您可以在这里找到一个非常相关的比较。

一旦代码足够热(第一个会胜出),性能将变得无关紧要。

考虑哪个更易于阅读 - 你的意见是第一个,我的意见是第二个。一旦您了解了Java-8中的流,就再也回不去了。

此外,有多种情况下使用流API会更加易读,并且可以并行处理。此外,流具有内部不同的优化,使得它们非常好用。这些优化主要需要您来完成。


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