Java中的LinkedHashMap

LinkedHashMap继承自HashMap,并且实现了Map接口

LinkedHashMap在继承了HashMap的底层数据结构的同时,维护了一个双向链表,保证了数据插入或访问顺序,是有序的哈希表

特点

  1. 元素是有序的:维护了双向链表,默认是按照插入顺序(也可以实现访问顺序:每次访问节点就将当前节点移动到链表尾端)
  2. 元素的键不能重复,值可以重复
  3. 元素的键和值都支持null值
  4. hash冲突:根据key的hash值和数组长度位运算得到索引位,若此位置已经有值,则形成链表存储在此索引位(一个索引位为一个桶,桶内是链表或红黑树)
  5. 数组扩容:当数组元素个数大于数组容量的75%的时候,数组会进行扩容,扩容到原来的2倍
  6. 链表转红黑树:当链表长度大于8并且数组长度大于64时,链表转化为红黑树;当红黑树的节点个数减少到6时,会退化为链表
  7. 非线程安全:不支持多线程操作

底层原理

  • LinkedHashMap是基于数组+链表+红黑树+双向链表的数据结构实现的
  • 数组的默认初始化容量为16,比例因子为0.75,当数组元素个数大于16*0.75(临界值),数组会进行扩容,并且将旧数组中的数组拷贝到新数组中
  • 当插入的元素出现hash冲突(计算出的索引位已经有值),则会在当前索引位形成链表存储数据
  • 当链表长度大于8但是数组长度小于64的时候,数组会进行扩容,并将原数组中的链表拆分成2个链表存储在不同位置的桶中(索引位)
  • 当链表长度大于8但是数组长度大于64的时候,链表会转化为红黑树
  • 维护双向链表,确认元素插入或者访问的顺序。在解决hash冲突时调整链表或者红黑树,双向链表不需要调整

迭代器直接遍历双向链表LinkedHashMap 的 entrySet()keySet()values() 遍历时间复杂度为 O(n),且顺序确定

方法

构造方法
public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
    
    //双向链表头节点
    transient LinkedHashMap.Entry<K,V> head;

    //双向链表尾节点
    transient LinkedHashMap.Entry<K,V> tail;

    //指定链表顺序:true 按照访问节点顺序;false 按照插入顺序
    final boolean accessOrder;

   public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

    //创建一个LinkedHashMap,手动设置初始化容量,默认比例因子0.75
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    //创建一个LinkedHashMap,默认初始化容量为16,比例因子为0.75
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

    //创建一个LinkedHashMap,将传入集合中的元素按照元素顺序复制到LinkedHashMap
    //(添加元素时维护了双向链表)
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    //创建一个LinkedHashMap,手动设置初始化容量,比例因子,元素顺序(按照插入还是访问顺序)
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
}
添加单个元素

实际使用的是HashMap中的put方法,LinkedHashMap只是重写了创建节点的方法(此中维护双向链表)

    //hashmap中的put方法
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            //调用LinkedHashMap中的方法创建节点
            tab[i] = newNode(hash, key, value, null);
        else {
           ......
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

    //创建新节点(LinkedHashMap.Entry继承自HashMap.Node,额外维护了双向链表的前后指针)
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<>(hash, key, value, e);
        //建立新节点的双向链表关系(前后指针)
        linkNodeLast(p);
        return p;
    }

    //在双向列表尾部添加节点
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }
创建LinkedHashMap节点
    //创建节点后维护节点在双向链表中的位置
    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        TreeNode<K,V> p = new TreeNode<>(hash, key, value, next);
        linkNodeLast(p);
        return p;
    }

    //创建节点后维护节点在双向链表中的位置
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }
添加多个元素

实际调用HashMap中的putAll方法,LinkedHashMap只是重写了创建节点的方法

    //实际调用hashmap中的方法
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }

    //hashmap中的方法
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        ......
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

    //hashmap中的方法
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
           ......
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
删除单个元素

实际调用hashmap中的删除方法,在删除完成后,调用LinkedHashMap中的afterNodeRemoval方法解绑双向链表

    //hashmap中的删除方法
    public V remove(Object key) {
        Node<K,V> e;
        return (e = removeNode(hash(key), key, null, false, true)) == null ?
            null : e.value;
    }

    //在removeNode方法中,删除完节点后,调用afterNodeRemoval解绑双向链表
    void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        //在双向链表中,将已删除的节点中的前后指针置为null
        p.before = p.after = null;
        //如果e节点的前一个节点(b)为null,那么双向链表的头节点就是a
        if (b == null)
            head = a;
        //如果e节点的前一个节点(b)不为null,则b节点的后一个节点置为a
        else
            b.after = a;
        //如果e的后一个节点(a)是null,那么双向链表的尾节点就是b
        if (a == null)
            tail = b;
        //如果e的后一个节点(a)不为null,则a的前一个节点置为b
        else
            a.before = b;
    }
获取单个元素

如果设置了集合顺序为访问顺序,则获取到元素后要重新维护元素在双向列表中的位置(移动到链表的尾端)

    //调用hashmap中的getNode方法获取元素,
    //如果LinkedHashMap设置的顺序是按照访问顺序,
    //则调用afterNodeAccess重新维护元素在双向链表中的位置
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

    
    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        //首先将尾节点tail赋值给last,p指向e节点
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            //e节点的后一个节点置为null
            p.after = null;
            //如果e节点的前一个节点(b)为null,则e节点的后一个节点(a)为头节点
            if (b == null)
                head = a;
            //如果e节点的前一个节点(b)不为null,则b的后一个节点是a
            else
                b.after = a;
            //如果e的后一个节点(a)不为null,则a的前一个节点是b
            if (a != null)
                a.before = b;
            //如果e的后一个节点(a)为null,则尾节点是b
            else
                last = b;
            //最终确认:last节点(也就是尾节点)是null,那么头节点就是e
            if (last == null)
                head = p;
            //last不为null,那么e的前一个节点是last(尾节点);last的后一个节点就是p
            else {
                p.before = last;
                last.after = p;
            }
            //双向链表尾部指针指向e
            tail = p;
            ++modCount;
        }
    }
迭代器
    abstract class LinkedHashIterator {
        LinkedHashMap.Entry<K,V> next;
        LinkedHashMap.Entry<K,V> current;
        int expectedModCount;

        //创建迭代器时,直接将双向链表的head放入迭代器的next
        LinkedHashIterator() {
            next = head;
            expectedModCount = modCount;
            current = null;
        }

        public final boolean hasNext() {
            return next != null;
        }

        //从head开始迭代遍历双向列表
        final LinkedHashMap.Entry<K,V> nextNode() {
            LinkedHashMap.Entry<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            current = e;
            next = e.after;
            return e;
        }

        public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            removeNode(p.hash, p.key, null, false, false);
            expectedModCount = modCount;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值