有关HashMap的相关知识点
引入红黑树优化HashMap的性能,通过数组+链表+红黑树实现;
在JDK1.8中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现:(h = k.hashCode()) ^ (h >>> 16)
equals方法
equals方法在Object类中默认的实现方式是: return this == obj 。也就是说只有this 和 obj引用同一个对象,才会返回true。
而我们往往需要用equals来判断 2个对象是否等价,而非验证他们的唯一性。这样我们在实现自己的类时,就要重写equals。
hashCode方法
这个方法返回对象的散列码,返回值是int类型的散列码。
重写hashCode时注意事项
重写hashCode方法时除了上述一致性约定,还有以下几点需要注意:
(1)返回的hash值是int型的,防止溢出。
(2)不同的对象返回的hash值应该尽量不同。(为了hashMap等集合的效率问题)
如果两个对象根据equals方法比较是相等的,那么调用这两个对象的任意一个hashcode方法都必须产生相同的结果。
如果不重写hashcode在我们调用HashSet和HashMap的时候可能会造成歧义,也就是用equals方法判断的两个对象相等,但是hashcode不相等,会造成hashmap散列到不同数组下标,导致了哈希表中有两个相同的key。
重写 hashcode 方法是为了让我们能够正常使用 HashMap 等集合类,因为 HashMap 判断对象是否相等既要比较 hashcode 又要使用 equals 比较。HashMap 的链表结构中遍历判断的时候,特定情况下重写的 equals 方法比较对象是否相等的业务逻辑比较复杂,循环下来更是影响查找效率。所以这里把 hashcode 的判断放在前面,只要 hashcode 不相等就玩儿完,不用再去调用复杂的 equals 了。这样的实现是为了提高 HashMap 的效率。
HashMap并非线程安全,多线程使用时,会导致值覆盖,会导致ConcurrentModificationException异常。
如果modCount不等于expectedModCount,则抛出ConcurrentModificationException异常
处理方法:
1、在使用iterator迭代的时候使用synchronized或者Lock进行同步;
2、使用并发容器。
Java中线程安全的map的实现方式:
HashTableSynchronized MapConcurrentHashMap1、注意线程安全,避免出现ConcurrentModificationException异常;
2、重写equals方法后需要同时改写hashcode方法;
HashMap是基于哈希表的Map接口的非同步实现。
1、利用key的hashCode重新hash计算出当前对象的元素在数组中的下标
2、存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中
3、获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。
4、HashMap是解决hash冲突的核心是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。
在并发的情况下,发生扩容时,可能会产生循环链表,在执行get的时候,会触发死循环,引起CPU的100%问题。
不可以
put操作调用putval方法,先判断Hash是否一致,然后在判断传入key和当前集合中是否有相同的key。如果key相同,则新值替换旧值。
如果想要实现在HashMap中插入两个相同的Key,需要重写HashMap的put方法或者重写对象的equals方法和hashcode方法。
这里记录一个通过创建新的MyHashMap类实现插入两个相同的key的功能的方法。
MyHashMap类继承HashMap类,重写put方法:
import java.util.HashMap; public class MyHashMap<O, O1> extends HashMap { @Override public Object put(Object key, Object value) { String newV = value.toString(); if (containsKey(key)) { String oldV = get(key).toString(); newV = oldV + "---" + newV; } return super.put(key, newV); } }测试:
import java.util.*; public class Test { public static void main(String[] args) { Map<Object, Object> map = new MyHashMap<Object, Object>(); map.put(1, 12); map.put(1, 13); map.put("长沙", "出美女"); map.put("长沙", "风景美"); Iterator it = map.entrySet().iterator(); while(it.hasNext()){ Map.Entry entry = (Map.Entry) it.next(); System.out.println(entry.getKey()+" "+ entry.getValue()); } } }结果:
1、HashMap计算key的hash值时调用单独的方法,在该方法中会判断key是否为null,如果是则返回0;而Hashtable中则直接调用key的hashCode()方法,因此如果key为null,则抛出空指针异常;
2、HashMap将键值对添加进数组时,不会主动判断value是否为null;而Hashtable则首先判断value是否为null。
3、以上原因主要是由于Hashtable继承自Dictionary,而HashMap继承自AbstractMap。
4、虽然ConcurrentHashMap也继承自AbstractMap,但是其也过滤掉了key或value为null的键值对。
hashmap中的元素个数超过数组大小*loadFactor(负载因子)时,就会进行数组扩容,loadFactor的默认值为0.75,如果过小,比如0.5,那么当存放的元素超过一半时就进行扩容,会造成资源的浪费;如果过大,比如1,那么当元素满的时候才进行扩容,会使get,put操作的碰撞几率增加。
JDK1.8引入红黑树大程度优化了HashMap的性能;
JDK1.8中的HashMap存储结构是由数组、链表、红黑树这三种数据结构形成,红黑树查询删除快新增慢。根据key的hash与table长度确定table位置,同一个位置的key以链表形式存储,超过一定限制链表转为树。
参考链接:
https://www.jianshu.com/p/75d9c2c3d0c1
https://www.cnblogs.com/ouym/p/8963219.html
https://blog.csdn.net/xjh1997/article/details/81368371
https://www.cnblogs.com/dolphin0520/p/3933551.html
https://www.cnblogs.com/yuanblog/p/4441017.html
https://www.cnblogs.com/aflyun/p/10733029.html
https://blog.csdn.net/qq_29373285/article/details/84999671
https://blog.csdn.net/shenjianzhuang/article/details/79033054
https://blog.csdn.net/gaozongjian/article/details/82911141
https://blog.csdn.net/u012156116/article/details/81206649
https://blog.csdn.net/wanderlustLee/article/details/80747860
http://www.importnew.com/20386.html
这些东西内容还是相当多的。。。。。
