原文: https://howtodoinjava.com/java/collections/linkedhashmap/

Java 中的LinkedHashMap用于存储非常类似于HashMap类的键值对。 区别在于,LinkedHashMap会在HashMap无序的情况下保持插入其中的元素的顺序。

在本 Java 集合教程中,我们将学习LinkedHashMap类,其方法,用例和其他重要细节。

  1. Table of Contents
  2. 1\. LinkedHashMap Hierarchy
  3. 2\. LinkedHashMap Features
  4. 3\. LinkedHashMap Constructors
  5. 4\. LinkedHashMap Methods
  6. 5\. LinkedHashMap Usecases
  7. 6\. LinkedHashMap Performance
  8. 7\. Concurrency in LinkedHashMap
  9. 8\. Conclusion

1. LinkedHashMap层次结构

LinkedHashMap类在 Java 中声明如下。 它扩展了HashMap ,并且实现了Map 接口。 'K'是键的类型,'V'是键的映射值的类型。

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
    //implementation
}

2. LinkedHashMap特性

有关 Java LinkedHashMap类的重要知识是:

  • 它存储类似于HashMap的键值对。
  • 它仅包含唯一键。 不允许重复的键。
  • 它可能具有一个null键和多个null值。
  • 通过将元素添加到内部管理的双链表中,它可以保持插入的KV对的顺序。

2.1 按插入顺序排序的LinkedHashMap

默认情况下,LinkedHashMap是插入顺序的。 将元素添加到元素时,它会保持其顺序。 在LinkedHashMap上进行迭代时,我们会按添加KV对的确切顺序获得它们。

LinkedHashMap<Integer, String> pairs = new LinkedHashMap<>();

pairs.put(1,  "A");
pairs.put(2,  "B");
pairs.put(3,  "C");
pairs.put(4,  "D");

pairs.forEach((key, value) -> {
    System.out.println("Key:"+ key + ", Value:" + value);
});

程序输出。

Key:1, Value:A
Key:2, Value:B
Key:3, Value:C
Key:4, Value:D

2.2 按访问顺序排序的LinkedHashMap

在访问顺序映射中,键是根据上次使用LinkedHashMap的任何方法对其进行访问时的访问顺序排序的。 调用putputIfAbsentgetgetOrDefaultcomputecomputeIfAbsentcomputeIfPresentmerge方法会导致对相应条目的访问。

键按从最近访问最少到最近访问的顺序排序,并建立 LRU 缓存。

要创建访问顺序图,LinkedHashMap有一个特殊的构造器参数。 设置为true时,LinkedHashMap维护访问顺序。

//3rd parameter set access order
LinkedHashMap<Integer, String> pairs = new LinkedHashMap<>(2, .75f, true);

pairs.put(1,  "A");
pairs.put(2,  "B");
pairs.put(3,  "C");
pairs.put(4,  "D");

//Access 3rd pair
pairs.get(3);

//Access 1st pair
pairs.getOrDefault(2, "oops");

pairs.forEach((key, value) -> {
    System.out.println("Key:"+ key + ", Value:" + value);
});

程序输出:

Key:1, Value:A
Key:4, Value:D
Key:3, Value:C
Key:2, Value:B

注意输出,最近访问的条目如何到达序列末尾。

3. LinkedHashMap构造器

LinkedHashMap 具有五种构造器:

  1. LinkedHashMap():使用默认的初始容量(16)和负载因子(0.75)初始化默认的LinkedHashMap实现。
  2. LinkedHashMap(int capacity):使用指定的容量和负载因子(0.75)初始化LinkedHashMap
  3. LinkedHashMap(Map map):使用与指定映射相同的映射初始化LinkedHashMap
  4. LinkedHashMap(int capacity, float fillRatio):使用指定的初始容量和负载因子初始化LinkedHashMap
  5. LinkedHashMap(int Capacity, float fillRatio, boolean Order):初始化LinkedHashMap的容量和填充率以及是否维护插入顺序或访问顺序。

    • 'true'启用访问顺序。
    • 'false'启用插入顺序。 使用其他构造器时,这是默认值行为。

4. LinkedHashMap方法

我们应该学习有关LinkedHashMap的重要方法如下:

  1. void clear():它将从映射中删除所有键值对。
  2. void size():它返回此映射中存在的键/值对的数量。
  3. void isEmpty():如果此映射不包含键值映射,则返回true
  4. boolean containsKey(Object key):如果映射中存在指定的键,则返回'true'
  5. boolean containsValue(Object key):如果将指定值映射到映射中的至少一个键,则返回'true'
  6. Object get(Object key):检索由指定的key映射的value
  7. Object remove(Object key):如果存在,它将从映射中删除指定键的键值对。
  8. boolean removeEldestEntry(Map.Entry eldest):当映射从访问顺序映射中删除其最旧的条目时,它返回'true'

4.1 Java LinkedHashMap示例

Java 程序,用于演示linkedhashmap方法的用法。

import java.util.Iterator;
import java.util.LinkedHashMap;

public class LinkedHashMapExample 
{
    public static void main(String[] args) 
    {
        //3rd parameter set access order
        LinkedHashMap<Integer, String> pairs = new LinkedHashMap<>();

        pairs.put(1,  "A");
        pairs.put(2,  "B");
        pairs.put(3,  "C");

        String value = pairs.get(3);    //get method

        System.out.println(value);

        value = pairs.getOrDefault(5, "oops");  //getOrDefault method

        System.out.println(value);

        //Iteration example
        Iterator<Integer> iterator =  pairs.keySet().iterator();

        while(iterator.hasNext()) {
            Integer key = iterator.next();
            System.out.println("Key: " + key + ", Value: " + pairs.get(key));
        }

        //Remove example
        pairs.remove(3);
        System.out.println(pairs);

        System.out.println(pairs.containsKey(1));    //containsKey method   

        System.out.println(pairs.containsValue("B"));    //containsValue method   
    }
}

程序输出:

C
oops
Key: 1, Value: A
Key: 2, Value: B
Key: 3, Value: C
{1=A, 2=B}
true
true

5. LinkedHashMap用例

在几乎所有需要使用HashMap的情况下,我们都可以使用LinkedHashMap。 在特性方面,它可以非常透明地替换HashMap

另外,LinkedHashMap维护插入顺序,这在我们要维护添加到Map的对的顺序时非常有用。

有序访问权限的LinkedHashMap通过覆盖removeEldestEntry()方法来强加一个用于在将新映射添加到映射时自动删除陈旧的策略的方法,从而为创建 LRU 缓存特性提供了一个很好的起点。 这使您可以使用定义的某些条件使数据过期。

6. LinkedHashMap的性能

HashMapLinkedHashMap以恒定的时间执行添加,删除和包含的基本操作。 LinkedHashMap的性能比HashMap差一些,因为它必须维护一个双向链表,而HashMap仅维护链表。

另一方面,在LinkedHashMap情况下遍历Map的速度比HashMap略快,因为所需的时间仅与“大小”成比例。 对于HashMap,迭代性能与“大小+容量”成正比。

7. LinkedHashMap中的并发

HashMapLinkedHashMap也不是线程安全的,这意味着我们不能直接在多线程应用程序中使用它们以获得一致的结果。 我们应该使用Collections.synchronizedMap(Map map)方法显式地同步它们。

Map<Integer, Integer> numbers = Collections.synchronizedMap(new LinkedHashMap<>());

Map<Integer, Integer> numbers = Collections.synchronizedMap(new HashMap<>());

对于HashMap,更建议使用ConcurrentHashMap,因为它提供的并发程度更高。

8. 总结

基于以上所有信息,我们可以说,在大多数情况下,最好选择HashMap而不是LinkedHashMap。 仅当我们有某些要求或用例需要保持添加到映射的元素顺序时,我们才更喜欢LinkedHashMap

两者在大多数现实用例中都提供几乎相同的性能。 当我们拥有大量数据时,则仅应考虑它们之间的权衡取舍。

学习愉快!

参考:

LinkedHashMap Java 文档