探寻HashMap

hashMap解决hash冲突

jdk1.8中 hashMap采用数组+链表+红黑树的形式
在这里插入图片描述

首先hashMap会有一个数组 Node<K,V>[] table 作为hash桶,放值的时候函数调用put()->putVal(),然后在putVal()一通操作,如果没有发生hash冲突那将新建一个hash桶:

  if ((p = tab[i = (n - 1) & hash]) == null)
      tab[i] = newNode(hash, key, value, null);

那么重点就是发生冲突是怎么解决的呢?其实它是通过链表+红黑树去解决的,刚开始是链表解决冲突,但是随着链表的长度增加搜索效率变慢,因此我们想到使用搜索二叉树去解决这个问题,但是当出现某种极端情况时,搜索二叉树就会退化为链表,因此又提出了平衡二叉树(ALV),但是由于多种原因,这里我们一般使用的是红黑树。那么在什么时候需要由链表转换为红黑树呢?且看下面这段代码:

// 这段代码就是处理hash冲突
Node<K, V> e;
K k;
// 这个判断是不是首结点 
// 何谓首结点 在我看来应该是这个Node<K,V>[i]对应的这个结点
// 由于它下面连接着一个链表或者一个红黑树,这个首结点就是这颗树的根节点或者链表头结点
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) {
 //如果找到尾部,则表明添加的key-value没有重复,在尾部进行添加
		if ((e = p.next) == null) {
			p.next = newNode(hash, key, value, null);
			/**
			重点在此  
			TREEIFY_THRESHOLD = 8,(binCount从0开始)
			如果链表的长度大于8则需要执行 treeifyBin(tab, hash);
			*/
			
			if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
				treeifyBin(tab, hash);
			break;


		}
// 链表中存在key,则跳出循环
		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;
}

treeifyBin()一定注意并不是链表长度大于8就一定将其转换成红黑树


//  Replaces all linked nodes in bin at index for given hash
//	unless table is too small, in which case resizes instead.
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        // 这里对Node<K,V>[] table数组扩容
        // 因为hashMap认为如果数组的长度小于64时,且下面的链表长度大于8
        // 他会觉得是数组长度不够所以需要扩容,容量每次都会乘以2
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
          //这里转为红黑树
        }
    }

HashMap和LinkedHashMap,TreeMap之间的比较

HashMap:存取无序,放入的顺序和读取出来的顺序不一致。
LinkedHashMap extends HashMap:存读有序(维护一个双链表实现有序的存取数据,也就是放入的顺序和读取的顺序一致),他可以通过构造方法指定accessOrder=false插入有序(默认),否则读取有序。如果为true,那么获取这个map中某个key对应的值后,他会将其移到链表尾部。

  public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

这里放一张转载的LinkedHashMap数据结构图片
在这里插入图片描述图片源地址,博文参考 https://segmentfault.com/a/1190000012964859

再看看TreeMap
TreeMap它和HashMap没什么关系,只是都继承自AbstractMap,都实现了Cloneable,Serializable接口,但是TreeMap实现了NavigableMap,而NavigableMap继承了SortedMap,因此它可以实现自定义的排序功能,当然这是对放入的key进行排序。当然默认就是自然顺序排序。它的底层就是红黑树的数据结构

博文参考 https://www.cnblogs.com/LiaHon/p/11221634.html#%E4%B8%89-treemap%E6%9E%84%E9%80%A0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值