【问题标题】:Which is the fastest way for a containsAny check?containsAny 检查的最快方法是什么?
【发布时间】:2019-10-10 10:01:28
【问题描述】:

Something like 'contains any' for Java set? 有几个解决方案

  • Collections.disjoint(A, B)
  • setA.stream().anyMatch(setB::contains)
  • Sets.intersection(set1, set2).isEmpty()
  • CollectionUtils.containsAny()

我的情况 set1 是 new ConcurrentHashMap<>().keySet() 而 set2 是 ArrayList

set1 最多可包含 100 个条目,set2 少于 10 个

或者他们都会做同样的事情并且表现相似?

【问题讨论】:

  • 你可以做一个基准测试,你会知道哪一个在你的场景中表现更好。在我看来,Collections.disjoint(A, B) 应该是首选,因为它内置于 Java 的集合框架中。
  • 有趣的问题。附加问题:setB.stream().anyMatch(setA::contains) 不会比setA.stream.... 表现更好吗?我的理解是 contains 在 Set 上的 O(1) 与列表上的 O(n) 中执行。因此,迭代 10 次并在 100 个元素上检查 contains 比迭代 100 次并在列表中检查 contains 更好。

标签: java


【解决方案1】:
public static void main(String[] args) {
    Map<String, String> map = new ConcurrentHashMap<>();
    List<String> list = new ArrayList<>();

    for (int i = 0; i < 100; i++) {
        map.put(RandomStringUtils.randomNumeric(5), RandomStringUtils.randomNumeric(5));
    }

    for (int i = 0; i < 10; i++) {
        list.add(RandomStringUtils.randomNumeric(5));
    }

    Set<String> set = new HashSet<>(list);

    List<Runnable> methods = new ArrayList<>();
    methods.add(() -> { Collections.disjoint(map.keySet(), list); });
    methods.add(() -> { Collections.disjoint(list, map.keySet()); });

    methods.add(() -> { map.keySet().stream().anyMatch(list::contains); });
    methods.add(() -> { list.stream().anyMatch(map.keySet()::contains); });

    methods.add(() -> { Sets.intersection(map.keySet(), set).isEmpty(); });
    methods.add(() -> { Sets.intersection(set, map.keySet()).isEmpty(); });

    methods.add(() -> { CollectionUtils.containsAny(map.keySet(), list); });
    methods.add(() -> { CollectionUtils.containsAny(list, map.keySet()); });

    for (Runnable method : methods) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            method.run();
        }
        long end = System.currentTimeMillis();
        System.out.println("took " + (end - start));
    }

}

获胜者是Collections.disjoint

took 15
took 32
took 484
took 62
took 157
took 47
took 24
took 32

【讨论】:

  • 虽然我会谨慎,不要只将Collections.disjoint 作为每个问题的最快解决方案 - 这是一个非常有趣的比较。我当然从没想过简单的stream - contains 如果你反转集合会产生如此不同的结果。您是否也为其他/更大的收藏/星座运行它?
【解决方案2】:

setA.stream().anyMatch(setB::contains) 将是最好的,因为所有其他选项都将是非延迟评估,并将在所有元素上执行。

对于流,它将是惰性求值,一旦找到任何匹配项就会返回。

另外,来自Documentation of CollectionUtils.containsAny()

换句话说,如果coll1和coll2的交集(java.lang.Iterable, java.lang.Iterable)不为空,则该方法返回true。

【讨论】:

  • 很确定CollectionUtils.containsAny() 也会在找到匹配项后立即返回。文档指定语义,而不是它的实现方式。
  • @doublep 我不能否认你在说什么
  • @RahulKumar 我查看了 CollectionUtils 的 implementation,基本上它循环了 smaller collection 并检查每个元素是否包含在 greater集合。所以他们的文档与实现没有直接关系。恕我直言,这只是为了方便,实际上应该与setA.stream.anyMatch(setB::contains) 具有相同的复杂性。所以我也更喜欢流。
  • @GameDroids 我也检查过。它很有效,但我更喜欢流,因为我们可以使用“并行流”来进一步加快执行速度。 (免责声明:我自己没有检查过)
猜你喜欢
  • 1970-01-01
  • 2014-01-28
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2011-04-21
  • 2010-11-24
  • 2012-05-20
  • 1970-01-01
相关资源
最近更新 更多