我读到在TreeSet中的搜索时间是log(N)的量级,而在HashSet中的搜索时间是1(常量)的量级.现在,为了在程序中测试这一点,我编写了一个Java程序.

import java.util.TreeSet;
import java.util.Random;

public class SearchSpeed {
    int totalElements, target;
    Set<Integer> hashSet, treeSet;
    Random r;

    // parameterized constructor
    SearchSpeed(int totalElements){
        this.totalElements = totalElements;
        r = new Random();
        hashSet = new HashSet<>();
        treeSet = new TreeSet<>();
    }

    // populating the sets and selecting the target element
    void populateSets(){
        hashSet.clear();
        treeSet.clear();
        int num, count = 0;
        while(hashSet.size() != totalElements){
            num = r.nextInt();
            hashSet.add(num);
            treeSet.add(num);
            count++;
            if(count == 5000){    // picking the 5000th element as the target element
                target = num;
            }
        }
    }

    // searching the target element in HashSet
    long searchHash(){
        long initialTime = System.nanoTime();
        hashSet.contains(target);
        long finalTime = System.nanoTime();
        return finalTime - initialTime;
    }

    // searching the target element in TreeSet
    long searchTree(){
        long initialTime = System.nanoTime();
        treeSet.contains(target);
        long finalTime = System.nanoTime();
        return finalTime - initialTime;
    }


    public static void main(String[] args) {

        SearchSpeed ss = new SearchSpeed(10000);

        long hashTime = 0, treeTime = 0;
        for(int i = 0; i < 1000; i++){
            ss.populateSets();
            hashTime += ss.searchHash();
            treeTime += ss.searchTree();
        }
        double avgTreeTime = treeTime/1000.0;
        double avgHashTime = hashTime/1000.0;

        System.out.printf("Time taken by TreeSet MINUS Time Taken by HashSet = %.4f nanoseconds\n",avgTreeTime - avgHashTime);
    }
}

在多次运行这个程序后,我得到了以下结果:

输出1:树集所用时间减go 哈希集所用时间=105.6000纳秒
输出2:树集所用时间减go 哈希集所用时间=99.1000纳秒
输出3:树集所用时间减go 哈希集所用时间=36.8000纳秒
输出4:树集所用时间减go 哈希集所用时间=50.6000纳秒
输出5:树集所用时间减go 哈希集所用时间=-30.4000纳秒

我原本预计时间差会更大一些,但在某些情况下,时间差低至36纳秒,在某些情况下甚至为负值.
这是我应该期待的吗?还是我在编写程序时遗漏了什么?

推荐答案

是.你错过了很多.太多了,事实上,你几乎不可能这样做.不过,好消息是,有一个图书馆可以做到这一点.确切地说,是JMH.

JVM不是一件简单的事情.它运行代码的速度很慢,做各种看似毫无意义的记账,这样它就知道哪0.1%的代码花费了95%的CPU资源(这些不是夸大的数字;这就是大多数应用程序最终要做的事情).一个just个测试某个算法速度的学术 case 显然不是这样的,但JVM针对现实生活中的应用程序进行了优化,而不是测试用例).一旦它弄清楚了这是什么,它就会使用所有看似毫无意义的簿记来制作高度优化的机器代码位,例如,编写这些代码来优化分支(CPU往往更快地执行"不跳转"而不是"跳转",这是任何循环 struct 都要转换成的.到目前为止,JVM知道哪条‘路径’更常用,并相应地进行优化.只有编译时的优化器在没有提示的情况下无法做到这一点).

这就是为什么您不能仅仅依靠计算两次nanTime()调用之间的增量并提取有关计时的有意义的结论的大约900个原因之一.

JMH解决了一些问题.不是全部,但大部分都是.遵循上面链接的教程,将您的基准测试挂在JMH线束中,然后then判断您的结果.

Java相关问答推荐

如何从片段请求数据到活动?在主要活动中单击按钮请求数据?

JPackage-results已安装-如何添加系统属性?

如何使用解析器组合子解析Java数组类型签名?

如何转换Tue Feb 27 2024 16:35:30 GMT +0800 String至ZonedDateTime类型""

如何粘合(合并)文件Lucene?

AlarmManager没有在正确的时间发送alert

@org.springframework.beans.factory.annotation.Autowired(required=true)-注入点有以下注释:-SpringBoot

对运行在GraalVM-21上的JavaFX应用程序使用分代ZGC会警告不支持JVMCI,为什么?

相同的Java SerializedLambda为implMethodKind返回不同的结果

Mac上的全屏截图在使用JavaFX时不能正常工作吗?

如何对多个字段进行分组和排序?

在向WebSphere中的文档添加元素时iText挂起

在添加AdMob时无法为Google Play构建应用程序包:JVM垃圾收集器崩溃和JVM内存耗尽

在Frege中,我如何将一个字符串安全地转换为一个可能的Int?

从泛型枚举创建EnumMap

AWS Java SDK v2.x中没有setObjectAcl方法

如何使用jOOQ在PostgreSQL中从枚举类型生成Java枚举

spring 更新多项管理关系

始终使用Spring Boot连接mongodb上的测试数据库

在外部类和内部类之间,当调用外部类内部或外部的主方法时,它们的静态初始化程序的运行顺序不同