为啥创建 HashMap 比创建 Object[] 更快?
Posted
技术标签:
【中文标题】为啥创建 HashMap 比创建 Object[] 更快?【英文标题】:Why is creating a HashMap faster than creating an Object[]?为什么创建 HashMap 比创建 Object[] 更快? 【发布时间】:2015-10-03 19:44:16 【问题描述】:我尝试构建自己的 Map 以提高特殊环境的性能,并且我意识到一些非常有趣的事情:创建 new Hashmap<Integer,String>(2000)
比 new Object[2000]
快 - 无论我以什么顺序执行这些命令。这让我很困惑,尤其是。因为Hashmap构造函数包含table = new Entry[capacity]
,根据this。我的测试台有问题吗?
public static void test(int amm) //amm=1_000_000
Map<Integer,String> m1 = null;
Object[] arr = null;
long time = System.nanoTime();
for(int i = 0; i < amm; i++)
m1 = new HashMap<Integer, String>(2000);
System.out.println("m1: " + (System.nanoTime() - time)); //m1: 70_455_065
time = System.nanoTime();
for(int i = 0; i < amm; i++)
arr = new Object[2000];
System.out.println("arr: " + (System.nanoTime() - time)); //arr: 1_322_473_803
我很想看看在另一台计算机上的测试结果。我不知道为什么创建 HashMap
比创建 Object[]
快 10 倍。
【问题讨论】:
这取决于JDK的实现。您使用哪个 JDK 和版本来执行此测试? @BladeCoder 我根据 java.version 使用 1.7.0_79 (IceTea 2.5.5) (7u79-2.5.5-0ubuntu0.14.04.2)。 确实在 OpenJDK 1.7 的封闭版本中,HashMap 的构造函数中没有初始化数组(它在第一次插入时完成),这解释了速度差距:grepcode.com/file_/repository.grepcode.com/java/root/jdk/… 【参考方案1】:如果你看HashMap
的实现,构造函数看起来像:
public HashMap(int initialCapacity, float loadFactor)
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
threshold = initialCapacity;
init();
而init()
看起来像:
/**
* Initialization hook for subclasses. This method is called
* in all constructors and pseudo-constructors (clone, readObject)
* after HashMap has been initialized but before any entries have
* been inserted. (In the absence of this method, readObject would
* require explicit knowledge of subclasses.)
*/
void init()
所以initialCapacity
实际上并不习惯于创建数组。它在哪里使用?查看put()
方法。
public V put(K key, V value)
if (table == EMPTY_TABLE)
inflateTable(threshold);
// hidden
在执行 put 时,实际上创建了数组。我没有显示inflateTable()
,但它做了一些数学运算并初始化了数组。
【讨论】:
你的源代码来自哪里?我找到了docjar.com/html/api/java/util/HashMap.java.html - 有什么不同。不过,我会尝试一下 :) 编辑:你是对的,添加 put(5,"Hello") 可以解决问题。有趣,你 @alexberne 我使用了 intellij 的视图源,在 MacOS 上是JDK_1.7.0_51
有趣...因此,如果您有一个程序在加载时初始化一个大 HashMap 并在用户单击按钮时将元素放入其中,那么 OpenJDK 实现将显示不同的性能。 -- 在 OpenJDK 上,应用程序将需要很长时间才能加载,并且在用户交互时会很快在 OracleJDK 上,应用程序将在第一次按钮单击时快速加载并且需要很长时间。 -- OracleJDK 解决方案的好处显然是能够创建大量未使用的 HashMap 而不会占用太多内存
@Falco:我怀疑您是否会注意到在单击按钮时创建大小为 2000 的对象数组对性能的影响。
@Falco,不,此代码来自OpenJDK,而不是某些 Oracle 专有补丁。 Oracle Java 非常接近 OpenJDK,只是并非所有组件都已打开(WebStart、JMC、HotSpot for ARM 等)。【参考方案2】:
一个空的 HashMap
对象比一个包含 2000 个 Object
引用的数组小得多。即使您将 2000 传递给 HashMap
构造函数的 initialCapacity
参数,它实际上还没有为对象创建 2000 个空间。
【讨论】:
以上是关于为啥创建 HashMap 比创建 Object[] 更快?的主要内容,如果未能解决你的问题,请参考以下文章
对象比相应的 .RData 文件大得多。为啥?可以手动做吗?
为啥 Android Studio 创建的 APK 比 Eclipse 创建的 APK 大?
[笔记]为啥hashmap查询速度快? 如何理解hashmap的散列?