HashMap源码分析

1.HashMap基本原理:HashMap内部维护了一个数组,默认长度16,存储的时候根据key的hashcode计算出下标,存储到对应下标的数组位置,如果hash冲突就挂成链表,链表长度达到8就转成红黑树,默认负载因子是0.75,如果数组的内容占满了0.75就会扩容,每次扩容是之前的两倍

 

2.核心成员变量分析

 

/**
 * 数组默认初始长度16
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

/**
 * 最大长度
 */
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
 * 负载因子,数组元素数量达到了0.75就会扩容
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
 * 转成红黑树的阈值,链表长度达到8就会转成红黑树
 */
static final int TREEIFY_THRESHOLD = 8;

/**
 * Node数组,将数据封装成一个Node放到table里面
 */
transient Node<K,V>[] table;

/**
 * 键值对数量
 */
transient int size;

3.Node类成员变量分析

 

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + "=" + value; }

    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }

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

    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

Node类实现了Map.Entry接口,里面存放key value,是一个单向链表

 

4.HashMap数据结构:HashMap内部维护了一个Node数组table,Node存放key-value,通过hash计算数组下标将元素封装成Node,放到对应table下标位置去,如果hash冲突就挂成链表,链表长度达到8转成红黑树,以下图片并不标准,大体示意HashMap的存储结构

5.降低hash冲突概率的hash算法

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

 

以上是put的代码,我们分析hash(key)如何拿到hashcode的。

key为null的时候返回0,我们好好分析一下(h = key.hashCode()) ^ (h >>> 16)

我现在put("name", "张三"), key.hashCode()==00000000001100110111101010001011

h : 00000000001100110111101010001011

h >>> 16==00000000000000000000000000110011

然后h和>>>16做异或

00000000001100110111101010001011 ^ 00000000000000000000000000110011

异或不同为1,相同为0,计算结果如下

0000 0000 0011 0011 0111 1010 1000 1011

0000 0000 0000 0000 0000 0000 0011 0011

0000 0000 0011 0011 0111 1010 1011 1000

最后返回的hashcode值为0000 0000 0011 0011 0111 1010 1011 1000

这么做的目的是为什么降低hash冲突,当数值小的时候高16为都为0,导致高16为不会参与hash计算,h >>> 16是将hashcode的高16为移到低16位,然后使用^预算,让hashcode的低16位中同时保存了高16位和低16位的特征,就能让高低16为全部都参与计算,来降低hash冲突

 

6.V put(K key, V value)源码分析

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        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
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

我们一行一行来分析,首先hash(key)已经拿到了一个混合了高低16位hashcode,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值