详细区别
1、HashTable多了elments和contains方法
2、HashTable的key和value都不能为null
3、HashTable线程安全,但是比HashMap慢,HashMap线程不安全。
4、即使这样,多线程场景我们也不用HashTable,我们用ConCuttrentHashMap
验证
HashTable的key和value都不能为null,HashMap可以
运行代码
java
public class Main {
public static void main(String[] args) {
HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", null);
System.out.println(hashMap.get("1"));
}
}
输出
out
null
运行代码
java
public class Main {
public static void main(String[] args) {
Hashtable<String, String> hashtable = new Hashtable<String, String>();
hashtable.put("1", null);
System.out.println(hashtable.get("1"));
}
}
输出(抛出了异常)
out
Exception in thread "main" java.lang.NullPointerException
at java.base/java.util.Hashtable.put(Hashtable.java:476)
at com.wuqingshun.utils.Main.main(Main.java:13)
HashMap线程不安全
运行代码
java
package com.wuqingshun.utils;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
// 全局共享的非线程安全 HashMap
private static final Map<String, Integer> unsafeHashMap = new HashMap<>();
// 每个线程插入的元素数量
private static final int ITEMS_PER_THREAD = 1000;
// 线程数量
private static final int THREAD_COUNT = 10;
// 预期总元素数量
private static final int EXPECTED_SIZE = THREAD_COUNT * ITEMS_PER_THREAD;
public static void main(String[] args) throws InterruptedException {
// 1. 创建并启动所有线程
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
int threadId = i;
threads[i] = new Thread(() -> {
// 每个线程插入 ITEMS_PER_THREAD 个唯一键值对
for (int j = 0; j < ITEMS_PER_THREAD; j++) {
// 键格式:thread-线程ID-序号,确保全局唯一
String key = "thread-" + threadId + "-" + j;
unsafeHashMap.put(key, j);
}
System.out.println("线程 " + Thread.currentThread().getName() + " 插入完成");
}, "thread-" + threadId);
threads[i].start();
}
// 2. 等待所有线程执行完毕
for (Thread thread : threads) {
thread.join();
}
// 3. 输出结果对比
int actualSize = unsafeHashMap.size();
System.out.println("=======================验证结果=======================");
System.out.println("预期 HashMap 大小:" + EXPECTED_SIZE);
System.out.println("实际 HashMap 大小:" + actualSize);
System.out.println("数据丢失数量:" + (EXPECTED_SIZE - actualSize));
System.out.println("结论:HashMap 线程不安全(实际size≠预期size)");
}
}
可能的输出
out
线程 thread-7 插入完成
线程 thread-8 插入完成
线程 thread-1 插入完成
线程 thread-0 插入完成
线程 thread-4 插入完成
线程 thread-5 插入完成
线程 thread-3 插入完成
线程 thread-2 插入完成
线程 thread-6 插入完成
线程 thread-9 插入完成
=======================验证结果=======================
预期 HashMap 大小:10000
实际 HashMap 大小:9474
数据丢失数量:526
结论:HashMap 线程不安全(实际size≠预期size)
HashTable的线程安全
运行代码
java
package com.wuqingshun.utils;
import java.util.Hashtable;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
// 全局共享的非线程安全 Hashtable
private static final Map<String, Integer> unsafeHashtable = new Hashtable<>();
// 每个线程插入的元素数量
private static final int ITEMS_PER_THREAD = 1000;
// 线程数量
private static final int THREAD_COUNT = 10;
// 预期总元素数量
private static final int EXPECTED_SIZE = THREAD_COUNT * ITEMS_PER_THREAD;
public static void main(String[] args) throws InterruptedException {
// 1. 创建并启动所有线程
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
int threadId = i;
threads[i] = new Thread(() -> {
// 每个线程插入 ITEMS_PER_THREAD 个唯一键值对
for (int j = 0; j < ITEMS_PER_THREAD; j++) {
// 键格式:thread-线程ID-序号,确保全局唯一
String key = "thread-" + threadId + "-" + j;
unsafeHashtable.put(key, j);
}
System.out.println("线程 " + Thread.currentThread().getName() + " 插入完成");
}, "thread-" + threadId);
threads[i].start();
}
// 2. 等待所有线程执行完毕
for (Thread thread : threads) {
thread.join();
}
// 3. 输出结果对比
int actualSize = unsafeHashtable.size();
System.out.println("=======================验证结果=======================");
System.out.println("预期 Hashtable 大小:" + EXPECTED_SIZE);
System.out.println("实际 Hashtable 大小:" + actualSize);
System.out.println("数据丢失数量:" + (EXPECTED_SIZE - actualSize));
System.out.println("结论:Hashtable 线程安全(实际size==预期size)");
}
}
可能输出
out
线程 thread-0 插入完成
线程 thread-2 插入完成
线程 thread-3 插入完成
线程 thread-8 插入完成
线程 thread-1 插入完成
线程 thread-6 插入完成
线程 thread-5 插入完成
线程 thread-4 插入完成
线程 thread-9 插入完成
线程 thread-7 插入完成
=======================验证结果=======================
预期 Hashtable 大小:10000
实际 Hashtable 大小:10000
数据丢失数量:0
结论:Hashtable 线程安全(实际size==预期size)
ConCurrentHashMap线程安全
运行代码
java
public class Main {
// 全局共享的非线程安全 ConcurrentHashMap
private static final Map<String, Integer> unsafeConcurrentHashMap = new ConcurrentHashMap<>();
// 每个线程插入的元素数量
private static final int ITEMS_PER_THREAD = 1000;
// 线程数量
private static final int THREAD_COUNT = 10;
// 预期总元素数量
private static final int EXPECTED_SIZE = THREAD_COUNT * ITEMS_PER_THREAD;
public static void main(String[] args) throws InterruptedException {
// 1. 创建并启动所有线程
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
int threadId = i;
threads[i] = new Thread(() -> {
// 每个线程插入 ITEMS_PER_THREAD 个唯一键值对
for (int j = 0; j < ITEMS_PER_THREAD; j++) {
// 键格式:thread-线程ID-序号,确保全局唯一
String key = "thread-" + threadId + "-" + j;
unsafeConcurrentHashMap.put(key, j);
}
System.out.println("线程 " + Thread.currentThread().getName() + " 插入完成");
}, "thread-" + threadId);
threads[i].start();
}
// 2. 等待所有线程执行完毕
for (Thread thread : threads) {
thread.join();
}
// 3. 输出结果对比
int actualSize = unsafeConcurrentHashMap.size();
System.out.println("=======================验证结果=======================");
System.out.println("预期 ConcurrentHashMap 大小:" + EXPECTED_SIZE);
System.out.println("实际 ConcurrentHashMap 大小:" + actualSize);
System.out.println("数据丢失数量:" + (EXPECTED_SIZE - actualSize));
System.out.println("结论:ConcurrentHashMap 线程安全(实际size==预期size)");
}
}
可能输出
out
线程 thread-1 插入完成
线程 thread-5 插入完成
线程 thread-9 插入完成
线程 thread-2 插入完成
线程 thread-8 插入完成
线程 thread-7 插入完成
线程 thread-3 插入完成
线程 thread-4 插入完成
线程 thread-0 插入完成
线程 thread-6 插入完成
=======================验证结果=======================
预期 ConcurrentHashMap 大小:10000
实际 ConcurrentHashMap 大小:10000
数据丢失数量:0
结论:ConcurrentHashMap 线程安全(实际size==预期size)
为什么ConCurrentHashMap比HashTable快
ConCurrentHashMap使用了分段锁,并不对整个数据进行锁定。
面试
两者的父类不同,对外提供的接口不同,对null的支持不同。
HashMap运行速度远大于HashTable,但是线程不安全。
多线程场景下,可以使用ConCurrentHashMap替代HashTable,因为ConCurrentHashMap使用了分段锁,并不对整个数据进行锁定。