当前位置: 首页 > news >正文

HashMap 与 Hashtable 深度对比分析

目录

  • 第一章:基础概念对比
  • 第二章:Hash 计算方法
  • 第三章:初始容量和扩容机制
  • 第四章:线程安全性对比
  • 第五章:性能对比
  • 第六章:实际代码演示

第一章:基础概念对比

1. HashMap vs Hashtable 基本对比

特性HashMapHashtable
线程安全非线程安全线程安全
继承关系继承自 AbstractMap继承自 Dictionary
null 值允许 null 键和 null 值不允许 null 键和 null 值
迭代器Fail-fast 迭代器Enumerator 迭代器
性能性能更好性能较差(同步开销)
推荐使用推荐使用不推荐使用

2. 类结构对比

HashMap 类结构

public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {// 默认初始容量static final int DEFAULT_INITIAL_CAPACITY = 16;// 最大容量static final int MAXIMUM_CAPACITY = 1 << 30;// 默认负载因子static final float DEFAULT_LOAD_FACTOR = 0.75f;// 树化阈值static final int TREEIFY_THRESHOLD = 8;// 反树化阈值static final int UNTREEIFY_THRESHOLD = 6;// 最小树化容量static final int MIN_TREEIFY_CAPACITY = 64;
}

Hashtable 类结构

public class Hashtable<K,V> extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable {// 默认初始容量private static final int DEFAULT_INITIAL_CAPACITY = 11;// 默认负载因子private static final float DEFAULT_LOAD_FACTOR = 0.75f;// 最大容量private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}

第二章:Hash 计算方法

1. HashMap 的 Hash 计算

JDK 8 及以后版本

static final int hash(Object key) {int h;return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

Hash 计算过程

  1. 获取 key 的 hashCode
  2. 将 hashCode 右移 16 位
  3. 与原 hashCode 进行异或运算
  4. 如果 key 为 null,返回 0

示例

String key = "hello";
int hashCode = key.hashCode();        // 假设为 99162322
int hash = hashCode ^ (hashCode >>> 16); // 99162322 ^ 1512 = 99160810

JDK 7 及以前版本

static int hash(int h) {h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);
}

2. Hashtable 的 Hash 计算

Hashtable 的 Hash 计算

private int hash(Object k) {return hashSeed ^ k.hashCode();
}

Hash 计算过程

  1. 获取 key 的 hashCode
  2. 与 hashSeed 进行异或运算
  3. 如果 key 为 null,抛出 NullPointerException

示例

String key = "hello";
int hashCode = key.hashCode();        // 假设为 99162322
int hash = hashSeed ^ hashCode;      // 0 ^ 99162322 = 99162322

3. Hash 计算对比分析

特性HashMapHashtable
扰动函数使用右移和异或使用 hashSeed
null 处理返回 0抛出异常
计算复杂度简单简单
分布均匀性更好一般

第三章:初始容量和扩容机制

1. HashMap 的初始容量和扩容

初始容量

// 默认初始容量
static final int DEFAULT_INITIAL_CAPACITY = 16;// 构造函数
public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // 0.75f
}public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);
}public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " + loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);
}

容量计算

static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

扩容机制

final Node<K,V>[] resize() {Node<K,V>[] oldTab = table;int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;int newCap, newThr = 0;if (oldCap > 0) {if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // 双倍扩容}else if (oldThr > 0)newCap = oldThr;else {newCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr;Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];table = newTab;if (oldTab != null) {for (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else {Node<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);if (loTail != null) {loTail.next = null;newTab[j] = loHead;}if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}

2. Hashtable 的初始容量和扩容

初始容量

// 默认初始容量
private static final int DEFAULT_INITIAL_CAPACITY = 11;// 构造函数
public Hashtable() {this(11, 0.75f);
}public Hashtable(int initialCapacity) {this(initialCapacity, 0.75f);
}public Hashtable(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal Load: " + loadFactor);if (initialCapacity == 0)initialCapacity = 1;this.loadFactor = loadFactor;table = new Entry<?,?>[initialCapacity];threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}

扩容机制

protected void rehash() {int oldCapacity = table.length;Entry<?,?>[] oldMap = table;int newCapacity = (oldCapacity << 1) + 1;if (newCapacity - MAX_ARRAY_SIZE > 0) {if (oldCapacity == MAX_ARRAY_SIZE)return;newCapacity = MAX_ARRAY_SIZE;}Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];modCount++;threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);table = newMap;for (int i = oldCapacity; i-- > 0;) {for (Entry<K,V> old = (Entry<K,V>)oldMap[i]; old != null;) {Entry<K,V> e = old;old = old.next;int index = (e.hash & 0x7FFFFFFF) % newCapacity;e.next = (Entry<K,V>)newMap[index];newMap[index] = e;}}
}

3. 初始容量和扩容对比

特性HashMapHashtable
默认初始容量1611
容量计算2 的幂次任意正整数
扩容方式双倍扩容2n+1 扩容
扩容时机负载因子 0.75负载因子 0.75
最大容量2^30Integer.MAX_VALUE - 8

第四章:线程安全性对比

1. HashMap 的线程安全性

HashMap 是非线程安全的

// 非线程安全的操作
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);
}public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value;
}

并发问题

  1. 数据丢失:多个线程同时 put 可能导致数据丢失
  2. 死循环:JDK 7 及以前版本在扩容时可能产生死循环
  3. 数据不一致:读取到不完整的数据

2. Hashtable 的线程安全性

Hashtable 是线程安全的

public synchronized V put(K key, V value) {// 实现细节
}public synchronized V get(Object key) {// 实现细节
}public synchronized V remove(Object key) {// 实现细节
}

同步机制

  • 所有公共方法都使用 synchronized 关键字
  • 整个方法级别的同步
  • 性能较差,但保证线程安全

3. 线程安全性对比

特性HashMapHashtable
线程安全
同步方式synchronized 方法
性能
并发问题
推荐使用单线程环境多线程环境(不推荐)

第五章:性能对比

1. 性能测试代码

public class HashMapVsHashtablePerformance {private static final int TEST_SIZE = 1000000;public static void main(String[] args) {System.out.println("=== HashMap vs Hashtable 性能对比 ===\n");// 测试 HashMaptestHashMap();// 测试 HashtabletestHashtable();// 测试并发性能testConcurrentPerformance();}private static void testHashMap() {System.out.println("--- HashMap 性能测试 ---");Map<String, Integer> map = new HashMap<>();// 测试 put 操作long startTime = System.currentTimeMillis();for (int i = 0; i < TEST_SIZE; i++) {map.put("key" + i, i);}long putTime = System.currentTimeMillis() - startTime;// 测试 get 操作startTime = System.currentTimeMillis();for (int i = 0; i < TEST_SIZE; i++) {map.get("key" + i);}long getTime = System.currentTimeMillis() - startTime;System.out.println("HashMap put 操作耗时: " + putTime + "ms");System.out.println("HashMap get 操作耗时: " + getTime + "ms");System.out.println("HashMap 总耗时: " + (putTime + getTime) + "ms");System.out.println();}private static void testHashtable() {System.out.println("--- Hashtable 性能测试 ---");Map<String, Integer> map = new Hashtable<>();// 测试 put 操作long startTime = System.currentTimeMillis();for (int i = 0; i < TEST_SIZE; i++) {map.put("key" + i, i);}long putTime = System.currentTimeMillis() - startTime;// 测试 get 操作startTime = System.currentTimeMillis();for (int i = 0; i < TEST_SIZE; i++) {map.get("key" + i);}long getTime = System.currentTimeMillis() - startTime;System.out.println("Hashtable put 操作耗时: " + putTime + "ms");System.out.println("Hashtable get 操作耗时: " + getTime + "ms");System.out.println("Hashtable 总耗时: " + (putTime + getTime) + "ms");System.out.println();}private static void testConcurrentPerformance() {System.out.println("--- 并发性能测试 ---");// 测试 HashMap 并发性能testHashMapConcurrent();// 测试 Hashtable 并发性能testHashtableConcurrent();}private static void testHashMapConcurrent() {System.out.println("--- HashMap 并发测试 ---");Map<String, Integer> map = new HashMap<>();int threadCount = 10;int operationsPerThread = TEST_SIZE / threadCount;long startTime = System.currentTimeMillis();Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {final int threadId = i;threads[i] = new Thread(() -> {for (int j = 0; j < operationsPerThread; j++) {map.put("key" + (threadId * operationsPerThread + j), j);}});threads[i].start();}for (Thread thread : threads) {try {thread.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}long concurrentTime = System.currentTimeMillis() - startTime;System.out.println("HashMap 并发操作耗时: " + concurrentTime + "ms");System.out.println("HashMap 最终大小: " + map.size());System.out.println();}private static void testHashtableConcurrent() {System.out.println("--- Hashtable 并发测试 ---");Map<String, Integer> map = new Hashtable<>();int threadCount = 10;int operationsPerThread = TEST_SIZE / threadCount;long startTime = System.currentTimeMillis();Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {final int threadId = i;threads[i] = new Thread(() -> {for (int j = 0; j < operationsPerThread; j++) {map.put("key" + (threadId * operationsPerThread + j), j);}});threads[i].start();}for (Thread thread : threads) {try {thread.join();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}long concurrentTime = System.currentTimeMillis() - startTime;System.out.println("Hashtable 并发操作耗时: " + concurrentTime + "ms");System.out.println("Hashtable 最终大小: " + map.size());System.out.println();}
}

2. 性能对比总结

性能指标HashMapHashtable
单线程性能优秀良好
多线程性能差(数据不安全)良好(但性能较低)
内存使用较少较多
CPU 使用较少较多
推荐场景单线程环境多线程环境(不推荐)

第六章:实际代码演示

1. 完整的对比演示

public class HashMapHashtableDemo {public static void main(String[] args) {System.out.println("=== HashMap vs Hashtable 完整对比演示 ===\n");// 1. 基本操作对比basicOperationsComparison();// 2. null 值处理对比nullValueHandlingComparison();// 3. 线程安全性对比threadSafetyComparison();// 4. 性能对比performanceComparison();}private static void basicOperationsComparison() {System.out.println("=== 基本操作对比 ===");// HashMap 操作System.out.println("--- HashMap 操作 ---");Map<String, Integer> hashMap = new HashMap<>();hashMap.put("key1", 1);hashMap.put("key2", 2);hashMap.put("key3", 3);System.out.println("HashMap 内容: " + hashMap);System.out.println("HashMap 大小: " + hashMap.size());System.out.println("HashMap 获取 key1: " + hashMap.get("key1"));// Hashtable 操作System.out.println("\n--- Hashtable 操作 ---");Map<String, Integer> hashtable = new Hashtable<>();hashtable.put("key1", 1);hashtable.put("key2", 2);hashtable.put("key3", 3);System.out.println("Hashtable 内容: " + hashtable);System.out.println("Hashtable 大小: " + hashtable.size());System.out.println("Hashtable 获取 key1: " + hashtable.get("key1"));System.out.println();}private static void nullValueHandlingComparison() {System.out.println("=== null 值处理对比 ===");// HashMap null 值处理System.out.println("--- HashMap null 值处理 ---");Map<String, Integer> hashMap = new HashMap<>();try {hashMap.put(null, 1);hashMap.put("key", null);System.out.println("HashMap 支持 null 键和 null 值");System.out.println("HashMap null 键值: " + hashMap.get(null));System.out.println("HashMap key 值: " + hashMap.get("key"));} catch (Exception e) {System.err.println("HashMap null 值处理异常: " + e.getMessage());}// Hashtable null 值处理System.out.println("\n--- Hashtable null 值处理 ---");Map<String, Integer> hashtable = new Hashtable<>();try {hashtable.put(null, 1);System.err.println("Hashtable 不应该支持 null 键");} catch (Exception e) {System.out.println("Hashtable null 键异常: " + e.getMessage());}try {hashtable.put("key", null);System.err.println("Hashtable 不应该支持 null 值");} catch (Exception e) {System.out.println("Hashtable null 值异常: " + e.getMessage());}System.out.println();}private static void threadSafetyComparison() {System.out.println("=== 线程安全性对比 ===");// HashMap 线程安全性测试System.out.println("--- HashMap 线程安全性测试 ---");Map<String, Integer> hashMap = new HashMap<>();testConcurrentAccess(hashMap, "HashMap");// Hashtable 线程安全性测试System.out.println("\n--- Hashtable 线程安全性测试 ---");Map<String, Integer> hashtable = new Hashtable<>();testConcurrentAccess(hashtable, "Hashtable");System.out.println();}private static void testConcurrentAccess(Map<String, Integer> map, String mapType) {int threadCount = 10;int operationsPerThread = 1000;CountDownLatch latch = new CountDownLatch(threadCount);long startTime = System.currentTimeMillis();for (int i = 0; i < threadCount; i++) {final int threadId = i;new Thread(() -> {try {for (int j = 0; j < operationsPerThread; j++) {String key = "key" + (threadId * operationsPerThread + j);map.put(key, j);map.get(key);}} finally {latch.countDown();}}).start();}try {latch.await();} catch (InterruptedException e) {Thread.currentThread().interrupt();}long endTime = System.currentTimeMillis();System.out.println(mapType + " 并发操作耗时: " + (endTime - startTime) + "ms");System.out.println(mapType + " 最终大小: " + map.size());}private static void performanceComparison() {System.out.println("=== 性能对比 ===");int testSize = 100000;// HashMap 性能测试long hashMapTime = testPerformance(new HashMap<>(), testSize, "HashMap");// Hashtable 性能测试long hashtableTime = testPerformance(new Hashtable<>(), testSize, "Hashtable");System.out.println("性能对比结果:");System.out.println("HashMap 耗时: " + hashMapTime + "ms");System.out.println("Hashtable 耗时: " + hashtableTime + "ms");System.out.println("性能差异: " + (hashtableTime - hashMapTime) + "ms");System.out.println("性能提升: " + String.format("%.2f%%", (double)(hashtableTime - hashMapTime) / hashtableTime * 100));}private static long testPerformance(Map<String, Integer> map, int testSize, String mapType) {long startTime = System.currentTimeMillis();// 测试 put 操作for (int i = 0; i < testSize; i++) {map.put("key" + i, i);}// 测试 get 操作for (int i = 0; i < testSize; i++) {map.get("key" + i);}long endTime = System.currentTimeMillis();long totalTime = endTime - startTime;System.out.println(mapType + " 性能测试完成,耗时: " + totalTime + "ms");return totalTime;}
}

总结

HashMap 和 Hashtable 关键差异总结

  1. Hash 计算方法

    • HashMap:使用扰动函数 (h = key.hashCode()) ^ (h >>> 16)
    • Hashtable:使用 hashSeed ^ key.hashCode()
  2. 初始容量

    • HashMap:16(2 的幂次)
    • Hashtable:11(任意正整数)
  3. 扩容机制

    • HashMap:双倍扩容(2n)
    • Hashtable:2n+1 扩容
  4. 线程安全性

    • HashMap:非线程安全
    • Hashtable:线程安全(synchronized)
  5. null 值处理

    • HashMap:允许 null 键和 null 值
    • Hashtable:不允许 null 键和 null 值
  6. 性能

    • HashMap:性能更好
    • Hashtable:性能较差(同步开销)

推荐使用

  • 单线程环境:使用 HashMap
  • 多线程环境:使用 ConcurrentHashMap(而不是 Hashtable)
  • 不推荐使用 Hashtable(已过时)
http://www.dtcms.com/a/461253.html

相关文章:

  • 网站开始开发阶段的主要流程辽宁建设工程信息网工程业绩怎么上传
  • 缓存雪崩、击穿、穿透是什么与解决方案
  • 桌面图标又乱了?这个小神器,让你的桌面布局“一键复位”
  • mongodb慢查询优化 速度欻欻滴~
  • 从零开始的C++学习生活 6:string的入门使用
  • 风景网站模板济南seo关键词排名工具
  • UE5 测量 -1,长度测量:P2制作定位球与定位线,P3制作射线检测节点,P4在鼠标位置生成定位球
  • UE5 GAS GameAbility源码解析 EndAbility
  • 潍坊网站建设 潍坊做网站外贸网站服务器推荐
  • 第7章 n步时序差分(3) n 步离轨策略学习
  • 【Leetcode hot 100】35.搜索插入位置
  • Django ORM 字段查询表达式(Field lookup expressions)
  • 设计模式--组合模式:统一处理树形结构的优雅设计
  • 推荐算法学习笔记(十九)阿里SIM 模型
  • 高级网站开发工程师证书现代网站建设
  • 只能在线观看的电影网站咋么做wordpress教程 菜单
  • echarts画一个饼图
  • 基于改进YOLO算法的果园环境中障碍物识别与检测技术研究
  • 三元锂电池和磷酸铁锂电池:从原子晶格到应用哲学的深度解析
  • vscode-background 扩展的原理、配置和使用
  • 2100AI相亲(三)
  • 时钟服务器主地址
  • 瑞安学校网站建设口碑好网站建设价格
  • 自己做的网站访问不了建设网站哪些公司好
  • SpringMVC启动流程
  • HTTP 请求方法与参数上传形式的关系
  • 如何减少 Elasticsearch 集群中的分片数量
  • 当通过API发送请求的方式自动触发Jenkins job报错HTTP Status 403 – Forbidden的解决办法
  • 一个网站如何工作流程建立网站需要哪些手续
  • H3C网络设备 实验二:搭建两个局域网,使两个局域网相互通信(路由器,固定ip)