HashMap
- HashMap底层是使用数组+链表的形式进行存储,数组作为主干,链表作为hash冲突后的解决方案。
- 加入数据的时候会用hashCode()和特定的算法算出对应的位置,如果对应的位置上已经有值,则通过equal()比较,如果不同则放入到链表中。每一对值都是一个Entry。
- 如果容量超过阈值(容量*负载因子)将会重新创建数组,同时将老数组的数据进行拷贝(拷贝过程中会重新计算位置)
- hashMap的长度为2的幂次,主要是为了保证计算出的序号均匀
int index = (n - 1) & hash;//n为容量,eg:16-1=》1111
https://www.cnblogs.com/chengxiao/p/6059914.html hashmap的原理,为什么容器的长度是2的幂次,为什么要重写hashcode()
https://blog.youkuaiyun.com/zjcjava/article/details/78495416长度为什么为2的幂次建议参考这篇文章
- 重写hashCode()和equals() compareTo()
- 如果不重写equals(),比较的将是两个对象的地址
- 如果不重写hashCode(),默认是地址。否则创建两个参数完全相同的对象,在hash数据结构中存放的位置将不同,将不能定位到同样的位置
- hashCode()并不是一定要重写,但是用到hash数据结构时一定要
- 但是重写hashCode()方法的时候一定要保证equals()返回的结果一致
- 有序容器(TreeSet)重写compareTo()
- 当然你自己定义的对象可以实现equals相同而hashCode不同,但java的Object类中规定相同的对象一定要有相同的hashCode
- 对象被存储到hash表中,变量不能改变,因为改变后虽然hashcode发生了变化,但是不会重新计算位置
- hashCode()不能使用随机数,hash值必须稳定,同一个对象hashcode值始终相同
https://www.cnblogs.com/happyPawpaw/p/3744971.html
https://www.cnblogs.com/kanliwei/p/4280425.html
- 在jdk8中,当冲突的节点数量超过8个后,将改用红黑树进行存储
- 但是并不要求像TreeSet那样一定要实现Comparable,不过实现了效率更高。(如果没有实现会调用System.identityHashCode(),效率较低)
是否需要实现Comparable:https://blog.youkuaiyun.com/zly9923218/article/details/51656920
- 添加数据时可能触发resize()操作,而多个线程同时进行put操作的时候,可能会同时resize(),resize()操作会重新计算元素存放位置,之前存放在同一位置的一批数据在重新计算后后可能还在一个链中(但是顺序翻转),因此并发过程可能造成循环引用,从而在get()这个位置的数据时无限循环。建议使用ConcurrentHashMap解决
- 多线程下put()操作,如果有hash碰撞,执行addEntry(hash, key, value, i),多个线程的i值相同,先加入的就可能会数据丢失
- equals()和hashCode()间存在一个约定,两个元素相同则hashcode也要相同
保证容器的线程安全
- 使用Hashtable、或者是synchronized、或者是Collections.synchronizedMap进行粗粒度的线程安全保护
- 选择并发包进行细粒度的并发控制:ConcurrentHashMap,CopyOnWriteArrayList,ArrayBlockingQueue,SynchronousQuene
ConcurrentHashMap
- 引入了分段锁,在每次进行操作的时候存在风险的位置都会添加上锁,保证并发情况下不会有问题