HashMap和Hashtable之间有什么区别
HashMap和Hashtable在Java中都是实现了Map接口的类,用于存储键值对的数据结构,但它们之间存在几个关键的区别:
-
线程安全:
- HashMap:是非线程安全的。如果有多个线程同时访问并修改HashMap,可能会产生并发问题(如数据不一致)。因此,在多线程环境下使用HashMap时,通常需要额外的同步措施。
- Hashtable:是线程安全的。Hashtable的所有公共方法都是同步的,这意味着在多线程环境下,Hashtable可以安全地被多个线程共享和修改。
-
性能:
- 由于HashMap是非线程安全的,它在单线程环境下的性能通常要高于Hashtable,因为它避免了同步的开销。
- Hashtable为了保证线程安全,使用了同步机制,这可能会降低其性能。
-
对null键和null值的支持:
- HashMap:允许使用null作为键(但只能有一个null键)和值(可以有多个null值)。
- Hashtable:不允许使用null作为键或值。
-
初始容量和扩容方式:
- HashMap:如果不指定初始容量,则默认容量为16,每次扩容时容量变为原来的2倍。HashMap总是使用2的幂次方作为哈希表的大小。
- Hashtable:默认初始容量为11,每次扩容时容量变为原来的2n+1(n为当前容量)。如果指定了初始容量,Hashtable会直接使用这个给定的容量。
-
失败迭代器(Fail-Fast Iterators):
- HashMap:提供了fail-fast迭代器,当在迭代过程中集合被修改(除了通过迭代器自身的
remove
方法),迭代器会快速失败并抛出ConcurrentModificationException
。 - Hashtable:不提供fail-fast迭代器,所以在迭代过程中修改集合可能不会抛出异常。
- HashMap:提供了fail-fast迭代器,当在迭代过程中集合被修改(除了通过迭代器自身的
-
遗留和现代化:
- Hashtable:是Java的遗留类,继承自
Dictionary
类,并实现了Map
接口。它是Java 1.0中引入的。 - HashMap:是Java 1.2中引入的,只实现了
Map
接口,是Map
接口的一个更现代化的实现。
- Hashtable:是Java的遗留类,继承自
总结来说,HashMap和Hashtable之间的主要区别在于线程安全、性能、对null键和null值的支持、初始容量和扩容方式、失败迭代器的行为以及它们在Java中的历史和现代化程度。根据具体的使用场景,可以选择使用HashMap或Hashtable。在多线程环境中,如果需要线程安全,则应选择Hashtable或使用Collections.synchronizedMap()方法来包装HashMap。在单线程环境中,如果对性能有要求,且不需要线程安全,则可以选择HashMap。
Hashtable使用示例
以下是一个简单的Hashtable
使用示例,该示例在Java中创建了一个Hashtable
对象,并向其中添加了一些键值对,然后展示了如何检索和删除这些键值对:
import java.util.Hashtable;
import java.util.Enumeration;
public class HashtableExample {
public static void main(String[] args) {
// 创建一个Hashtable对象
Hashtable<String, Integer> table = new Hashtable<>();
// 向Hashtable中添加键值对
table.put("Apple", 1);
table.put("Banana", 2);
table.put("Cherry", 3);
// 使用get方法根据键获取值
Integer appleCount = table.get("Apple");
System.out.println("Number of apples: " + appleCount);
// 使用containsKey方法检查Hashtable是否包含某个键
boolean hasBanana = table.containsKey("Banana");
System.out.println("Does the table contain banana? " + hasBanana);
// 使用containsValue方法检查Hashtable是否包含某个值
boolean hasValue2 = table.containsValue(2);
System.out.println("Does the table contain value 2? " + hasValue2);
// 遍历Hashtable中的所有键值对
Enumeration<String> keys = table.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Integer value = table.get(key);
System.out.println("Key: " + key + ", Value: " + value);
}
// 使用remove方法根据键删除键值对
table.remove("Banana");
// 再次遍历Hashtable以展示删除后的结果
System.out.println("Hashtable after removing 'Banana':");
keys = table.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Integer value = table.get(key);
System.out.println("Key: " + key + ", Value: " + value);
}
}
}
在这个示例中:
- 我们首先导入了
java.util.Hashtable
和java.util.Enumeration
类。 - 然后创建了一个
Hashtable
对象table
,它存储字符串类型的键和整数类型的值。 - 使用
put
方法向table
中添加了几个键值对。 - 使用
get
方法根据键检索对应的值。 - 使用
containsKey
和containsValue
方法检查table
中是否包含特定的键或值。 - 使用
keys
方法和Enumeration
来遍历table
中的所有键,并使用get
方法获取每个键对应的值。 - 最后,使用
remove
方法根据键删除一个键值对,并再次遍历table
以展示删除后的内容。
请注意,Hashtable
是线程安全的,因此可以在多线程环境中安全使用,但性能可能会受到同步操作的影响。在现代Java编程中,HashMap
通常是更常用的选择,尤其是在不需要线程安全的情况下。如果需要线程安全,可以考虑使用Collections.synchronizedMap(new HashMap<>())
来包装一个HashMap
。