zoukankan      html  css  js  c++  java
  • 集合类框架之--Map

    java集合框架---Map体系

    HashMap层次体系:

    1.实现了Cloneable,可以被克隆
    2.实现了Serializable,可以被序列化
    3.继承AbstractMap,实现了Map接口,具有Map的所有功能。
     

    HashMap的底层结构为 数组+链表或红黑树,当链表的数量达到一定数量的时候,会转换成红黑树。红黄树的节点树remove到一定数量的时候,会重新转换成链表。

    数组的查询效率为O(1),链表的查询效率是O(k),红黑树的查询效率是O(log n),n为一个桶中的元素个数,当元素数量非常多的时候,转化为红黑树能极大地提高效率。

     

    put(K key, V value)方法

    public V put(K key, V value) {
        // 调用hash(key)计算出key的hash值(hash发散)
        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;
        // 如果table数量为0,初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            //使用resize()进行初始化
            n = (tab = resize()).length;
        // 如果桶没有元素
        if ((p = tab[i = (n - 1) & hash]) == null)
            // 新建一个Node放在第一个位置
            tab[i] = newNode(hash, key, value, null);
        else {
            // 桶中已经有元素了
            Node<K,V> e; K k;
            // 如果桶中第一个元素的key与待插入元素的key相同,保存到e中用于后续修改value值
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 如果第一个元素是树节点,则调用树节点的putTreeVal插入元素
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                // 遍历这个桶对应的链表,binCount用于存储链表中元素的个数
                for (int binCount = 0; ; ++binCount) {
                    // 如果链表遍历完了都没有找到相同key的元素,说明该key对应的元素不存在,则在链表最后插入一个新节点
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 如果插入新节点后链表长度大于8,则判断是否需要树化,因为第一个元素没有加到binCount中,所以这里-1
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    // 如果待插入的key在链表中找到了,则退出循环
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            // 如果找到了对应key的元素
            if (e != null) { // existing mapping for key
                // 记录下旧值
                V oldValue = e.value;
                // 判断是否需要替换旧值
                if (!onlyIfAbsent || oldValue == null)
                    // 替换旧值为新值
                    e.value = value;
                afterNodeAccess(e);
                // 返回旧值
                return oldValue;
            }
        }
    
        // 到这里了说明没有找到元素,修改次数加1
        ++modCount;
        // 元素数量加1,判断是否需要扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        // 没找到元素返回null
        return null;
    }

    HashMap的 put方法主要逻辑:

    1.Node[] 数组未初始化,则初始化

    2.对key求hash值,计算下标

    3.如果没有碰撞,直接放桶里

    4.如果碰撞了,判断链表中存在该元素,存在则替换,不存在则插到链表的后面

    5.如果链表的长度大于8,则转换成红黑树

    6.判断桶是否已经满了,满了则需要扩容

    ConcurrentHashMap层次体系:

    ConcurrentHashMap的put方法主要逻辑:

    1.判断Node[]是否初始化,没有初始化则初始化

    2.通过Hash定位数组的索引坐标,是否有节点存在,

    没有则使用CAS进行节点的添加(链表头节点),失败进入下一层循环

    3.检查内部是否正在进行扩容,如果正在扩容,协助扩容(。。。)

    4. if var7 != null  ,则使用synchronized锁住元素

       a.如果是链表,添加

          b.如果是红黑树,添加

    5.判断链表的长度 >=8 ,超过临界值,就把链表转换成红黑树 

    总结:

    HashMap:线程不安全,数组+链表,红黑树

    HashTable:锁住整个对象,线程安全,数组+链表

    ConcurrentHashMap:CAS判断对应的坐标是否有节点,有节点,用同步锁锁住,数组+链表+红黑树

  • 相关阅读:
    vue系列---响应式原理实现及Observer源码解析(七)
    学习Lowdb小型本地JSON数据库
    渐进式web应用开发---Service Worker 与页面通信(七)
    webpack4核心模块tapable源码解析
    electron 创建托盘应用
    运维堡垒机开发
    使用Supervisord软件管理go服务进程
    安装Harbor之http版本
    Ubuntu 18 LTS netplan 网络配置
    用GO开发企业级分布式云存储系统
  • 原文地址:https://www.cnblogs.com/Jemb/p/11623068.html
Copyright © 2011-2022 走看看