HashMap摘要
HashMap应该算是Java程序员使用频率最高的容器类了,它主要用于处理键值映射数据。随着JDK的升级,JDK1.8后对HashMap底层采取了不少优化,例如引入红黑树,改用Node类存放节点等。但HashMap是线程不安全的类,遇到多线程问题时需要改用其他相关线程安全类。
HashMap继承体系
HashMap<K,V>是一个key-value结构,继承自抽象类AbstractMap<K,V>,实现了Map<K,V>以及Cloneable和Serializable接口;Cloneable是一个标记接口,里面没有任何东西,表明类中需要重写clone()方法时必须实现此接口;Serializable也是一个标记接口,表明需要序列化此类对象时必须实现该接口。
类中定义的serialVersionUID是人为定义的一个序列化编号,对于需要序列化的类即实现了Serializable接口的类建议一定加上serialVersionUID的定义,且一定为private static final long类型。下图是HashMap继承图。
HashMap容量
HashMap初始化容量DEFAULT_INITIAL_CAPACITY = 16,默认不写容量时就是16,下列是源码写法。首先,为何初始容量选择16,这个没有太多的解释,经验表明,16应该是大多数开发者最好的选择;其次,为何写成1 << 4,而不是直接写16,这个里面原理就深入了。
当我们在安装了阿里巴巴规范插件的IDEA中定义HashMap时,它会自动提醒我们将容量初始化为2的幂,这样做是为了方便进行位运算,位与运算&比算术运算效率高得多,而位与运算服务于将Key映射到index的算法。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
当我们计算出了Key的Hash值,我们如何将通过Hash值将相应的键均匀分布到HashMap中的位置上呢?HashMap是通过Hash值对容量取模运算的,但由于位于&运算效率大于取模运算,容量取2的幂时使用相应的位与运算和取模运算等价,所以一般使用2的幂作为容量初始值。下列是计算index公式,Length - 1得到的二进制位全是1,与hash位与运算等同于直接取HashCode后几位,具体的取模和位与运算为何等价读者可自行查阅资料。
index = (Length - 1) & hash
若随机取一个数作为初始值,HashMap在底层会直接取到大于等于此值的最小的2的幂。因此&#x