微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么使用相同的HashMap迭代器读取密钥时,Java HashMap获取(密钥)的速度比使用Set的迭代器读取密钥要快?

对于HashMap< Integer,Integer>,在插入10000000个唯一随机值后.我使用hashmap的keySet()执行get(),如下面的代码片段所示:

HashMap<Integer,Integer> hashmap = 
                        new HashMap<Integer,Integer>(10000000,0.99f);

// ... Code to put unique 10000000 associations into the hashmap ...

int iteration = 100;
long startTime,totalTime = 0;

while(iteration > 0) {
    for(Integer key: hashmap.keySet()) {
       startTime = System.currentTimeMillis();
       hashmap.get(key);
       totalTime += (System.currentTimeMillis() - startTime);
    }
    iteration--;
}
System.out.println(totalTime/100 + " ms");

运行上面的代码,我得到:225毫秒

现在,如果我将上面的代码改为使用set,就像在下面的代码片段中一样:

Set<Integer> set = new HashSet<Integer>(hashmap.keySet());
while(iteration > 0) {
    for(Integer key: set) {
       startTime = System.currentTimeMillis();
       hashmap.get(key);
       totalTime += (System.currentTimeMillis() - startTime);
    }
    iteration--;
}
System.out.println(totalTime/100 + " ms");

运行此代码后,我得到:414毫秒

为什么这种性能差异?

P.S.:我使用了以下JVM参数:

-xms2048m -Xmx4096m -XX:MaxPermSize=256m

解决方法

当您读取大型数据结构(大于32 KB)时,您如何阅读该数据结构会影响性能.

这些是您缓存的典型大小和速度.

L1:   32 KB,4 clock cycles.
L2:  256 KB,11 clock cycles.
L3: 3-30 MB,40-75 clock cycles.
Main memory: up to 2TB,200-500 clock cycles.

这意味着缓存局部性非常重要.也就是说,如果你正在读取L1中的某些东西,那么它比从L3读取的速度快20倍.

在您的情况下,您正在使用哈希数据结构.这是为随机访问和随机排列而设计的,遗憾的是它具有非常差的可缓存性.随机访问内存,它可能在较慢的内存区域.

但是,这是一个例外.如果您多次访问相同的数据,例如从迭代器中获取一个键,或者按顺序扫描一个集合,例如这就是迭代器对HashMap(而不是TreeMap)所做的事情,你将访问的下一条数据更可能是在同一个缓存行(每个缓存行长度为64字节)或下一行.这些类型的访问执行得更好,因为cpu被设计为非常快速地执行向量操作.

BTW你的工作集就是一组键,如果你的值是不同的对象,我希望你实际看这些对象时会慢得多(因为这会增加工作集的大小以及缓存需要多少内存)它)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐