1. HsahMap
1.1 概述,API文档介绍
- 开始于JDK1.2
- 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
- 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
- HashMap 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
- 通常,默认加载因子 (0.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
- 如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。
1.2 声明和构造方法
声明:public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable
// HashMap 构造方法摘要
HashMap() // 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity) // 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。
HashMap(int initialCapacity, float loadFactor) // 构造一个带指定初始容量和加载因子的空 HashMap。
HashMap(Map<? extends K,? extends V> m) // 构造一个映射关系与指定 Map 相同的新 HashMap。
1.3 HashMap 扩容原理
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field threshold.
* Otherwise, because we are using power-of-two expansion, the
* elements from each bin must either stay at same index, or move
* with a power of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
} // 扩容至原来的 2 倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) { // 将 map 里面的元素复制到新的 map 里面
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
2. Hashtable
2.1 概述,API文档介绍
- 开始于JDK1.0
- 此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
- 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
- Hashtable 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
- 通常,默认加载因子(0.75)在时间和空间成本上寻求一种折衷。
- 初始容量主要控制空间消耗与执行
rehash
操作所需要的时间损耗之间的平衡。 - 如果很多条目要存储在一个
Hashtable
中,那么与根据需要执行自动 rehashing 操作来增大表的容量的做法相比,使用足够大的初始容量创建哈希表或许可以更有效地插入条目。
2.2 声明和构造方法
声明:public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, Serializable
// Hashtable 构造方法摘要
Hashtable() // 用默认的初始容量 (11) 和加载因子 (0.75) 构造一个新的空哈希表。
Hashtable(int initialCapacity) // 用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表。
Hashtable(int initialCapacity, float loadFactor) // 用指定初始容量和指定加载因子构造一个新的空哈希表。
Hashtable(Map<? extends K,? extends V> t) // 构造一个与给定的 Map 具有相同映射关系的新哈希表。
2.3 Hashtable中的特殊的迭代方式 -- 枚举
定义:public interface Enumeration<E>
枚举是从JDK1.0版本出现的一种迭代方式,实现 Enumeration 接口的对象,它生成一系列元素,一次生成一个。连续调用 nextElement
方法将返回一系列的连续元素。
这些方法主要通过向量的元素、哈希表的键以及哈希表中的值进行枚举。枚举也用于将输入流指定到 SequenceInputStream
中。
与枚举作用相同的是迭代器,public interface Iterator<E>。始于JDK1.2版本。
迭代器相比于枚举:1. 简化了书写;2. 在迭代期间可以移除Collection中的元素。
public static void main(String[] args) {
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("JAVA01", "Java基础课程");
hashtable.put("JAVA02", "Java高级课程");
hashtable.put("JAVA03", "JavaWEB课程");
hashtable.put("JAVA04", "JavaEE课程");
Enumeration<String> keys = hashtable.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
System.out.println(key + ":::" +hashtable.get(key));
}
}
2.4 Hashtable 扩容原理
/**
* Increases the capacity of and internally reorganizes this
* hashtable, in order to accommodate and access its entries more
* efficiently. This method is called automatically when the
* number of keys in the hashtable exceeds this hashtable's capacity
* and load factor.
*/
@SuppressWarnings("unchecked")
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1; // 将 容量扩容至:old * 2 + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) { // 将 map 中的值复制到新 map 里面
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
3. TreeMap
3.1 概述,API文档介绍
- 开始于JDK1.2
- 基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
- 此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。
3.2 声明和构造函数
声明:public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, Serializable
// TreeMap 构造方法摘要
TreeMap() // 使用键的自然顺序构造一个新的、空的树映射。
TreeMap(Comparator<? super K> comparator) // 构造一个新的、空的树映射,该映射根据给定比较器进行排序。
TreeMap(Map<? extends K,? extends V> m) // 构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自然顺序 进行排序。
TreeMap(SortedMap<K,? extends V> m) // 与指定有序映射具有相同映射关系和相同排序顺序的新的树映射。
3.3 TreeMap 里面一些专有方法
这些方法大都是从 Navigable 接口和 SortedMap接口中继承过来的,其中 Navigable 接口是 SortedMap 接口的拓展。具有了针对给定搜索目标返回最接近匹配项的导航方法。
K firstKey() // 返回此映射中当前第一个(最低)键。
K lastKey() // 返回映射中当前最后一个(最高)键。
K ceilingKey(K key) // 返回大于等于给定键的最小键;如果不存在这样的键,则返回 null。
K floorKey(K key) // 返回小于等于给定键的最大键;如果不存在这样的键,则返回 null。
K higherKey(K key) // 返回严格大于给定键的最小键;如果不存在这样的键,则返回 null。
K lowerKey(K key) // 返回严格小于给定键的最大键;如果不存在这样的键,则返回 null。
Map.Entry<K,V> firstEntry() // 返回一个与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null。
Map.Entry<K,V> lastEntry() // 返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null。
Map.Entry<K,V> ceilingEntry(K key) // 返回一个键-值映射关系,它与大于等于给定键的最小键关联;如果不存在这样的键,则返回 null。
Map.Entry<K,V> floorEntry(K key) // 返回一个键-值映射关系,它与小于等于给定键的最大键关联;如果不存在这样的键,则返回 null。
Map.Entry<K,V> higherEntry(K key) // 返回一个键-值映射关系,它与严格大于给定键的最小键关联;如果不存在这样的键,则返回 null。
Map.Entry<K,V> lowerEntry(K key) // 返回一个键-值映射关系,它与严格小于给定键的最大键关联;如果不存在这样的键,则返回 null。
Map.Entry<K,V> pollFirstEntry() // 移除并返回与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null。
Map.Entry<K,V> pollLastEntry() // 移除并返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null。
SortedMap<K,V> headMap(K toKey) // 返回此映射的部分视图,其键值严格小于 toKey。
SortedMap<K,V> subMap(K fromKey, K toKey) // 返回此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)。
SortedMap<K,V> tailMap(K fromKey) // 返回此映射的部分视图,其键大于等于 fromKey。
NavigableSet<K> descendingKeySet() // 返回此映射中所包含键的逆序 NavigableSet 视图。
NavigableSet<K> navigableKeySet() // 返回此映射中所包含键的 NavigableSet 视图。
NavigableMap<K,V> descendingMap() // 返回此映射中所包含映射关系的逆序视图。
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) // 返回此映射的部分视图,其键的范围从 fromKey 到 toKey。
NavigableMap<K,V> headMap(K toKey, boolean inclusive) // 返回此映射的部分视图,其键小于(或等于,如果 inclusive 为 true)toKey。
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) // 返回此映射的部分视图,其键大于(或等于,如果 inclusive 为 true)fromKey。
4. 分析
Map<K,V>:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。
此接口取代 Dictionary 类,后者完全是一个抽象类,而不是一个接口。
|--Hashtable
1. 出现版本:JDK1.0
2. 此实现是同步的
3. 默认构建空间大小为 11,加载因子 0.75
4. 容量不足的时候,扩容至原来的 2 倍加 1
5. 不支持null key 和 null value, 会报出 NullPointException
6. 支持的遍历方式: 迭代器,枚举
|--HashMap
1. 出现版本:JDK1.2
2. 此实现不是同步的
3. 默认构建空间大小为 16,加载因子 0.75
4. 容量不足的时候,扩容至原来的 2 倍
5. 支持null key 和 null value
6. 支持的遍历方式: 迭代器
|--TreeMap
1. 出现版本:JDK1.2
2. 此实现不是同步的
3. 底层实现:二叉树(红黑树)