Java Hashtable 类

2025 年 4 月 1 日 | 阅读 11 分钟

Java 中的 Hashtable 是一个数据结构,其中每个键都是唯一的,并用于存储键值对。它属于 Java.util 包,实现了 Map 接口,以便处理其包含的元素。Hashtable 的一个重要特性是它能够提供快速的查找和插入操作。

在内部,哈希表使用哈希函数来确定一个数组(称为桶或槽)的索引,每个桶标识了存储键值对的潜在位置。当我们向 Hashtable 添加一个条目时,会计算键的哈希码,并根据哈希码检索将存储条目的特定桶。如果桶中已存在条目,则会发生冲突解决过程,将新条目附加到该桶或存储在另一个位置。

在使用方面,Hashtable 就像 HashMap 一样,但它是同步的,因此是线程安全的。这意味着多个线程可以并发地操作 Hashtable 而不会导致数据损坏。另一方面,同步可能会减慢性能,因此如果不需要同步,HashMap 可能是更好的选择。

注意事项

  • Hashtable 是一个列表数组。每个列表称为一个桶。桶的位置通过调用 hashcode() 方法来确定。Hashtable 包含基于键的值。
  • Java Hashtable 类包含唯一元素。
  • Java Hashtable 类不允许 null 键或值。
  • Java Hashtable 类是同步的。
  • Hashtable 类的初始默认容量为 11,而加载因子为 0.75。

Hashtable 类声明

让我们看看 java.util.Hashtable 类的声明。

Hashtable 类参数

让我们看看 java.util.Hashtable 类的参数。

  • K:这是此映射所维护的键的类型。
  • V:这是映射值的类型。

Hashtable 的特性

键值映射: 与其他 Map 实现不同,Hashtable 存储键值对。其键值存储允许通过键直接检索值。

同步: Hashtable 是同步的,因为它具有线程安全性。多个线程可以同时访问和修改 Hashtable,而不会导致数据损坏。相反,这种同步可能会损害工作负载,尤其是在高并发应用程序中。

不允许 null 键或值: 但是,Hashtable 不允许 null 键或值。尝试放入 null 键或值将生成 NullPointerException。

Hashtable 迭代: 可以使用多种方式遍历 Hashtable 的项,例如 keySet()、values() 和 entrySet()。它们返回集合,您可以对这些集合应用各种方法,从而分别允许您遍历键、值或键值对。

性能: 在正常情况下,哈希表对于 get() 和 put() 等基本操作具有恒定时间性能。另一方面,在高并发访问下,它的性能可能会变慢,而与 HashMap 等非同步集合相比。

大小调整: HashTable 具有默认的大小调整机制,当需要存储更多元素时,它会自动增大。当元素数量达到阈值(加载因子)的范围时,HashTable 会在内部增加其容量并重新哈希元素。

枚举: Hashtable 类还实现了 Enumeration 接口,用于遍历其元素。该接口对于与旧 Java 代码的兼容性问题而存在,并且比迭代器或增强 for 循环功能要旧且性能较低。

遗留类: hashtable 是 Java 自早期版本以来就一直存在的遗留类。主要被更健壮的 HashMap 和 ConcurrentHashMap 类取代,它们执行相同的功能,但性能更好,功能更多。尽管如此,Hashtable 仍可在遗留项目或以线程安全为主要关注点的情况下找到。

Hashtable 的优点

线程安全性: Hashtable 是线程安全的,因此可以在多线程环境中安全使用。多个线程可以同时访问哈希表进行读写,而无需外部同步,这简化了并发编程过程。

一致的性能: 在常规情况下,Hastable 可以实现基本操作(如 get() 和 put())的恒定时间性能。因此,它适用于需要一致性能的任务。

自动大小调整: 哈希表足够智能,可以根据需要存储的元素数量进行自我调整。从给定的元素数量开始达到加载因子,Hashtable(在内部)会增加其容量并重新哈希元素。它确保 Hashtable 能够自动处理大量数据,而无需手动干预。

键值映射: 哈希表是一种非常适合快速存储和检索键值对的数据结构。它可以通过应用键来轻松搜索值,用于缓存、索引和数据检索等操作。

遗留支持: 哈希表已包含在 Java 早期版本的 Java 集合中。它仍然在遗留代码库中使用,以提供与旧 Java 应用程序和库互操作的方式。

枚举支持: HashTable 提供枚举功能,您可以使用 Enumeration 接口遍历其元素。虽然枚举在重复任务方面不如迭代器或增强 for 循环合适,但它们可用于与旧 Java 代码的向后兼容。

无 null 键或值: Hashtable 不支持 null 键或值。这可以防止逻辑错误和 NullPointerException。

负载下的可预测性能: 就 Hashtable 的同步功能而言,即使在大量并发访问的情况下,它也倾向于保持相当稳定。然而,与非同步集合相比,同步开销可能会对性能产生更大影响,但 Hashtable 的性能在许多用途中都是一致且足够的。

Hashtable 的缺点

同步开销: Hashtable 是同步的,这意味着一次只有一个线程可以访问它。在这里,这会引入额外的开销,并且可能会影响性能,尤其是在不需要大量并发的情况下。

性能影响: 因此,Hashtable 的操作比非同步的 HashMap 操作慢。在高并发情况下,这会加剧性能差距。

灵活性有限: HashTable 不支持 null 键和 null 值。在允许或适当使用 null 值的情况下,这种限制可能不方便。

内存使用效率低下: Hashtable 根据其初始容量和加载因子预先保留内存槽。如果容量设置得太大,可能会浪费内存空间。如果表未完全填充,这种情况更可能发生。

过时功能: Hashtable 类是 Java 语言中一种老旧的类。虽然它在许多情况下仍然有效,但像 HashMap 和 ConcurrentHashMap 这样更新的替代品提供了更好的性能、更多功能和更少的内存消耗。

枚举与迭代器: Hashtable 类提供 Enumeration 接口来遍历表中的元素。然而,枚举比迭代器或增强 for 循环更简单,但不如它们灵活和通用,而迭代器和增强 for 循环在较新的集合中可用。

Java Hashtable 类的构造函数

Hashtable 类提供了几个构造函数来创建 hashtable 的实例。每个构造函数允许我们以不同的方式初始化hashtable,根据您的需求提供灵活性。

构造函数描述
Hashtable()它创建一个空的hashtable,具有初始默认容量和加载因子。
Hashtable(int capacity)它接受一个整数参数,并创建一个包含指定初始容量的哈希表。
Hashtable(int capacity, float loadFactor)它用于创建具有指定初始容量和加载因子的哈希表。
Hashtable(Map<? extends K,? extends V> t)它创建一个新的哈希表,具有与给定 Map 相同的映射。

Java Hashtable 类的方法

方法描述
void clear()用于重置哈希表。
Object clone()返回 Hashtable 的浅表副本。
V compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)用于计算指定键及其当前映射值(如果不存在则为 null)的映射,并更新 Map。
V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)如果指定的键尚未与值关联(或映射为 null),则使用给定的映射函数计算其值,并将其输入到此 Map 中,除非结果为 null。
V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)如果指定键的值存在且非 null,则用于计算给定键及其当前映射值的新映射。
枚举elements()返回哈希表中值的枚举。
Set<Map.Entry<K,V>> entrySet()返回 Map 中包含的映射的视图。
boolean equals(Object o)用于将指定的对象与 Map 进行比较。
void forEach(BiConsumer<? super K,? super V> action)对 Map 中的每个条目执行给定的操作,直到所有条目都被处理完毕或操作抛出异常。
V getOrDefault(Object key, V defaultValue)返回指定键映射到的值,如果 Map 中没有该键的映射,则返回 defaultValue。
int hashCode()返回 Map 的哈希码值。
Enumeration<K> keys()返回哈希表中键的枚举。
Set<K> keySet()返回 Map 中包含的键的 Set 视图。
V merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction)如果指定的键尚未与值关联或与 null 关联,则将其与给定的非 null 值关联。
V put(K key, V value)将指定的键与指定的值插入哈希表中。
void putAll(Map<? extends K,? extends V> t))用于将所有键值对从 map 复制到 hashtable。
V putIfAbsent(K key, V value)如果指定的键尚未与值关联(或映射到 null),则将其与给定值关联并返回 null,否则返回当前值。
boolean remove(Object key, Object value)从哈希表中移除与指定键关联的指定值。
V replace(K key, V value)替换指定键的指定值。
boolean replace(K key, V oldValue, V newValue)为指定键替换旧值与新值。
void replaceAll(BiFunction<? super K,? super V,? extends V> function)通过调用给定的函数来替换每个条目的值,直到所有条目都被处理完毕或函数抛出异常。
String toString()返回 Hashtable 对象的字符串表示形式。
集合values()返回 Map 中包含的值的 collection 视图。
boolean contains(Object value)此方法返回哈希表中是否存在等于指定值的项,如果存在则返回 true,否则返回 false。
boolean containsValue(Object value)此方法返回哈希表中是否存在等于指定值的项,如果存在则返回 true,否则返回 false。
boolean containsKey(Object key)此方法返回哈希表中是否存在等于指定键的项,如果存在则返回 true,否则返回 false。
boolean isEmpty()此方法在哈希表为空时返回 true;如果它包含至少一个键,则返回 false。
protected void rehash()用于增加哈希表的大小并重新哈希其所有键。
V get(Object key)返回与键关联的值的对象。
V remove(Object key)用于移除键及其值。此方法返回与键关联的值。
int size()此方法返回哈希表中的条目数。

构造函数

Hashtable()

Hashtable() 构造函数初始化一个新的空hashtable。它将初始容量设置为默认值(通常为 11),并将加载因子设置为默认值 0.75。容量和加载因子是内部参数,会影响 hashtable 在添加元素时的性能和大小调整行为。

HashTableExample1.java

输出

Hashtable: {}

Hashtable(int initialCapacity)

Hashtable(int initialCapacity) 构造函数以指定的初始容量初始化一个新的空hashtable。初始容量是用于存储键值对的桶的数量,它会影响 hashtable 的性能和内存使用。默认加载因子 0.75 决定了何时应该调整 hashtable 的大小以容纳更多元素。

HashTableExample2.java

输出

Hashtable: {}

Hashtable(int initialCapacity, float loadFactor)

Hashtable(int initialCapacity, float loadFactor) 构造函数以指定的初始容量和加载因子初始化一个新的空hashtable。初始容量决定了桶的数量,而加载因子决定了何时应该调整hashtable的大小。指定自定义加载因子可以根据预期元素的数量和内存限制来影响hashtable的大小调整行为。

HashTableExample3.java

输出

Hashtable: {}

Hashtable(Map<? extends K, ? extends V> t)

Hashtable(Map<? extends K, ? extends V> t) 构造函数使用指定 Map 的相同映射初始化一个新的hashtable。它将来自指定 Map 的所有键值对复制到新的hashtable中,从而有效地克隆其内容。此构造函数提供了一种便捷的方式来使用现有 Map 中的预定义映射创建hashtable。

HashTableExample4.java

输出

Hashtable: {One=1, Three=3, Two=2}

Java Hashtable 示例

Hashtable1.java

输出

103 Rahul
102 Ravi
101 Vijay
100 Amit

Java Hashtable 示例:remove()

Hashtable2.java

输出

Before remove: {103=Rahul, 102=Ravi, 101=Vijay, 100=Amit}
After remove: {103=Rahul, 101=Vijay, 100=Amit}

Java Hashtable 示例:getOrDefault()

Hashtable3.java

输出

Vijay
Not Found

Java Hashtable 示例:putIfAbsent()

Hashtable4.java

输出

Initial Map: {103=Rahul, 102=Ravi, 101=Vijay, 100=Amit}
Updated Map: {104=Gaurav, 103=Rahul, 102=Ravi, 101=Vijay, 100=Amit}
Updated Map: {104=Gaurav, 103=Rahul, 102=Ravi, 101=Vijay, 100=Amit}

Java Hashtable 示例:Book

HashtableExample.java

输出

3 Details:
103 Operating System Galvin Wiley 6
2 Details:
102 Data Communications & Networking Forouzan Mc Graw Hill 4
1 Details:
101 Let us C Yashwant Kanetkar BPB 8