# Java `LinkedHashMap`类

> 原文： [https://howtodoinjava.com/java/collections/linkedhashmap/](https://howtodoinjava.com/java/collections/linkedhashmap/)

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

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

```java
Table of Contents

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

## 1\. `LinkedHashMap`层次结构

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

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

```

## 2\. `LinkedHashMap`特性

有关 Java `LinkedHashMap`类的重要知识是：

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

#### 2.1 按插入顺序排序的`LinkedHashMap`

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

```java
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);
});

```

程序输出。

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

```

#### 2.2 按访问顺序排序的`LinkedHashMap`

在访问顺序映射中，键是根据上次使用`LinkedHashMap`的任何方法对其进行访问时的访问顺序排序的。 调用`put`，`putIfAbsent`，`get`，`getOrDefault`，`compute`，`computeIfAbsent`，`computeIfPresent`或`merge`方法会导致对相应条目的访问。

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

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

```java
//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);
});     

```

程序输出：

```java
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`方法的用法。

```java
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   
    }
}

```

程序输出：

```java
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`的性能

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

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

## 7\. `LinkedHashMap`中的并发

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

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

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

```

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

## 8\. 总结

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

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

学习愉快！

参考：

[`LinkedHashMap` Java 文档](https://docs.oracle.com/javase/10/docs/api/java/util/LinkedHashMap.html)