HashMap
和 Hashtable
的区别
1. 线程安全性
HashMap
:是非线程安全的。这意味着在多线程环境下,如果多个线程同时对HashMap
进行读写操作,并且至少有一个线程对HashMap
进行了结构性的修改(例如添加或删除一个映射),则可能会导致数据不一致或抛出ConcurrentModificationException
异常。
import java.util.HashMap;
import java.util.Map;
public class HashMapNonThreadSafeExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
// 模拟多线程操作
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
hashMap.put("key" + i, i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
hashMap.get("key" + i);
}
});
thread1.start();
thread2.start();
}
}
Hashtable
:是线程安全的。它的大部分方法都使用了synchronized
关键字进行同步,因此在多线程环境下可以安全地进行读写操作。但这种同步机制会带来一定的性能开销。
import java.util.Hashtable;
import java.util.Map;
public class HashtableThreadSafeExample {
public static void main(String[] args) {
Map<String, Integer> hashtable = new Hashtable<>();
// 模拟多线程操作
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
hashtable.put("key" + i, i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
hashtable.get("key" + i);
}
});
thread1.start();
thread2.start();
}
}
2. 空键和空值
HashMap
:允许键和值为null
。但只能有一个键为null
,可以有多个值为null
。
import java.util.HashMap;
import java.util.Map;
public class HashMapNullExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put(null, 1);
hashMap.put("key", null);
System.out.println(hashMap);
}
}
Hashtable
:不允许键和值为null
。如果尝试将null
作为键或值插入Hashtable
中,会抛出NullPointerException
异常。
import java.util.Hashtable;
import java.util.Map;
public class HashtableNullExample {
public static void main(String[] args) {
Map<String, Integer> hashtable = new Hashtable<>();
try {
hashtable.put(null, 1);
} catch (NullPointerException e) {
System.out.println("Caught NullPointerException: " + e.getMessage());
}
}
}
3. 继承和历史原因
HashMap
:是 Java 1.2 引入的,继承自AbstractMap
类。Hashtable
:是 Java 早期的类,继承自Dictionary
类,从 Java 2 开始也实现了Map
接口。
4. 性能
HashMap
:由于是非线程安全的,没有同步开销,因此在单线程环境下性能较高。Hashtable
:由于使用了同步机制,在多线程环境下虽然安全,但性能相对较低。
多线程环境下的选择
在多线程环境下,一般不建议使用 Hashtable
,可以根据具体需求选择以下两种替代方案:
1. ConcurrentHashMap
ConcurrentHashMap
是线程安全的,并且在多线程环境下性能比 Hashtable
高很多。它采用了分段锁(Java 7 及以前)或 CAS(Compare-And-Swap,Java 8 及以后)和 synchronized 来实现并发控制,允许多个线程同时进行读写操作,而不需要对整个 Map
进行加锁。
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
// 模拟多线程操作
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
concurrentHashMap.put("key" + i, i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
concurrentHashMap.get("key" + i);
}
});
thread1.start();
thread2.start();
}
}
2. Collections.synchronizedMap()
如果需要一个线程安全的 Map
,并且对性能要求不是特别高,也可以使用 Collections.synchronizedMap()
方法将一个非线程安全的 Map
(如 HashMap
)转换为线程安全的 Map
。但这种方式的性能通常不如 ConcurrentHashMap
。
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class SynchronizedMapExample {
public static void main(String[] args) {
Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> synchronizedMap = Collections.synchronizedMap(hashMap);
// 模拟多线程操作
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
synchronizedMap.put("key" + i, i);
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
synchronizedMap.get("key" + i);
}
});
thread1.start();
thread2.start();
}
}