【问题标题】:Best way to initialize a HashMap初始化 HashMap 的最佳方法
【发布时间】:2011-11-24 13:45:32
【问题描述】:

我通常会这样做

HashMap<String,String> dictionary = new HashMap<String,String>();

我开始考虑它,据我所知,HashMap 是通过哈希表在后台实现的。
使用散列将对象存储在表中以查找它们应存储在表中的位置。

我没有对dictionary 的构造设置大小这一事实是否会降低性能?
IE。构建期间哈希表的大小是多少?随着元素的增加,是否需要为表分配新的内存?
还是我对这里的概念感到困惑?
默认容量和负载是否足够,或者我应该花时间了解实际数字吗?

【问题讨论】:

  • 不是基于java.util.Hashtable(同步的),而是基于哈希表。
  • @EJP:不要相信所有书面阅读,但read the source。 Java 的HashMap 是一个非常简单的链式哈希表实现,看不到树。
  • @EJP,链式桶的结构是一棵树,而不是二叉树、b-tree 或 spline,但仍然是一棵树。至于抱怨:你在 put 上有分配,在 get 上有更差的局部性(间接,即缓存未命中),在碰撞上有额外的间接。例如(只是快速谷歌)Zukowski 等人的一项研究 [4]已经表明,对于现代处理器上的小型缓存驻留哈希表,布谷鸟哈希比链式哈希要快得多。 en.wikipedia.org/wiki/… Impl。 IdentityHashMap的就是java中通用的HT应该是什么。内存占用少,速度快。
  • @bestsss:将列表称为树在病态意义上是正确的,并且非常令人困惑。将列表列表称为树也是如此。 OTOH,我可以想象 HashMap 不是非常快或内存效率不是很高,因为它确实做了很多分配。
  • @larsmans 这不是一个列表,它是一个链表数组。这是我记得很多年前我读到的关于哈希表的第一个名称(基于树的)。 Knuth 确实将其称为链式。如今(10 多年)硬件线性探测器的 2 次幂可能会产生最佳性能和内存占用。 (可能与素数长度表相媲美,但它需要 mod(~30 个时钟)而不是右移(1 个时钟)来获得索引)

标签: java optimization data-structures hashmap hashtable


【解决方案1】:

我没有在字典的构造上设置大小这一事实是否会导致性能下降?

取决于您要在HashMap 中存储多少以及您的代码之后将如何使用它。如果你可以预先给它一个大概的数字,它可能会更快,但是:“如果迭代性能很重要,不要将初始容量设置得太高 [...]”1 因为迭代时间与容量。

在非性能关键的代码片段中执行此操作将被视为过早优化。如果您要超越 JDK 作者,请确保您的测量结果表明您的优化很重要。

在构建过程中哈希表的大小是多少?

根据API docs,16.

随着元素的增加,是否需要为表格分配新的内存?

是的。每次它比负载因子(默认 = .75)更满时,它都会重新分配。

默认容量和负载是否足够

只有你自己知道。分析您的程序以查看它是否在 HashMap.put 中花费了太多时间。如果不是,请不要打扰。

【讨论】:

    【解决方案2】:

    如果需要,Hashmap 会自动增加大小。初始化的最佳方法是,如果您有某种预期可能需要多少元素,并且如果数字很大,只需将其设置为不需要不断调整大小的数字。此外,如果您阅读 Hashmap 的 JavaDoc,您会看到默认大小为 16,加载因子为 0.75,这意味着一旦哈希图已满 75%,它将自动调整大小。因此,如果您希望容纳 100 万个元素,那么您自然需要比默认尺寸更大的尺寸

    【讨论】:

      【解决方案3】:

      here 所述,默认初始容量为 16,默认负载因子为 0.75。您可以使用不同的 c'tors 更改其中任何一个,这取决于您的使用情况(尽管这些通常适用于一般用途)。

      【讨论】:

        【解决方案4】:

        我首先将它声明为接口 Map。

        Map<String,String> dictionary = new HashMap<String,String>();
        

        我没有在结构上设置大小的事实吗? 字典使性能下降?

        是的,应设置初始容量以获得更好的性能。

        是否需要为表分配新内存作为元素 增加

        是的,负载系数也会影响性能。

        更多详情见docs

        【讨论】:

          【解决方案5】:

          Java 的好处在于它是开源的,因此您可以调用source code,它回答了许多问题:

          1. 不,HashMapHashTable 之间没有关系。 HashMap 派生自 AbstractMap,并且在内部不使用 HashTable 来管理数据。

          2. 忽略显式大小是否会降低性能取决于您的使用模型(或者更具体地说,您在地图中放入了多少东西)。每次达到某个阈值(0.75 * &lt;current map capacity&gt;)时,地图会自动加倍大小,加倍操作代价高昂。因此,如果您知道大约有多少元素将进入地图,您可以指定一个大小并防止它需要分配额外的空间。

          3. 如果没有使用构造函数指定地图的默认容量,则为 16。因此,当第 12 个元素添加到地图时,地图的容量将翻倍至 32。然后在 24 日再次,以此类推。

          4. 是的,容量增加时需要分配新的内存。而且这是一项相当昂贵的操作(参见resize()transfer() 函数)。

          与您的问题无关,但仍然值得注意,我建议您声明/实例化您的地图,例如:

          Map<String,String> dictionary = new HashMap<String,String>();
          

          ...当然,如果您碰巧知道要在地图中放置多少元素,您也应该指定它。

          【讨论】:

          • java 7 风格,不用在表达式右侧指定泛型参数就更好了。
          • 。当然,如果您碰巧知道要在地图中放置多少元素,您也应该指定它。 除以负载因子并四舍五入到下一个 pow-2。如果您指定 new HashMap(7) 并放置 7 个元素,您将首先初始化为 8,然后增长(重新散列)到 16。
          猜你喜欢
          • 2015-12-02
          • 2018-03-09
          • 1970-01-01
          • 1970-01-01
          • 2015-07-16
          • 2010-09-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多