【问题标题】:How is the implementation of LinkedHashMap different from HashMap?LinkedHashMap 的实现与 HashMap 有何不同?
【发布时间】:2011-03-02 12:37:53
【问题描述】:

如果 LinkedHashMap 的时间复杂度和 HashMap 的复杂度一样,为什么我们需要 HashMap?与 Java 中的 HashMap 相比,LinkedHashMap 的额外开销是多少?

【问题讨论】:

    标签: java hashmap complexity-theory linkedhashmap


    【解决方案1】:

    如果 LinkedHashMap 的时间复杂度和 HashMap 的复杂度一样,为什么还需要 HashMap?

    您不应将复杂性与性能混为一谈。两种算法可以具有相同的复杂性,但其中一种算法的性能始终优于另一种。

    记住f(N) is O(N)的意思是:

    limit(f(N), N -> infinity) <= C*N  
    

    其中C 是一个常数。复杂性并没有说明C 值的大小。对于两种不同的算法,常量C 很可能不同

    (请记住,大 O 复杂性与 N 变得非常大时的行为/性能有关。它不会告诉您较小的 N 值的行为/性能。)


    话虽如此:

    • 在等效用例中HashMapLinkedHashMap 操作之间的性能差异相对较小。

    • LinkedHashMap 使用更多内存。例如,Java 11 实现在每个映射条目中有两个额外的引用字段来表示之前/之后列表。在没有压缩 OOP 的 64 位平台上,额外开销是每个条目 16 个字节。

    • 相对较小的性能和/或内存使用差异实际上对使用性能或内存至关重要的应用程序的人来说非常重要1

    1 - ...以及那些不必要地痴迷于这些事情的人。

    【讨论】:

      【解决方案2】:

      LinkedHashMap 在您需要知道 Map 中键的插入顺序时是一种有用的数据结构。一种合适的用例是实现 LRU 缓存。由于 LinkedHashMap 的顺序维护,数据结构比 HashMap 需要额外的内存。如果不需要插入顺序,则应始终使用 HashMap。

      【讨论】:

        【解决方案3】:

        HashMap 不维护插入顺序,因此不维护任何双向链表。

        LinkedHashMap 最显着的特点是它维护了键值对的插入顺序。 LinkedHashMap 使用双向链表。

        LinkedHashMap 的条目如下所示:

          static class Entry<K, V> {
             K key;
             V value;
             Entry<K,V> next;
             Entry<K,V> before, after;        //For maintaining insertion order    
             public Entry(K key, V value, Entry<K,V> next){
                 this.key = key;
                 this.value = value;
                 this.next = next;
             }
          }
        

        通过使用 before 和 after - 我们跟踪 LinkedHashMap 中新添加的条目,这有助于我们维护插入顺序。

        在参考之前的条目和 after 指的是LinkedHashMap 中的下一个条目。

        图表及分步说明请参考http://www.javamadesoeasy.com/2015/02/linkedhashmap-custom-implementation.html

        【讨论】:

          【解决方案4】:

          HashMap 和 LinkedHashMap 的另一个主要区别是:LinkedHashMap 的迭代效率更高。

          由于 LinkedHashMap 中的元素相互连接,因此迭代所需的时间与地图的大小成正比,而不管其容量如何。 但在 HashMap 的情况下;由于没有固定的顺序,因此对其进行迭代需要与其容量成正比的时间。

          我已在我的blog 上提供了更多详细信息。

          【讨论】:

            【解决方案5】:

            LinkedHashMap 继承了 HashMap,这意味着它使用 HashMap 的现有实现将键和值存储在节点(条目对象)中。除此之外,它还存储了一个单独的双向链表实现,以维护输入键的插入顺序。

            看起来像这样:

            头节点节点1 节点2 节点3 节点4 头节点。

            所以额外的重载是在这个双向链表中维护插入和删除。 好处是:保证迭代顺序是插入顺序,在HashMap中是没有的。

            【讨论】:

              【解决方案6】:
              • 重新调整大小应该更快,因为它会迭代 双链表将内容传输到新的表格数组中。
              • containsValue() 被重写以利用更快的 迭代器。
              • LinkedHashMap 也可用于创建 LRU 缓存。一个特别的 LinkedHashMap(容量, loadFactor, accessOrderBoolean) 构造函数 用于创建一个链接的哈希映射,其迭代顺序为 最后访问其条目的顺序,从 最近最少访问到最近访问。在这种情况下,仅 使用 get() 查询地图是一种结构修改。

              【讨论】:

                【解决方案7】:
                • LinkedHashMap 还维护一个双向链表,贯穿其所有条目,这将提供可重现的顺序。这个链表定义了迭代顺序,通常是键插入映射的顺序(插入顺序)。
                • HashMap 没有这些额外成本(运行时间、空间),当您不关心插入顺序时,应该优先使用 LinkedHashMap。

                【讨论】:

                • 我想你的意思是迭代顺序?
                • @MichaelDeardeuff 你是对的,但答案通常是正确的,因为iteration order = insertion order 是默认的。 插入顺序的可能替代方法是访问顺序
                【解决方案8】:

                LinkedHashMap 会占用更多内存。普通HashMap 中的每个条目都只有键和值。每个LinkedHashMap 条目都有这些引用 对下一个和前一个条目的引用。还有一点点家务要做,虽然这通常无关紧要。

                【讨论】:

                • 因此性能比HashMap略低。
                • @Jon Skeet 是否像 LinkedHashMap 的值包含对前一个值和下一个值的额外引用,删除或插入任何项目也需要额外的重新连接成本?如果我得到完整的实现,或者有链接或解释,那就太好了。
                • @Passionate 程序员:源代码是公开的——为什么不去那里看看呢?是的,插入和删除确实需要更新链表。
                • @我不是 Java 程序员,也不知道在哪里可以找到 :(.
                • @StinePike:嗯,在行为方面有所不同——但就查找、添加项目等所花费的时间而言,它们大致相当。 (就复杂性而言 - 常数因素可能略有不同。)