美文网首页
HashMap源码走读

HashMap源码走读

作者: 我是光芒万丈 | 来源:发表于2022-05-30 10:16 被阅读0次

由于各个版本JDK间差异,本文主要基于JDK8源码来进行分析。

1. 构造方法

hashmap采用lazyload模式, 实例化时构造只会设置负载因子,与下次扩展容量具体源码如下:

    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        //设置负载因子                                       
        this.loadFactor = loadFactor;
        //设置下次扩容容量,tableSizeFor会返回2的幂
        this.threshold = tableSizeFor(initialCapacity);
    }

2. put方法

会在放置数据时会进行扩容,由于put方法主要是调用putVal方法,因此,此处重点分析下putVal方法。

 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的长度
        if ((tab = table) == null || (n = tab.length) == 0)
            //初始化扩容 resize重新获取扩容长度:出事状态直接给定16,并实例化数组,超过负载因子即已用超过总容量容量的0.75则扩展一倍,并移动
            n = (tab = resize()).length;
            // n为数组长度,(n - 1) & hash是为了将数组取值控制在数组长度内
        if ((p = tab[i = (n - 1) & hash]) == null)
            //如果数组这个位置是空的,构建一个新的节点
            tab[i] = newNode(hash, key, value, null);
        else {
            //需要放置到原有链表或红黑树中
            Node<K,V> e; K k;
            //p为这一个数据项的链表或红黑树的第一个节点,如果相等将节点返回给e
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                //如果是红黑树 放置到树上
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
               //循环链表
                for (int binCount = 0; ; ++binCount) {
                     //已经到尾部,增加一个新节点
                    if ((e = p.next) == null) {
                        //尾部指代那个一个新节点
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //长度超过8树化整个链表
                            treeifyBin(tab, hash);
                        break;
                    }
                    //节点已存在返回原有节点
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    //此处e已经等于p.next
                    p = e;
                }
            }
            //非新增情况
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    //替换为最新值
                    e.value = value;
                //为linkedHashmap扩展方法
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
       //容量已经超过需扩容容量  threshold = 容量*负载引子
        if (++size > threshold)
             //判断是否需要扩容
            resize();
        //为linkedHashmap扩展方法
        afterNodeInsertion(evict);
        return null;
    }

3. get方法

get方法用于获取已有值,主要是调用了getNode方法

        public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

接下来重点分析下getNode方法

    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //table不为空 长度大于0  hash &(n - 1)对应的数组下标不为空,否则数据不存在直接返回空 并把first只想对应的数组项
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            //检查第一个节点是否相等
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                //第二个节点是树节点,从树中获取
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                //循环链表找到对应值
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

相关文章

  • HashMap源码走读

    由于各个版本JDK间差异,本文主要基于JDK8源码来进行分析。 1. 构造方法 hashmap采用lazyload...

  • HashMap剖析

    Java集合:HashMap源码剖析 一、HashMap概述 二、HashMap的数据结构 三、HashMap源码...

  • HashMap源码

    HashMap的源码,基于jdk1.7Map的源码 AbstractMap的源码 HashMap的源码

  • [epoll 源码走读] LT 与 ET 模式区别

    走读内核源码,看看 epoll 的 LT 和 ET 模式区别。 详细信息可以参考文章《[epoll 源码走读] e...

  • HashMap源码笔记(二)

    紧接这上一篇:HashMap源码笔记(一)我们继续来分析HashMap源码,接下来我们来看看HashMap的源码说...

  • JTA-atomikos源码走读

    来源JTA-atomikos源码走读

  • 面试准备

    1.HashMap && CurrentHashMap源码分析 HashMap源码解析 java 并发编程之 Co...

  • java源码分析之LinkedHashMap

    相关文章java源码分析之HashMap(一)java源码分析之HashMap(二)java源码分析之HashMa...

  • HashMap原理以及ConcurrentHashMap

    一、HashMap的关键参数及部分源码解析 1.1 HashMap的几个关键参数 HashMap的源码中存下以下几...

  • 【16】 hashmap

    hashmap 1.7 和1.8的区别 hashmap全家桶 hashmap 源码解析 hashmap hashm...

网友评论

      本文标题:HashMap源码走读

      本文链接:https://www.haomeiwen.com/subject/zkhoprtx.html