说一下HashMap和HashTable的区别

详细区别

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使用了分段锁,并不对整个数据进行锁定。

相关推荐
沐知全栈开发几秒前
Memcached delete 命令详解
开发语言
yangyanping201081 分钟前
广告系统设计二之RTA系统设计
java·spring·mybatis
lly2024061 分钟前
Lua 基本语法
开发语言
刘 大 望5 分钟前
开发自定义MCP Server并部署
java·spring·ai·语言模型·aigc·信息与通信·ai编程
无籽西瓜a7 分钟前
【西瓜带你学设计模式 | 第三期-工厂方法模式】工厂方法模式——定义、实现方式、优缺点与适用场景以及注意事项
java·后端·设计模式·工厂方法模式
格林威11 分钟前
Baumer相机铝型材表面划伤长度测量:实现损伤量化评估的 5 个关键技术,附 OpenCV+Halcon 实战代码!
开发语言·人工智能·数码相机·opencv·计算机视觉·c#·工业相机
Zzxy11 分钟前
Spring Security + JWT 简单集成
java·spring boot
2401_8274999913 分钟前
python核心语法01-数据存储与运算
java·数据结构·python
Magic--15 分钟前
进程间通信(IPC):原理、场景与选型
java·服务器·数据库
AI科技星20 分钟前
基于v≡c公设的理论优化方案
c语言·开发语言·算法·机器学习·数据挖掘