zoukankan      html  css  js  c++  java
  • HashMap源码解析

    变量:

        /**
         * 初始化容量为16
         */
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    
        /**
         * 最大容量:2^30
         */
        static final int MAXIMUM_CAPACITY = 1 << 30;
    
        /**
         * 负载因子默认0.75,负载因子越小,hash冲突几率越低
         */
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
        /**
         * 初始化一个空的数组
         */
        static final Entry<?,?>[] EMPTY_TABLE = {};
    
        /**
         * 存值的数组,初始化为空数组
         */
        transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
    
        /**
         * HashMap存入元素的个数
         */
        transient int size;
    
        /**
         * 临界值,HashMap能存储的大小,公式为(threshold=capacity*loadFactor)
         */
        // If table == EMPTY_TABLE then this is the initial capacity at which the
        // table will be created when inflated.
        int threshold;
    
        /**
         * 负载因子
         */
        final float loadFactor;
    
        /**
         *HashMap修改的次数,用于迭代
         */
        transient int modCount;
    
        /**
         * 最大值*/
        static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;

    构造方法:

        public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)//MAXIMUM_CAPACITY 最大容量
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))//loadFactor 负载因子
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
    
            this.loadFactor = loadFactor;
            threshold = initialCapacity;
            init();
        }

    走完构造方法,HashMap创建了一个空的数组,transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE; 当使用put(K, V)存入键值对的时候,才会使用inflateTable(initialCapacity)初始化数组的大小为initialCapacity。下面是put(K, V)源码:

        public V put(K key, V value) {
            if (table == EMPTY_TABLE) {
                inflateTable(threshold);//初始化table的大小
            }
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key);//根据key值计算出hash值
            int i = indexFor(hash, table.length);//根据hash值计算出数组的下标
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {//如果当前数组上的链表头不为null,遍历链表
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {//不为null时,检测key值得hash是否相同,如果相同替换掉原来的值,并返回原来的值
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
    
            modCount++;//修改次数+1
            addEntry(hash, key, value, i);//添加元素
            return null;
        }
        private static int roundUpToPowerOf2(int number) {
            // assert number >= 0 : "number must be non-negative";
            return number >= MAXIMUM_CAPACITY
                    ? MAXIMUM_CAPACITY
                    : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;//确保是2的倍数
        }
    
        /**
         * Inflates the table.
         */
        private void inflateTable(int toSize) {
            // Find a power of 2 >= toSize
            int capacity = roundUpToPowerOf2(toSize);
    
            threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);//临界值赋值
            table = new Entry[capacity];
            initHashSeedAsNeeded(capacity);
        }

    放入元素,如果是table是空数组,初始化table,临界值;

    key是null,将该entry放入到数组table的第0个下标处(第一个位置);

    根据key值计算出hash值,根据hash值计算出在数组table中的下标,根据下标找到链表,如果链表头不为null,遍历链表,检查key值是否存在于链表,如果存在,将新值替换掉旧值,并返回旧值;

    addEntry方法,将新的entry添加到链表头中。

        void addEntry(int hash, K key, V value, int bucketIndex) {
           Entry<K,V> e = table[bucketIndex];//记录原来的链表头
            table[bucketIndex] = new Entry<K,V>(hash, key, value, e);//表头引用该变成要插入的entry元素
            if (size++ >= threshold)
                resize(2 * table.length);//加入元素后,空间不足,扩容table长度的2倍
        }
  • 相关阅读:
    Linux的文本编辑和文本内容查看命令
    服务器日志查看处理(一)
    MAC 系统java开发环境搭建教程
    git撤销已经push到远端的commit
    Centos7 编译安装 Libmcrypt 库
    curl和wget的区别和使用
    docker 安装vim和yum命令
    docker安装GD扩展
    【docker】为docker下的php容器安装php-redis扩展【编译安装】
    mysql大表优化方案
  • 原文地址:https://www.cnblogs.com/teiba/p/9087669.html
Copyright © 2011-2022 走看看