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

相关推荐
隐积3 小时前
3.1.7.Qt容器大家族(3):QVariant——万能盒子
开发语言·qt
都叫我大帅哥6 小时前
Java JJWT 完全指南:从入门到生产级实践
java
名字还没想好☜6 小时前
Next.js ‘use client‘ 到底加在哪:Server/Client Components 边界与常见报错
开发语言·前端·javascript·react·next.js
初级代码游戏7 小时前
iOS开发 Swift 速记1:变量和基本数据类型
开发语言·ios·swift
前端开发张小七7 小时前
Java 学习笔记 · 第一课:基础语法与核心概念
java·后端
酷在前行8 小时前
【R生态】PERMANOVA 进阶实战:距离选择、PERMDISP、两两比较与受限置换(保姆级教程)
开发语言·r语言
ThsPool8 小时前
【ENVI二次开发学习整理 04】ENVI功能扩展与二次开发
java·前端·javascript
cxr8288 小时前
Graphify vs GitNexus vs CodeGraph — 三工具架构深度对比
开发语言·架构·知识图谱
赤壁小虾8 小时前
【渲染流水线】[逐片元阶段]-[透明度测试]以UnityURP为例
java·前端·数据库
废弃的小码农9 小时前
功能测试--Day07--Python编程基础
开发语言·python