HashMap源码理解

HashMap介绍

HashMap是一个散列表,它是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,
它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度
。这个映射函数叫做散列函数,存放记录的数组叫做散列表。

HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。


HashMap 的实例有两个参数影响其性能:capacity 和 loadFactor。
capacity 只是哈希表在创建时的容量。
loadFactor 是哈希表在其容量自动增加之前可以达到多满的一种尺度。
当哈希表中的size超出了loadFactor*capacity,
则要对该哈希表进行 resize 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
通常,默认loadFactor是 0.75
loadFactor过高虽然减少了空间开销,
但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。
在设置capacity时应该考虑到映射中所需的条目数及其loadFactor,以便最大限度地减少 rehash 操作次数。
如果capacity大于最大条目数除以loadFactor,则不会发生 resize 操作。

HashMap的构造函数和常用方法

// 默认构造函数。
HashMap()

// 带容量大小的构造函数
HashMap(int capacity)

// 带容量大小和加载因子的构造函数
HashMap(int capacity, float loadFactor)

// 带子Map的构造函数
HashMap(Map<? extends K, ? extends V> map)

源码 (Android API 25)

//默认容量大小,但是在JDK中查看是1 << 4,反正必须是2的幂
static final int DEFAULT_INITIAL_CAPACITY = 4;

//最大容量
 static final int MAXIMUM_CAPACITY = 1 << 30;

//默认加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

//在这个map中的键值对数量
transient int size;

//hashmap的阈值,用于判断是否调整hashmap的容量(threshold=capacity * load factor)
int threshold;

//hsshmap结构被修改的次数
transient int modCount;

//hashmap的key-value都存储在table中
transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;


/** *带两个参数的构造方法 */
public HashMap(int initialCapacity, float loadFactor) {

    //校验初始容量
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //对初始容量做判断,最大只能是MAXIMUM_CAPACITY,最小只能是DEFAULT_INITIAL_CAPACITY
    if (initialCapacity > MAXIMUM_CAPACITY) {
        initialCapacity = MAXIMUM_CAPACITY;
    } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) {
        initialCapacity = DEFAULT_INITIAL_CAPACITY;
    }

    //此时的阈值就是初始容量
    threshold = initialCapacity;
    init();
}


//根据传入的容量真正的创建HashMapEntry
private void inflateTable(int toSize) {
    // Find a power of 2 >= toSize
    int capacity = roundUpToPowerOf2(toSize);

    // Android-changed: Replace usage of Math.min() here because this method is
    // called from the <clinit> of runtime, at which point the native libraries
    // needed by Float.* might not be loaded.
    float thresholdFloat = capacity * loadFactor;
    if (thresholdFloat > MAXIMUM_CAPACITY + 1) {
        thresholdFloat = MAXIMUM_CAPACITY + 1;
    }

    threshold = (int) thresholdFloat;
    // 创建Entry数组,用来保存数据
    table = new HashMapEntry[capacity];
}

/** *Rehashes the contents of this map into a new array with a * larger capacity. This method is called automatically when the * number of keys in this map reaches its threshold. * *当map中的key达到阈值后自动调用,将map中的内容全部转移到一个newTable中 */

void resize(int newCapacity) {
    HashMapEntry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }

    HashMapEntry[] newTable = new HashMapEntry[newCapacity];
    transfer(newTable);
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

//将所有的entries转移到newTable中
void transfer(HashMapEntry[] newTable) {
    int newCapacity = newTable.length;
    for (HashMapEntry<K,V> e : table) {
        while(null != e) {
            HashMapEntry<K,V> next = e.next;
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}

//根据key删除
public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.getValue());
}


//根据key删除
final Entry<K,V> removeEntryForKey(Object key) {
    if (size == 0) {
        return null;
    }
    //根绝key计算出hash值
    int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
    int i = indexFor(hash, table.length);
    HashMapEntry<K,V> prev = table[i];
    HashMapEntry<K,V> e = prev;

    while (e != null) {
        HashMapEntry<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;
}

//获取键值对个数
public int size() {
    return size;
}

//判断是否为空
public boolean isEmpty() {
    return size == 0;
}


//获取key对应的value
public V get(Object key) {
    if (key == null)
        return getForNullKey();
    Entry<K,V> entry = getEntry(key);

    return null == entry ? null : entry.getValue();
}

//hashmap中的key和value都可以为null
private V getForNullKey() {
    if (size == 0) {
        return null;
    }
    for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {
        if (e.key == null)
            return e.value;
    }
    return null;
}

//判断是否包含key
public boolean containsKey(Object key) {
    return getEntry(key) != null;
}

//向hashmap中存入键值对
public V put(K key, V value) {
    //如果表为空则调用inflateTable创建一个表
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    if (key == null)
        return putForNullKey(value);
    //计算出hash值
    int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
    int i = indexFor(hash, table.length);
    for (HashMapEntry<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++;
    //新增一个Entry
    addEntry(hash, key, value, i);
    return null;
}

//清空整个hashmap
public void clear() {
    modCount++;
    Arrays.fill(table, null);
    size = 0;
}

//判断是否包含某个值
public boolean containsValue(Object value) {
    if (value == null)
        return containsNullValue();

    //创建一个临时的数组,在数组中遍历判断是否包含
    HashMapEntry[] tab = table;
    for (int i = 0; i < tab.length ; i++)
        for (HashMapEntry e = tab[i] ; e != null ; e = e.next)
            if (value.equals(e.value))
                return true;
    return false;
}

//HashMapEntry的实现,包含key、value、next、和hsah值。
static class HashMapEntry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    HashMapEntry<K,V> next;
    int hash;

    /** * Creates new entry. */
    HashMapEntry(int h, K k, V v, HashMapEntry<K,V> n) {
        value = v;
        next = n;
        key = k;
        hash = h;
    }

    public final K getKey() {
        return key;
    }

    public final V getValue() {
        return value;
    }

    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }

//判断两个Entry是否相等,只有当key和value都相等时才返回true
    public final boolean equals(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry e = (Map.Entry)o;
        Object k1 = getKey();
        Object k2 = e.getKey();
        if (k1 == k2 || (k1 != null && k1.equals(k2))) {
            Object v1 = getValue();
            Object v2 = e.getValue();
            if (v1 == v2 || (v1 != null && v1.equals(v2)))
                return true;
        }
        return false;
    }

    public final int hashCode() {
        return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
    }

    public final String toString() {
        return getKey() + "=" + getValue();
    }


    void recordAccess(HashMap<K,V> m) {
    }


    void recordRemoval(HashMap<K,V> m) {
    }
}

//新增一个Entry,有可能导致size超过阈值时调用,然后会创建一个新的hashmap
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        //创建一个新的hashmap容量是原来的两倍
        resize(2 * table.length);
        hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }

    createEntry(hash, key, value, bucketIndex);
}

//新增Entry时不会导致size超过阈值时调用此方法
void createEntry(int hash, K key, V value, int bucketIndex) {
    HashMapEntry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
    //size加1
    size++;
}

HashMap简单使用

HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three",3);

System.out.println(hashMap);
//打印结果:
{one=1, two=2, three=3}

hashMap.remove("one");
System.out.println(hashMap);
//打印结果:
{two=2, three=3}

System.out.println(hashMap.containsKey("two"));
//打印结果
true

System.out.println(hashMap.containsValue(3));
//打印结果:
true

HashMap遍历

Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    // 取key
    key = (String)entry.getKey();
    // 取value
    value = (Integer)entry.getValue();
}
    原文作者:wangrcheng
    原文地址: https://blog.csdn.net/wangcheng_/article/details/79646604
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞