JAVA集合-HashMap

268 阅读2分钟

简介

HashMap存放的键值对,是常用的Java集合之一。
JDK1.8之前HashMap是由数组+链表组成,主体是数组,链表用来解决哈希冲突。
JDK1.8之后,当链表长度大于8链表会转为红黑树(如果数组长度小于64,则会优先扩容数组)。

结构

基本结构

HashMap的结构如上图所示。

HashMap通过对key的hashcode扰动后得到hash值,然后(数组长度-1)&hash值得到数组的下标,
若该位置已经有数据了,就判断两个key和hash值是否相同,若相同直接覆盖,否则通过拉链法解决
hash冲突。关于扰动函数和拉链法下面会讲到。

扰动函数和拉链法

扰动函数

static final int hash(Object key) {
        int h;
        //key.hashCode():获得key的hashCode
        //^:异或运算(二进制位,相同为0,不同为1)
        //>>>:右移
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

以上是JDK1.8 HashMap扰动函数的源码。

扰动函数是为了减少哈希碰撞。打个比方,数组下标的算法在上面已经说明了,如果数组的长度为
15,二进制则是0000000000001111,与hashCode进行与运算,高位全部归零,只取最后四位,
碰撞会非常严重,如果散列设计本来就不好,情况则会更加糟糕。经过扰动处理后,hashCode的
高位和低位进行了一个混合,增大了低位的随机性。最后和数组长度取模得到下标。

拉链法

拉链法上面的图已经画出来了,就是发生hash碰撞时,将发生碰撞的数据追加到链表后面。

流程及属性

基本属性

	//数组,长度一直为2的幂次倍
	transient Node<K,V>[] table;
	//具体的数据
	transient Set<Map.Entry<K,V>> entrySet;
	//数据个数
	transient int size;
	//计数器。每次更改或扩容map
	transient int modCount;
	//临界值(加载因子*容量),实际大小超过临界值会进行扩容
	int threshold;
	//加载因子,默认0.75,越小数组存放的数据小,越大数组存放的数据多
	final float loadFactor;

上面是HashMap的属性。

static class Node<K,V> implements Map.Entry<K,V> {
	//hash值,hash冲突时用来比对
	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节点属性。

流程图

数据插入的流程大致如上图所示。

结束

这里讲到了HashMap一些常见的属性和流程,接下来会讲其他几种集合。如果发现错误或者需要改进的地方,欢迎大家及时指正。