前言
hash
任何字符都有一个hash值,一个对象的hash值是唯一确定的
例如查看a的hash值可以使用a.hashcode()来打印a的原始hash值
但是原始hash值并不能作为hashmap的key,还需要一定的算法也就是hash函数来对值进行转换,转换为二次hash值也就是本hashmap所使用的作为key的hash值。
注意:hash值的算法有很多例如希尔与取余还有数字分析法,直接定址法,平方取中法这里我们讨论的是常见的除留余数法。
下面是1.8的hashmap的二次hash
二次hash会使数据更加均匀分布于桶中。
hashmap
长啥样?
扩容
factor是装填因子,是map扩容的依据。上图放第13个数据触发扩容
hashmap的扩容是成倍扩容
16 32 64
hashmap的优势是可以通过《计算hash函数》这一步骤避免盲目顺序查找
jdk8红黑树
避免链化过长,于是树化。
树化阈值为8且数组长度不小于64。
长啥样?
左小右大,其实就是折半查找的判定树,相同颜色的节点还要满足平衡树的条件。
当我们删除元素时可能需要调整平衡性
这些都是为了能够更加高效的查找。
红黑树的缺点是treenode更加占空间了
红黑树退化成链表
第一种情况是正对扩容的情况导致的退化
第二种退化情况是针对移除元素时的操作要在移除前判断
为什么重写equals要重写hashcode?
总结:放入时三步比较。1.hash值 2 .equals 3.二次hash
下面的hashset可以换为hashmap
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashCode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashCode 值作比较,如果没有相符的 hashCode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashCode 值的对象,这时会调用 equals() 方法来检查 hashCode 相等的对象是否真的相同。如果两者相同,HashSet 就《不会让其加入操作成功》。如果不同的话,就会重新散列到其他位置。。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。 其实, hashCode() 和 equals()都是用于比较两个对象是否相等。
注意点:
1.要知道hashcode方便我们进行查找操作进而去比较。
2.要知道上述过程的顺序:先比较hash值,不一样在比较equals,还不一样说明两者完全不同那就会发生二次散列。
那为什么两个对象有相同的 hashCode 值,它们也不一定是相等的?
因为 hashCode() 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关(所谓哈希碰撞也就是指的是不同的对象得到相同的 hashCode )。 总结下来就是 :
如果两个对象的hashCode 值相等,那这两个对象不一定相等(哈希碰撞)。
如果两个对象的hashCode 值相等并且equals()方法返回 true,我们才认为这两个对象相等。
如果两个对象的hashCode 值不相等,我们就可以直接认为这两个对象不相等。
重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。这样hashset当中就会重复加入equals的对象了。