文章目录
Map下的接口与类关系图(部分)
1.HashMap
构造方法
有构造方法可以知道HashMap有两个重要的参数
int threshold; //阀值,下一个 参数负载因子 进行扩容,初始时HashMap容量大小为threshold
final float loadFactor;//负载因子
我们来看一下HashMap(int ,float)
public HashMap(int initialCapacity, float loadFactor) {
//桶数组的大小小于0时抛异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//如果桶数组的大小超过最大值,则简单的将桶容量修改为最大值2的30次方
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//如果负载因子不符合规范,那么抛异常
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
//这个意思是根据桶数组的大小求一个“极限值threshold”,当桶中的键值队个数超过这个大小就进行扩容
//tableSizeFor方法求得的数字是刚超过initialCapacity的一个2的n次方的数,例如initialCapacity是1000,那么得到的threshold就是1024
this.threshold = tableSizeFor(initialCapacity);
}
**tableSizeFor(initialCapacity)**将找到一个最接近且大于cap的2的N次方的数,应为这样是的发生hash冲突的概率更小。
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
putVal(…)方法中
if (++size > threshold)//当size大于阀值时 重新散列,
resize();
普通方法
public V put(K key, V value)
添加数据,当添加相同的Key时,新的值会覆盖旧的值,如果想看putVal()函数,看源码把,这就不贴出来了。
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
public V get(Object key)
根据Key获取值(value)根据源码可以看出,使用哈希码比较值,无返回空,有则返回值,如果想看getNode()函数,看源码把,这就不贴出来了。
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
public V replace(K key, V value)
输入参数Key 与 value ,如果存在用新值替换旧值并返回旧值,不存在则返回NULL。
public V replace(K key, V value) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) != null) {
V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
return null;
}
clear()
HashMap中使用clear()删除所有的数据,原理将所有的Node<K,V>置空,这样java GC自动回收空间。
Node<K,V>为HashMap的内置类
Node<K,V> implements Map.Entry<K,V>
Entry<K,V> 为Map的内置接口
/**
* Removes all of the mappings from this map.
* The map will be empty after this call returns.
*/
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
2.Hashtable
与HashMap不同的是Hashtable的函数几乎都是同步的,这意味着它是线程安全的。Hashtable实现线程安全的方法,就是因为它共有的方法都有synchronized修饰。它的key、value都不可以为null。而HashMap的key与value都可为空。
3.TreeMap
TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。
TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。
TreeMap 实现了Cloneable接口,意味着它能被克隆。
TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。
TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。
TreeMap的构造函数
一个指定Comparator的TreeMap
只要新建一个类实现Comparator接口
/**
*
* 定义一个逆序的Integer比较器
*/
class IntegerComparetor implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}
定义TreeMap时,加入构造器参数即可
TreeMap<Integer,Integer> map = new TreeMap(new IntegerComparetor());
WeakHashMap 继承于AbstractMap,实现了Map接口。
和[HashMap](http://www.cnblogs.com/skywang12345/p/3310835.html)一样,WeakHashMap 也是一个**散列表**,它存储的内容也是**键值对(key-value)映射**,而且**键和值都可以是null**。
4. WeakHashMap
WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:
(01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
(02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
(03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。
什么叫做弱引用
**弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。**不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
动态回收过程
(01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
将“键值对”添加到WeakHashMap中时,添加的键都是弱键。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
(02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到queue队列中。
例如,当我们在将“弱键”key添加到WeakHashMap之后;后来将key设为null。这时,便没有外部外部对象再引用该了key。
接着,当Java虚拟机的GC回收内存时,会回收key的相关内存;同时,将key添加到queue队列中。
(03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的“弱键”;同步它们,就是删除table中被GC回收的“弱键”对应的键值对。
例如,当我们“读取WeakHashMap中的元素或获取WeakReference的大小时”,它会先同步table和queue,目的是“删除table中被GC回收的‘弱键’对应的键值对”。删除的方法就是逐个比较“table中元素的‘键’和queue中的‘键’”,若它们相当,则删除“table中的该键值对”。
5.总结HashMap,Hashtable,TreeMap,WeakHashMap
HashMap 是基于“拉链法”实现的散列表。一般用于单线程程序中。
Hashtable 也是基于“拉链法”实现的散列表。它一般用于多线程程序中,属于线程安全,方法由synchronized修饰。
WeakHashMap 也是基于“拉链法”实现的散列表,它一般也用于单线程程序中。相比HashMap,WeakHashMap中的键是“弱键”,当“弱键”被GC回收时,它对应的键值对也会被从WeakHashMap中删除;而HashMap中的键是强键。
TreeMap 是有序的散列表,它是通过红黑树实现的。它一般用于单线程中存储有序的映射。