zoukankan      html  css  js  c++  java
  • HashMap的源码分析(一)

    1.hashMap的关键值

      DEFAULT_INITIAL_CAPACITY:默认初始容量16,∈(0,1<<30),实际大小为2的整数次幂;

      DEFAULT_LOAD_FACTOR:默认加载因子0.75,

      threshold:阈值,resize的判断条件。(threshold=容量*加载因子)

    2.hashMap 的构造函数

        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;
            threshold = initialCapacity;
            init();
        }

        public HashMap(int initialCapacity) { //指定容量,加载因子0.75
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }

        public HashMap() { //默认容量16,加载因子0.75
            this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
        }

        public HashMap(Map<? extends K, ? extends V> m) {//构造一个映射关系与指定 Map 相同的新 HashMap.(一直不明白这样构造和new HashMap(); 然后 map.putAll(map2) 有什么区别?希望看到的大神讲解一下··)
            this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                          DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
            inflateTable(threshold);

            putAllForCreate(m);
        }

    3.hashMap的get()、put()方法

      ①put

        public V put(K key, V value) {
            if (table == EMPTY_TABLE) {   
                inflateTable(threshold);
            }
            if (key == null)      //如果key为nullreturn putForNullKey(value);   
            int hash = hash(key);  
            int i = indexFor(hash, table.length);  //key不为null,根据key的hash值计算出table位置  (i = hash & 容量 )
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {   
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  //遍历对比key==this.key?新值替换旧值,返回旧值:新增entry
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
    
            modCount++;
            addEntry(hash, key, value, i);//return null;    //返回值不为null,说明这次put发生了新旧值得替换
        }

        private V putForNullKey(V value) { //put进去key为null的键值对
            for (Entry<K,V> e = table[0]; e != null; e = e.next) { //key为null的entry默认放在table[0],遍历table[0]的entry
                if (e.key == null) { //若果以前有key为null的entry
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue; //新值替换旧值,并且返回旧值
                }
            }
            modCount++;
            addEntry(0, null, value, 0); //以前没有当前key值,新增entry,
            return null;
        }

        void addEntry(int hash, K key, V value, int bucketIndex) {//新增entry
            if ((size >= threshold) && (null != table[bucketIndex])) { //size(key-value对)达到阈值 && 当前table不是空的---》resize
                resize(2 * table.length); //扩容为当前2倍,根据新的容量重新计算分配map中entry的位置
                hash = (null != key) ? hash(key) : 0;
                bucketIndex = indexFor(hash, table.length);
            }

            createEntry(hash, key, value, bucketIndex); //新建entry
        }

        void createEntry(int hash, K key, V value, int bucketIndex) { //新建entry
            Entry<K,V> e = table[bucketIndex]; //那到当前table的第一个entry(马上第一要变第二了!)
            table[bucketIndex] = new Entry<>(hash, key, value, e); //把新entry插入到table的第一格
            size++; //key-value对数量加一
        }

      ②get

        public V get(Object key) {
            if (key == null)
                return getForNullKey();    //返回key为null的value 
            Entry<K,V> entry = getEntry(key);    //拿到key的entry return null == entry ? null : entry.getValue();  //返回entry的value
        }
        private V getForNullKey() { //如果map.get(null) 返回table[0]中key为null的value,没有返回null
            if (size == 0) { //hashMap已占用槽的长度
                return null;
            }
            for (Entry<K,V> e = table[0]; e != null; e = e.next) {
                if (e.key == null)
                    return e.value;
            }
            return null;
        }

        final Entry<K,V> getEntry(Object key) {
            if (size == 0) { //判断map中的key-value对不是0
                return null;
            }

            int hash = (key == null) ? 0 : hash(key); //获取key的hash值
            for (Entry<K,V> e = table[indexFor(hash, table.length)]; //通过hash值和容量计算出table位置n,遍历table[n]里面的entry对比key相等的返回entry
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            }
            return null;
        }

     ③remove

        public V remove(Object key) {
            Entry<K,V> e = removeEntryForKey(key);
            return (e == null ? null : e.value);  //remove方法返回删除掉的value
        }

        final Entry<K,V> removeEntryForKey(Object key) {
            if (size == 0) {
                return null;
            }
            int hash = (key == null) ? 0 : hash(key);
            int i = indexFor(hash, table.length); //通过hash计算index
            Entry<K,V> prev = table[i]; //首次遍历是未 table[i] 的头entry
            Entry<K,V> e = prev; //用于遍历的entry

            while (e != null) {
                Entry<K,V> next = e.next; //拿到后项entry
                Object k;
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) { //找到要删除的key的entry
                    modCount++;
                    size--; //删除后size减一
                    if (prev == e) //如果要删除的是 table[i]的头entry
                        table[i] = next; //老二晋升为 table[i]的头entry
                    else
                        prev.next = next; //前一entry的next指向后一entry(删除当前entry)
                    e.recordRemoval(this);
                    return e;
                }
                prev = e; //将当前entry设为前女友
                e = next; //下一个
            }

            return e;
        }

    ④clear

        public void clear() {
            modCount++;                   //对map的操作次数,用于迭代时的线程安全
            Arrays.fill(table, null);
            size = 0;                       //数组中的entry数量设为0,此时的容量没有缩小。仍为clear前的容量
        }
    
        public static void fill(Object[] a, Object val) {
            for (int i = 0, len = a.length; i < len; i++)        //遍历将数组的所有值设为null,
                a[i] = val;
        }

     ⑤containsValue

        public boolean containsValue(Object value) {
            if (value == null)
                return containsNullValue();
    
            Entry[] tab = table;
            for (int i = 0; i < tab.length ; i++)          //遍历数组
                for (Entry e = tab[i] ; e != null ; e = e.next)        //遍历entry
                    if (value.equals(e.value))
                        return true;
            return false;
        }
    
        private boolean containsNullValue() {
            Entry[] tab = table;
            for (int i = 0; i < tab.length ; i++)
                for (Entry e = tab[i] ; e != null ; e = e.next)
                    if (e.value == null)
                        return true;
            return false;
        }
  • 相关阅读:
    C#创建资源文件
    C#基础-获得当前程序的 空间名.类名.方法名
    C# 事务
    sql作业
    获取IP和mac地址
    winform文本框不能粘贴、复制和屏蔽右键
    Linux的iptables常用配置范例(1)
    自动化运维工具Ansible详细部署
    rsync+inotify实现数据的实时备份
    leetCode(26):Unique Binary Search Trees
  • 原文地址:https://www.cnblogs.com/caoyajun33-blog/p/7615132.html
Copyright © 2011-2022 走看看