zoukankan      html  css  js  c++  java
  • Java集合(8)--HashMap源码分析

    /**
         * The default initial capacity - MUST be a power of two.
         */
        static final int DEFAULT_INITIAL_CAPACITY = 16;
    
        /**
         * The maximum capacity, used if a higher value is implicitly specified
         * by either of the constructors with arguments.
         * MUST be a power of two <= 1<<30.
         */
        static final int MAXIMUM_CAPACITY = 1 << 30;
    
        /**
         * The load factor used when none specified in constructor.
         */
        static final float DEFAULT_LOAD_FACTOR = 0.75f;

    上面是一些变量的默认值

    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);
    
            // Find a power of 2 >= initialCapacity
            //找到一个大于initialCapacity的数,并且这个数是2^n中大于initialCapacity的最小的一个数
            int capacity = 1;
            while (capacity < initialCapacity)
                capacity <<= 1;
    
            this.loadFactor = loadFactor;
            //计算阈值
            threshold = (int)(capacity * loadFactor);
            table = new Entry[capacity];
            init();
    }

    获得hashcode的索引,length是散列表的长度

    static int indexFor(int h, int length) {
            return h & (length-1);
        }

    get方法:

    public V get(Object key) {
            if (key == null)
                return getForNullKey();
            //计算hashcode的hash码
            int hash = hash(key.hashCode());
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                Object k;
                //既要hashcode相同,又要key相等,调用equals方法
                //key在方法开头的if语句就判断了,这里不必担心key为null
                if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                    return e.value;
            }
            return null;
        }

    put方法:

    public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key.hashCode());
            int i = indexFor(hash, table.length);
            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))) {
                    V oldValue = e.value;
                    e.value = value;
    e.recordAccess(
    this); //返回的是旧的值 return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }

    resize方法:

    void resize(int newCapacity) {
            Entry[] oldTable = table;
            int oldCapacity = oldTable.length;
            //到最大容量了,不能再增加了
            if (oldCapacity == MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return;
            }
    
            Entry[] newTable = new Entry[newCapacity];
            transfer(newTable);
            table = newTable;
            threshold = (int)(newCapacity * loadFactor);
        }

    transfer方法:重新拷贝值到新数组,重新计算hash,工作量大

    void transfer(Entry[] newTable) {
            Entry[] src = table;
            int newCapacity = newTable.length;
            for (int j = 0; j < src.length; j++) {
                Entry<K,V> e = src[j];
                if (e != null) {
                    src[j] = null;
                    do {
                        Entry<K,V> next = e.next;
                        //重新计算hash
                        int i = indexFor(e.hash, newCapacity);
                        e.next = newTable[i];
                        newTable[i] = e;
                        e = next;
                    } while (e != null);
                }
            }
        }

    当要加入的键值对个数大于阈值时,重新计算目标空间targetCapacity=(numKeysToBeAdded / loadFactor) + 1,还要找到一个新容量newCapacity大于targetCapacity,且是2^n的最小值,再resize

    public void putAll(Map<? extends K, ? extends V> m) {
            int numKeysToBeAdded = m.size();
            if (numKeysToBeAdded == 0)
                return;
            if (numKeysToBeAdded > threshold) {
                int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
                if (targetCapacity > MAXIMUM_CAPACITY)
                    targetCapacity = MAXIMUM_CAPACITY;
                int newCapacity = table.length;
                while (newCapacity < targetCapacity)
                    newCapacity <<= 1;
                if (newCapacity > table.length)
                    resize(newCapacity);
            }
    
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
                put(e.getKey(), e.getValue());
        }

    删除键:

    final Entry<K,V> removeEntryForKey(Object key) {
           //计算hash码
            int hash = (key == null) ? 0 : hash(key.hashCode());
            int i = indexFor(hash, table.length);
    
            Entry<K,V> prev = table[i];
            Entry<K,V> e = prev;
    
            while (e != null) {
                Entry<K,V> next = e.next;
                Object k;
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k)))) {
                    modCount++;
                    size--;
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    e.recordRemoval(this);
                    return e;
                }
                //也就是链表的删除
                prev = e;
                e = next;
            }
    
            return e;
        }

    addEntry方法:

    void addEntry(int hash, K key, V value, int bucketIndex) {
            Entry<K,V> e = table[bucketIndex];
            table[bucketIndex] = new Entry<>(hash, key, value, e);
           // 容量要大于阈值时,重设大小为原来的两倍
           if (size++ >= threshold)
                resize(2 * table.length);
        }
  • 相关阅读:
    Java面向对象——属性赋值的过程
    Java面向对象——类的成员之三:构造器(构造方法)constructor
    课后作业—5
    缓冲类的使用示例
    缓冲技术
    流的基类
    流的分类
    什么是流?
    关于开发中异常处理的建议
    阅读笔记-3
  • 原文地址:https://www.cnblogs.com/pipi-style/p/4738022.html
Copyright © 2011-2022 走看看