Java 8中HashMap和LinkedHashMap如何解决冲突

什么时候会产生冲突??
HashMap中调用hashCode()方法来计算hashCode。
由于在Java中两个不同的对象可能有一样的hashCode,所以不同的键可能有一样hashCode,从而导致冲突的产生。

解决:

  1. 在Java 8 之前,HashMap和其他基于map的类都是通过链地址法解决冲突,它们使用单向链表来存储相同索引值的元素。
  2. 从Java 8开始,HashMap,ConcurrentHashMap和LinkedHashMap在处理频繁冲突时将使用平衡树来代替链表,当同一hash桶中的元素数量超过特定的值(默认是8)便会由链表切换到平衡树,这会将get()方法的性能从O(n)提高到O(logn)
  3. 当从链表切换到平衡树时,HashMap迭代的顺序将会改变。不过这并不会造成什么问题,因为HashMap并没有对迭代的顺序提供任何保证。
  4. 从Java 1中就存在的Hashtable类为了保证迭代顺序不变,即便在频繁冲突的情况下也不会使用平衡树。这一决定是为了不破坏某些较老的需要依赖于Hashtable迭代顺序的Java应用。
  5. 除了Hashtable之外,WeakHashMap和IdentityHashMap也不会在频繁冲突的情况下使用平衡树。
  6. 使用HashMap之所以会产生冲突是因为使用了键对象的hashCode()方法,而equals()和hashCode()方法不保证不同对象的hashCode是不同的。需要记住的是,相同对象的hashCode一定是相同的,但相同的hashCode不一定是相同的对象。
  7. 在HashTable和HashMap中,冲突的产生是由于不同对象的hashCode()方法返回了一样的值。在HashTable和HashMap中,冲突的产生是由于不同对象的hashCode()方法返回了一样的值。

为什么HashMap线程不安全:

首先如果多个线程同时使用put方法添加元素,而且假设正好存在两个 put 的 key 发生了碰撞(根据 hash 值计算的 bucket 一样),那么根据 HashMap 的实现,这两个 key 会添加到数组的同一个位置,这样最终就会发生其中一个线程的 put 的数据被覆盖。

第二就是如果多个线程同时检测到元素个数超过数组大小* loadFactor ,这样就会发生**多个线程同时对 Node 数组进行扩容,**都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给 table,也就是说其他线程的都会丢失,并且各自线程 put 的数据也丢失。

Entry代码

  static class Entry<K,V> implements Map.Entry<K,V> {  
        final K key;  
        V value;  
        Entry<K,V> next;  
        final int hash;  
  
        /**  
         * Creates new entry.  
         */  
        Entry(int h, K k, V v, Entry<K,V> n) {  
            value = v;  
            next = n;  
            key = k;  
            hash = h;  
        }  
、  
    }  

导致HashMap出现死循环是因为多线程会导致HashMap的Entry节点形成环链,这样当遍历集合时Entry的next节点用于不为空,从而形成死循环

猜你喜欢

转载自blog.csdn.net/mulinsen77/article/details/84879833