第九章 集合
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); }
Hash算法(JDK 1.8)
保证了高16位的变化能反应到低16位,相对而言减少了位运算,是一种折中的设计。
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
(HashMap JDK 1.7 和 JDK 1.8的实现https://my.oschina.net/hosee/blog/618953)
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):与开放地址法相比,链地址法有如下优点:
- 散列值不同不会发生冲突,减少查找时间
- 链表的结点动态申请,适合数据量不确定的情况
- 删除结点易于操作
常见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. Hashtable与HashMap
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
http://www.importnew.com/22007.html