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

相关推荐
沐知全栈开发1 小时前
Bootstrap 多媒体对象
开发语言
PythonFun2 小时前
WPS动态序号填充,告别手动调整烦恼
java·前端·python
Hx_Ma162 小时前
测试题(二)
java·开发语言
ShineWinsu2 小时前
对于C++中list的详细介绍
开发语言·数据结构·c++·算法·面试·stl·list
tackchen2 小时前
venv-manager 管理 Conda 环境 和 Python 虚拟环境 (venv)
开发语言·python·conda
lly2024062 小时前
ASP #include 指令详解
开发语言
2501_936960362 小时前
ROS快速入门杰哥教程1-77残缺版
java·开发语言
小灵吖2 小时前
LangChain4j 记忆化(ChatMemory)
java·后端
游乐码2 小时前
c#封装成员变量和成员方法和访问修饰符
开发语言·c#