hashtable和 hashmap 源码信息
数据结构 | 初始容量 | 负载因子 |
---|---|---|
hashtable | 11 | 0.75 |
hashmap | 16 | 0.75 |
- hshtable
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {
/**
* The hash table data.
*/
private transient Entry<?,?>[] table;
/**
* The total number of entries in the hash table.
*/
private transient int count;
/**
* The table is rehashed when its size exceeds this threshold. (The
* value of this field is (int)(capacity * loadFactor).)
*
* @serial
*/
private int threshold;
/**
* The load factor for the hashtable.
*
* @serial
*/
private float loadFactor;
/**
* The number of times this Hashtable has been structurally modified
* Structural modifications are those that change the number of entries in
* the Hashtable or otherwise modify its internal structure (e.g.,
* rehash). This field is used to make iterators on Collection-views of
* the Hashtable fail-fast. (See ConcurrentModificationException).
*/
private transient int modCount = 0;
/** use serialVersionUID from JDK 1.0.2 for interoperability */
@java.io.Serial
private static final long serialVersionUID = 1421746759512286392L;
/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
*
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor the load factor of the hashtable.
* @throws IllegalArgumentException if the initial capacity is less
* than zero, or if the load factor is nonpositive.
*/
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}
/**
* Constructs a new, empty hashtable with the specified initial capacity
* and default load factor (0.75).
*
* @param initialCapacity the initial capacity of the hashtable.
* @throws IllegalArgumentException if the initial capacity is less
* than zero.
*/
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
/**
* Constructs a new, empty hashtable with a default initial capacity (11)
* and load factor (0.75).
*/
public Hashtable() {
this(11, 0.75f);
}
/**
* Constructs a new hashtable with the same mappings as the given
* Map. The hashtable is created with an initial capacity sufficient to
* hold the mappings in the given Map and a default load factor (0.75).
*
* @param t the map whose mappings are to be placed in this map.
* @throws NullPointerException if the specified map is null.
* @since 1.2
*/
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}
- hashmap:最大容量 MAXIMUM_CAPACITY
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The bin count threshold for using a tree rather than list for a
* bin. Bins are converted to trees when adding an element to a
* bin with at least this many nodes. The value must be greater
* than 2 and should be at least 8 to mesh with assumptions in
* tree removal about conversion back to plain bins upon
* shrinkage.
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* The bin count threshold for untreeifying a (split) bin during a
* resize operation. Should be less than TREEIFY_THRESHOLD, and at
* most 6 to mesh with shrinkage detection under removal.
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* The smallest table capacity for which bins may be treeified.
* (Otherwise the table is resized if too many nodes in a bin.)
* Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
* between resizing and treeification thresholds.
*/
static final int MIN_TREEIFY_CAPACITY = 64;
哈希冲突
哈希冲突(Hash Collision):不同的关键字通过同一个哈希函数可能得到同一哈希地址,即 key1 ≠ key2,而 Hash(key1) = Hash(key2),这种现象称为哈希冲突。
解决方案
常用的解决哈希冲突的方式有链地址法
和开放地址法
扩容
哈希表(Hashtable)在扩容时确实会重新计算所有元素的哈希值,并将它们重新插入到新的更大的数组中。这个过程称为“重新散列”(Rehashing)。尽管如此,扩容操作不会影响原有的数据查询,原因如下:
扩容过程
-
创建新数组:
- 创建一个新的、更大的数组来存储哈希表的元素。
-
重新散列:
- 遍历当前哈希表中的所有元素。
- 计算每个元素在新数组中的新位置。
- 将元素插入到新数组中。
-
更新引用:
- 将哈希表的内部引用从旧数组切换到新数组。
不影响原有数据查询的原因
-
原子性:
- 扩容操作通常是一个原子操作,即在扩容完成之前,哈希表对外部查询是不可见的。这意味着在扩容过程中,查询操作仍然使用旧数组进行,不会受到影响。
-
线程安全:
- 在多线程环境中,哈希表的扩容操作需要确保线程安全。通常会使用锁机制来防止在扩容过程中其他线程的干扰。
-
数据一致性:
- 扩容完成后,所有元素都已经重新插入到新数组中,且哈希表的内部引用已经更新。因此,查询操作会立即使用新数组,而不会出现数据丢失或不一致的情况。
示例
假设有一个简单的哈希表,初始容量为4,存储了以下键值对:
- (1, “A”)
- (2, “B”)
- (3, “C”)
当哈希表的负载因子达到阈值(例如0.75)时,哈希表会扩容到新的容量8。扩容过程如下:
- 创建一个容量为8的新数组。
- 重新计算每个元素的哈希值并插入新数组:
- (1, “A”) -> 新位置
- (2, “B”) -> 新位置
- (3, “C”) -> 新位置
- 更新哈希表的内部引用,使其指向新数组。
扩容完成后,查询操作会立即使用新数组,而不会影响原有的数据查询。
注意事项
- 性能影响:扩容操作虽然不会影响查询结果,但会暂时增加哈希表的性能开销。因此,合理设置初始容量和负载因子可以减少扩容的频率。
- 线程安全:在多线程环境中,确保扩容操作的线程安全非常重要,通常通过锁机制来实现。