java基础进阶篇(七)_LinkedHashMap------【java源码栈】


目录
  • 一.概述
  • 二.特点
  • 三.应用场合
  • 四.构造方法
    • 1.参数为空
    • 2.accessOrder
  • 五.源码结构分析
  • 六.常见问题
    • 1.如何实现的元素有序?
    • 2.如何保证顺序的正确以及同步
    • 3.如何实现两种顺序(插入顺序或者访问顺序)?
    • 4.为什么重写containsValue()而不重写containsKey()?
  • 七.常用方法

一.概述

??LinkedHashMap是HashMap的子类,关于HashMap可以看下前面的章节:

public class LinkedHashMap
    extends HashMap
    implements Map

二.特点

  • 非线程安全
  • LinkedHashMap 内部保证顺序; 分插入顺序和访问排序两种, 如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。 HashMap不保证插入顺序.
  • LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
  • LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。
  • LinkedHashMap的插入顺序和访问顺序可以由开发者自己决定.

三.应用场合

??HashMap是无序的,当我们希望有顺序(保证插入顺序)地去存储key-value时,就需要使用LinkedHashMap了.

使用HashMap:

Map hashMap = new HashMap();
hashMap.put(1, "a1");
hashMap.put(3, "a3");
hashMap.put(2, "a2");
hashMap.put(0, "a0");
Set> set = hashMap.entrySet();
Iterator> iterator = set.iterator();
while (iterator.hasNext()) {
    Entry entry = iterator.next();
    Integer key = (Integer) entry.getKey();
    String value = (String) entry.getValue();
    System.out.println("key:" + key + ",value:" + value);
}

输出结果:

key:0,value:a0
key:1,value:a1
key:2,value:a2
key:3,value:a3

??HashMap 给key做了排序, 不能保证插入顺序, 当有插入顺序的需求时, 就轮到LinkedHashMap 登场了.

使用LinkedHashMap:

Map hashMap = new LinkedHashMap();
hashMap.put(1, "a1");
hashMap.put(3, "a3");
hashMap.put(2, "a2");
hashMap.put(0, "a0");
Set> set = hashMap.entrySet();
Iterator> iterator = set.iterator();
while (iterator.hasNext()) {
    Entry entry = iterator.next();
    Integer key = (Integer) entry.getKey();
    String value = (String) entry.getValue();
    System.out.println("key:" + key + ",value:" + value);
}

??输出结果:

key:1,value:a1
key:3,value:a3
key:2,value:a2
key:0,value:a0

??完美保证了插入时的顺序.

四.构造方法

??LinkedHashMap继承了HashMap,所以它们有很多相似的地方。
??在这儿我们只看参数为空 和修改排序模式的两种构造方法.

1.参数为空

/**
 * Constructs an empty insertion-ordered LinkedHashMap instance
 * with the default initial capacity (16) and load factor (0.75).
 */
public LinkedHashMap() {
    super();
    accessOrder = false;
}

??默认初始容量是16, 负载因子是0.75. 这里调用了super() 即HashMap的构造方法.
accessOrder设置为false,表示不是访问顺序而是插入顺序存储的,这也是默认值,表示LinkedHashMap中存储的顺序是按照调用put方法插入的顺序进行排序的。LinkedHashMap也提供了可以设置accessOrder的构造方法.

2.accessOrder

public LinkedHashMap(int initialCapacity,
                     float loadFactor,
                     boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

??通过例子来看下参数accessOrder 在插入顺序访问顺序上的作用.
true的情况:

Map hashMap = new LinkedHashMap(16,0.75f,true);
hashMap.put(1, "a1");
hashMap.put(3, "a3");
hashMap.put(2, "a2");
hashMap.put(0, "a0");
Set> set = hashMap.entrySet();
Iterator> iterator = set.iterator();
while (iterator.hasNext()) {
    Entry entry = iterator.next();
    Integer key = (Integer) entry.getKey();
    String value = (String) entry.getValue();
    System.out.println("key:" + key + ",value:" + value);
}

??输出结果:

key:1,value:a1
key:3,value:a3
key:2,value:a2
key:0,value:a0

??accessOrder 等于true: 保证了插入顺序. 前面的例子,默认为false的情况, 保证了访问顺序即对key进行了排序.

五.源码结构分析

??LinkedHashMap 实现了Map接口。其内部还维护了一个双向链表.
??默认情况,遍历时的顺序是按照插入节点的顺序。这也是其与HashMap最大的区别。
??也可以在构造时传入accessOrder参数,使得其遍历顺序按照访问的顺序输出。
??LinkedHashMap的实现主要分两部分,一部分是哈希表,另外一部分是链表。

??可以理解为带链表的数组, 哈希表是由数组+链表组成的, 长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢? 一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

??HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。

??首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

??下面是HashMap 的结构, 也可以理解成单向链表结构,(个人自定义的名称, 便于理解勿喷)

??而LinkedHashMap 的双向链表结构如下:(作者自涂鸦)

??红色箭头表示节点Entry 之间是双向的, 回到主题, 为什么LinkedHashMap不重写containsKey()?
??比如我们拥有 1 处 的key 和value, 从1 -> 2, 需要经历3个步骤
??1.把key 转换成hash 值
??2.把hash值转换成索引,方便在主干查找到对应的数组下标.(描述不准确,是这个意思)
??3.没完呢, 还得根据key 或者是转化后的hash去查找所在下标处的Entry.

三个步骤等于回到了ArrayList 的原始数组时代.
??再次重申,便于理解,高手请指教勿喷.

七.常用方法

??常用方法请参照前面的章节: [ java基础进阶篇(七)_LinkedHashMap------【java源码栈】][]