第九章 集合

1. ArrayList

  基于动态数组,支持随机查询,访问速度快。

  默认构造初始容量为10的空列表,每次扩容增加50%。插入删除需要复制数组,效率低。

  Vector 线程安全,扩容时增加100%。

2. LinkedList

  基于链表实现,使用迭代器遍历查询,访问速度慢。

  插入删除效率高。

3. HashMap

  Hash算法(JDK 1.7)

  全部32位变化都会引起hash值的改变,高位的变化会反映到低位。  

static int hash(int h) {
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
View Code

   Hash算法(JDK 1.8)

  保证了高16位的变化能反应到低16位,相对而言减少了位运算,是一种折中的设计。  

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
View Code

  HashMap不是线程安全的,rehash可能存在死循环:

  当两个线程重新调整HashMap大小的过程中,存储在链表中的元素次序与原来相反,移动到新位置的元素存放于链表头部而不是尾部。如果存在竞态条件,会形成回环。

  解决Hash冲突的方法:https://blog.csdn.net/u012104435/article/details/47951357

  •  开放地址法:

    1)线性探测法:

      插入元素时,若发生冲突,则从该位置向后遍历hash表,直到找到一个空位并放入(导致hash值相同的数据靠在一起,占用其他hash值数据的位子)。

      查找元素时,首先指向散列值指向的位置,从该位置开始向后遍历,直到

      1、找到指定元素

      2、找到空槽,表示查找元素不存在(所以不能随便删除元素)

      3、整个hash表遍历完毕(查找元素不存在且hash表已满)。

    2)线性补偿探测法:将探测步长从1改为Q,hash = (hash + Q)%m。且要求Q与m互质,以便探测到哈希表中所有单元。

    3)伪随机探测法:将线性探测的补偿从常数改为随机数,实际应用中有一个随机数序列,将此序列依次作为探测步长。不同的关键字具有不同的探测步长,减少堆聚的出现。

  •  再散列:当发生冲突时,采用另外的hash函数计算地址,直到无冲突。
  •    链地址法(hashMap):与开放地址法相比,链地址法有如下优点:
    1. 散列值不同不会发生冲突,减少查找时间
    2. 链表的结点动态申请,适合数据量不确定的情况
    3. 删除结点易于操作

  常见Hash算法:https://www.cnblogs.com/duanxz/p/3710690.html

    直接寻址法:取关键字或关键字的某个线性函数值作为散列地址,例如 H(key) = key or H(key) = a*key + b;

    数字分析法:分析关键字的规律构造冲突较低的散列函数

    平方取中法:取关键字平方后的中间几位作为散列值

    随机数法:选择一个随机函数,取关键字的随机值作为散列值,适用于关键字长度不一的情况

    折叠法:将关键字分隔成几部分,取这些部分的叠加和(去除进位)作为散列值

  让HashMap同步的方法: Map m = Collections.synchronizeMap(hashMap);

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
        return new SynchronizedMap<>(m);
}
private static class SynchronizedMap<K,V>
        implements Map<K,V>, Serializable {
        private static final long serialVersionUID = 1978198479659022715L;

        private final Map<K,V> m;     // Backing Map
        final Object      mutex;        // Object on which to synchronize

        SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

        SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        }
        ...
        //所有方法锁住mutex对象
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        ...
}    

  HashMap源码分析:http://www.cnblogs.com/xwdreamer/archive/2012/06/03/2532832.html

4. HashtableHashMap

  1)继承不同

    Hashtable继承自Dictionary类,HashMap继承自AbstractMap类。

  2)线程安全性不同

    Hashtable是线程安全的,方法添加synchronized关键字确保同步。HashMap不是线程安全的。

  3)对null处理不同

    HashMap支持null作为key和value,Hashtable不允许(key, value都不允许)。HashMap的方法get()返回null时,既可以表示没有改键,也可以表示该键对应的值为null,所以不能判断是否有该键,而应该使用containsKey()。

  4)初始容量及扩容算法不同

    HashMap初始容量16,HashTable初始容量11。HashMap扩容时容量*2,HashTable扩容时当前容量*2+1。

  5)哈希算法不同

    Hashtable使用key的hashcode对数组长度取模。HashMap对key的hashcode二次hash,然后对数组长度取模。

5. ConcurrentHashMap

  

  

猜你喜欢

转载自www.cnblogs.com/walker993/p/9341982.html