java - 为什么 Collections.synchronizedSet(HashSet) 对于 addAll、retainAll 和 contains 比 HashSet 更快?

标签 java performance set hashset

我运行了一个测试来为我的程序找到最佳的并发 Set 实现,使用非同步的 HashSet 作为控制,并得到了一个有趣的结果:addAll Collections.synchronizedSet(HashSet) 的 >、retainAllcontains 操作似乎比常规 HashSet< 的操作更快。我的理解是 SynchronizedSet(HashSet) 永远不应比 HashSet 快,因为它由带同步锁的 HashSet 组成。我已经进行了多次测试,结果相似。我做错了什么吗?

相关结果:

Testing set: HashSet
Add: 17.467758 ms
Retain: 28.865039 ms
Contains: 22.18998 ms
Total: 68.522777 ms
--
Testing set: SynchronizedSet
Add: 17.54269 ms
Retain: 20.173502 ms
Contains: 19.618188 ms
Total: 57.33438 ms

相关代码:

public class SetPerformance {
    static Set<Long> source1 = new HashSet<>();
    static Set<Long> source2 = new HashSet<>();
    static Random rand = new Random();
    public static void main(String[] args) {
        Set<Long> control = new HashSet<>();
        Set<Long> synch = Collections.synchronizedSet(new HashSet<Long>());

        //populate sets to draw values from
        System.out.println("Populating source");
        for(int i = 0; i < 100000; i++) {
            source1.add(rand.nextLong());
            source2.add(rand.nextLong());
        }

        //populate sets with initial values
        System.out.println("Populating test sets");
        control.addAll(source1);
        synch.addAll(source1);

        testSet(control);
        testSet(synch);
    }

    public static void testSet(Set<Long> set) {
        System.out.println("--\nTesting set: " + set.getClass().getSimpleName());
        long start = System.nanoTime();
        set.addAll(source1);
        long add = System.nanoTime();
        set.retainAll(source1);
        long retain = System.nanoTime();
        boolean test;
        for(int i = 0; i < 100000; i++) {
            test = set.contains(rand.nextLong());
        }
        long contains = System.nanoTime();
        System.out.println("Add: " + (add - start) / 1000000.0 + " ms");
        System.out.println("Retain: " + (retain - add) / 1000000.0 + " ms");
        System.out.println("Contains: " + (contains - retain) / 1000000.0 + " ms");
        System.out.println("Total: " + (contains - start) / 1000000.0 + " ms");
    }
}

最佳答案

您没有预热 JVM。

请注意,您首先运行 HashSet 测试。

  1. 我稍微更改了您的程序以循环运行测试 5 次。在我的机器上,SynchronizedSet 更快,第一次测试。
  2. 然后,我尝试颠倒两个测试的顺序,只运行一次测试。 HashSet 又赢了。

在这里阅读更多相关信息:How do I write a correct micro-benchmark in Java?

此外,查看 Google Caliper用于处理所有这些微基准测试问题的框架。

关于java - 为什么 Collections.synchronizedSet(HashSet) 对于 addAll、retainAll 和 contains 比 HashSet 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25066983/

相关文章:

MySQL - LONGTEXT 字段导致 SELECT-WHERE 语句挂起

C - 有一个简单的循环来进行算术计算;探查器显示这是一个瓶颈。如何加快速度?

java - 在 ArrayList 中查找重复的随机数

python - pandas 中的并行字符串替换

java - 使用 java mail api 触发邮件时在邮件中添加了不必要的附件

delphi - Delphi Prism 中 CharInSet 函数的替换

java - 如何使用对象在 Java 中设置差异?

algorithm - 查找具有非冲突类别的项目子集

java - 由于字符串根据内容存储在字符串常量池中(当我们用 = 初始化它时),因此在这种情况下为什么 s1 == s3 false

java - 如何将数据作为参数从java传递到python脚本