HashMap怎么学(一)内部结构

179 阅读2分钟

HashMap是JDK提供的一个集合工具类,也是最常用的工具类之一。

HashMap本身可以介绍的内容很多,所以小西准备用一个系列来讲,该篇文章先通过一个简单的例子把它的部分原理讲明白,后面的文章再慢慢深入介绍其他部分。

数据结构

  1. 顶层是一个数组
  2. 当发生hash冲突时,数组中的元素会转变成链表的头部或者红黑树的顶点

\

案例

首先我们看下运行代码:

public static void main(String[] args) {
		//初始化代码,插入12条数据
	  Map<Integer, Integer> map = new HashMap<>(20);
	  for(int i=0;i<12;i++) {
	  	map.put(i, i);
	  }
}

当运行完步骤一后,我们看下HashMap中内部变量的情况

HashMap内部变量图

从上图我们可以看到:

  1. size为12,表示HashMap里面元素的个数
  2. 我们存入的12条数据被保存在了table变量的前12个位置
  3. table数组的长度为16
  4. 每个元素的index刚好和它的key和value相等

原理分析

问题一: 为什么table的长度是16呢?

因为我们调用的是HashMap的无参构造函数,它的默认长度为变量DEFAULT_INITIAL_CAPACITY的值。

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

问题二: 如果我们调用了带参构造函数,内部变量table的长度是我们指定的值吗?

不一定。HashMap会通过以下方法将传入的initialCapacity值转换成第一个大于等于该值的2的指数倍的值。例如传入10的话会创建一个长度为16的table数组,传入20的话为32,以此类推。

/**
     * Returns a power of two size for the given target capacity.
     */
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

问题三: 为什么index为6的元素,它的key和value都是16呢?

HashMap首先会将key对象传入hash方法并得到一个int类型的hash值,然后传递给下面的putVal方法。(这里会将对象key的hashcode()返回值的高16位和低16位做一个异或操作,目的是为了减少哈希碰撞)

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

putVal方法主要关注下面最后一行即可,它会把内部数组table的长度值减1和上面方法返回的hash值进行与操作得到该key对应相关插入到table数组的index。

 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);

再回到问题三,我们传入的key和value都是值为6的Integer对象(new Integer(6)),而Integer对象的hashCode()方法返回的就是该Integer对象的内部int值,调用hash方法返回的值也是6;再通过(n - 1) & hash得到的数组index也是6。

注:n-1=15,对应的二进制值为1111,hash的值为6对应的二进制值为0110,1111&0110=0110

感兴趣的同学还可以关注公众号“小西学JAVA”同步获取最新文章。

引用

HashMap内部结构图引用自 JDK1.8之HashMap实现原理 - 吴振照 - 博客园