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

Java TreeMap与HashTable深度解析:有序映射与线程安全映射

一、TreeMap集合深度解析

1.1 TreeMap的核心特点

数据结构本质
TreeMap是基于红黑树实现的有序键值对集合,它保证了元素按照键的自然顺序或自定义顺序进行排序。
核心特性详解
  1. 有序性保证
  • 所有元素按照键的顺序排列
  • 支持自然排序和定制排序两种方式
  • 提供了一系列基于顺序的操作方法
  1. 性能特征
  • 查询、插入、删除操作的时间复杂度:O(log n)
  • 相对于HashMap,插入和删除稍慢,但有序性提供额外功能
  • 自动维护红黑树的平衡
  1. 键的要求
  • 键对象必须实现Comparable接口,或在构造时提供Comparator
  • 键不能为null(会抛出NullPointerException)
  • 值可以为null

1.2 TreeMap基本使用

① 自然排序示例
import java.util.Map;
import java.util.TreeMap;public class TreeMapNaturalOrder {public static void main(String[] args) {System.out.println("=== TreeMap自然排序演示 ===");// String类型的自然排序(按字母表顺序)Map<String, Integer> stringMap = new TreeMap<>();stringMap.put("orange", 1);stringMap.put("apple", 2);stringMap.put("pear", 3);System.out.println("String键排序: " + stringMap);// 输出: {apple=2, orange=1, pear=3}// Integer类型的自然排序(按数字升序)Map<Integer, String> integerMap = new TreeMap<>();integerMap.put(3, "val3");integerMap.put(2, "val2");integerMap.put(1, "val1");integerMap.put(5, "val5");integerMap.put(4, "val4");System.out.println("Integer键排序: " + integerMap);// 输出: {1=val1, 2=val2, 3=val3, 4=val4, 5=val5}// 验证有序性System.out.println("第一个键: " + ((TreeMap<Integer, String>) integerMap).firstKey());System.out.println("最后一个键: " + ((TreeMap<Integer, String>) integerMap).lastKey());}
}
② 自定义排序 - Comparable接口
import java.util.*;// 自定义类实现Comparable接口
class Student implements Comparable<Student> {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Student other) {// 先按年龄排序,年龄相同按姓名排序int ageCompare = Integer.compare(this.age, other.age);return ageCompare != 0 ? ageCompare : this.name.compareTo(other.name);}@Overridepublic String toString() {return "Student{name='" + name + "', age=" + age + "}";}// 必须重写equals和hashCode方法@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}public class TreeMapComparable {public static void main(String[] args) {System.out.println("=== TreeMap Comparable接口演示 ===");Map<Student, String> studentMap = new TreeMap<>();studentMap.put(new Student("Tom", 20), "计算机科学");studentMap.put(new Student("Alice", 19), "数学");studentMap.put(new Student("Bob", 20), "物理");studentMap.put(new Student("Charlie", 18), "化学");System.out.println("按年龄和姓名排序:");studentMap.forEach((student, major) -> System.out.println(student + " -> " + major));}
}
③ 自定义排序 - Comparator接口
import java.util.*;class Person {public String name;public int salary;Person(String name, int salary) {this.name = name;this.salary = salary;}@Overridepublic String toString() {return "{Person: " + name + ", salary: " + salary + "}";}
}public class TreeMapComparator {public static void main(String[] args) {System.out.println("=== TreeMap Comparator自定义排序 ===");// 使用匿名内部类定义ComparatorMap<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {@Overridepublic int compare(Person p1, Person p2) {// 按姓名排序return p1.name.compareTo(p2.name);}});map.put(new Person("Tom", 5000), 1);map.put(new Person("Bob", 6000), 2);map.put(new Person("Lily", 5500), 3);map.put(new Person("Alice", 7000), 4);System.out.println("按姓名排序:");for (Person key : map.keySet()) {System.out.println(key + " -> " + map.get(key));}// 使用Lambda表达式定义按工资排序Map<Person, Integer> salaryMap = new TreeMap<>((p1, p2) -> Integer.compare(p1.salary, p2.salary));salaryMap.put(new Person("Tom", 5000), 1);salaryMap.put(new Person("Bob", 6000), 2);salaryMap.put(new Person("Lily", 5500), 3);System.out.println("\n按工资排序:");salaryMap.forEach((person, value) -> System.out.println(person + " -> " + value));}
}

1.3 TreeMap特有方法

import java.util.TreeMap;public class TreeMapSpecialMethods {public static void main(String[] args) {System.out.println("=== TreeMap特有方法演示 ===");TreeMap<Integer, String> treeMap = new TreeMap<>();treeMap.put(1, "One");treeMap.put(3, "Three");treeMap.put(5, "Five");treeMap.put(7, "Seven");treeMap.put(9, "Nine");System.out.println("原始Map: " + treeMap);// 第一个和最后一个元素System.out.println("第一个键: " + treeMap.firstKey());System.out.println("最后一个键: " + treeMap.lastKey());// 范围查询System.out.println("小于等于4的最大键: " + treeMap.floorKey(4)); // 3System.out.println("大于等于4的最小键: " + treeMap.ceilingKey(4)); // 5// 子MapSystem.out.println("3到7的子Map: " + treeMap.subMap(3, 7));System.out.println("小于5的Map: " + treeMap.headMap(5));System.out.println("大于等于5的Map: " + treeMap.tailMap(5));// 逆序System.out.println("逆序Map: " + treeMap.descendingMap());}
}

二、HashTable集合深度解析

2.1 HashTable的核心特点

数据结构特性
  • 基于哈希表实现键值对存储
  • 线程安全:所有方法都是同步的
  • 键值限制:键和值都不能为null
  • 性能特征:由于同步开销,性能低于HashMap
HashTable 的特点
├── 数据结构
│   ├── 基于哈希表实现
│   └── 键值对存储
├── 线程安全
│   ├── 所有方法都是同步的
│   └── 适合多线程环境
├── 键和值的限制
│   ├── 键和值都不能为 null
│   └── 键必须实现 hashCode() 和 equals()
├── 性能
│   ├── 查找、插入、删除的时间复杂度为 O(1)
│   └── 由于同步机制,性能低于 HashMap
├── 初始容量和负载因子
│   ├── 默认初始容量:11
│   └── 默认负载因子:0.75
├── 扩容机制
│   ├── 当元素数量超过容量 × 负载因子时扩容
│   └── 扩容规则:新容量 = 旧容量 × 2 + 1
├── 遍历方式
│   ├── 使用 Enumeration 遍历
│   └── 使用 Iterator 遍历
├── 与 HashMap 的区别
│   ├── HashTable 线程安全,HashMap 非线程安全
│   ├── HashTable 不允许 null 键和值,HashMap 允许
│   └── HashTable 性能较低,HashMap 性能较高
└── 使用场景├── 多线程环境└── 需要线程安全的键值对存储
与HashMap的关键区别

特性

HashTable

HashMap

线程安全

是(同步方法)

null键值

不允许

允许

性能

较低

较高

继承关系

Dictionary

AbstractMap

迭代器

Enumeration

Iterator

2.2 HashTable基本操作

创建和基本操作
import java.util.Enumeration;
import java.util.Hashtable;public class HashTableBasic {public static void main(String[] args) {System.out.println("=== HashTable基本操作 ===");// 创建HashTable的不同方式Hashtable<String, Integer> hashtable1 = new Hashtable<>(); // 默认容量11,负载因子0.75Hashtable<String, Integer> hashtable2 = new Hashtable<>(16); // 指定初始容量Hashtable<String, Integer> hashtable3 = new Hashtable<>(16, 0.8f); // 指定容量和负载因子// 添加元素Hashtable<String, Integer> hashtable = new Hashtable<>();hashtable.put("Apple", 10);hashtable.put("Banana", 20);hashtable.put("Cherry", 30);System.out.println("添加元素后: " + hashtable);// 尝试添加null值 - 会抛出NullPointerExceptiontry {hashtable.put("NullKey", null); // 抛出异常} catch (NullPointerException e) {System.out.println("不能添加null值: " + e.getMessage());}try {hashtable.put(null, 100); // 抛出异常} catch (NullPointerException e) {System.out.println("不能添加null键: " + e.getMessage());}}
}
访问和删除操作
public class HashTableAccess {public static void main(String[] args) {Hashtable<String, Integer> hashtable = new Hashtable<>();hashtable.put("Apple", 10);hashtable.put("Banana", 20);hashtable.put("Cherry", 30);hashtable.put("Orange", 40);System.out.println("初始HashTable: " + hashtable);// 访问元素int bananaQuantity = hashtable.get("Banana");System.out.println("Banana数量: " + bananaQuantity);// 访问不存在的键Integer unknown = hashtable.get("Unknown");System.out.println("不存在的键返回值: " + unknown);// 删除元素Integer removed = hashtable.remove("Cherry");System.out.println("删除的元素值: " + removed);System.out.println("删除后: " + hashtable);// 检查存在性boolean hasApple = hashtable.containsKey("Apple");boolean hasValue20 = hashtable.containsValue(20);System.out.println("包含Apple键: " + hasApple);System.out.println("包含值20: " + hasValue20);// 获取大小System.out.println("HashTable大小: " + hashtable.size());}
}

2.3 HashTable遍历方式

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;public class HashTableIteration {public static void main(String[] args) {System.out.println("=== HashTable遍历方式 ===");Hashtable<String, Integer> hashtable = new Hashtable<>();hashtable.put("Apple", 10);hashtable.put("Banana", 20);hashtable.put("Cherry", 30);hashtable.put("Orange", 40);// 1. 使用Enumeration遍历键(传统方式)System.out.println("1. Enumeration遍历键:");Enumeration<String> keys = hashtable.keys();while (keys.hasMoreElements()) {String key = keys.nextElement();System.out.println("Key: " + key + ", Value: " + hashtable.get(key));}// 2. 使用Enumeration遍历值System.out.println("\n2. Enumeration遍历值:");Enumeration<Integer> values = hashtable.elements();while (values.hasMoreElements()) {System.out.println("Value: " + values.nextElement());}// 3. 使用EntrySet遍历(推荐)System.out.println("\n3. EntrySet遍历:");for (Map.Entry<String, Integer> entry : hashtable.entrySet()) {System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());}// 4. 使用KeySet遍历System.out.println("\n4. KeySet遍历:");for (String key : hashtable.keySet()) {System.out.println("Key: " + key + ", Value: " + hashtable.get(key));}// 5. 使用forEach(Java 8+)System.out.println("\n5. forEach遍历:");hashtable.forEach((key, value) -> System.out.println("Key: " + key + ", Value: " + value));}
}

2.4 HashTable线程安全演示

import java.util.Hashtable;public class HashTableThreadSafety {public static void main(String[] args) throws InterruptedException {System.out.println("=== HashTable线程安全演示 ===");Hashtable<String, Integer> sharedHashtable = new Hashtable<>();// 创建写入任务Runnable writeTask = () -> {String threadName = Thread.currentThread().getName();for (int i = 0; i < 100; i++) {String key = threadName + "-" + i;sharedHashtable.put(key, i);System.out.println(threadName + " 写入: " + key);}};// 创建读取任务Runnable readTask = () -> {String threadName = Thread.currentThread().getName();for (int i = 0; i < 50; i++) {int size = sharedHashtable.size();System.out.println(threadName + " 读取大小: " + size);try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}};// 启动多个线程Thread writer1 = new Thread(writeTask, "Writer-1");Thread writer2 = new Thread(writeTask, "Writer-2");Thread reader1 = new Thread(readTask, "Reader-1");Thread reader2 = new Thread(readTask, "Reader-2");writer1.start();writer2.start();reader1.start();reader2.start();// 等待所有线程完成writer1.join();writer2.join();reader1.join();reader2.join();System.out.println("最终HashTable大小: " + sharedHashtable.size());System.out.println("数据一致性检查完成");}
}

2.5 HashTable容量管理

public class HashTableCapacity {public static void main(String[] args) {System.out.println("=== HashTable容量管理 ===");// 创建指定容量的HashTableHashtable<String, Integer> hashtable = new Hashtable<>(5, 0.5f);System.out.println("初始容量相关信息:");System.out.println("大小: " + hashtable.size());// 注意:HashTable没有公开的capacity()方法// 添加元素直到触发扩容for (int i = 0; i < 10; i++) {hashtable.put("Key" + i, i);System.out.println("添加Key" + i + "后,大小: " + hashtable.size());}System.out.println("最终HashTable: " + hashtable);// 清空操作hashtable.clear();System.out.println("清空后大小: " + hashtable.size());System.out.println("是否为空: " + hashtable.isEmpty());}
}

三、集合选择指南

3.1 三大Map实现类对比

特性

HashMap

TreeMap

HashTable

数据结构

数组+链表/红黑树

红黑树

哈希表

排序

无序

按键排序

无序

线程安全

null键值

允许

键不能为null

都不允许

性能

O(1)平均

O(log n)

O(1)平均

使用场景

大多数场景

需要排序

多线程环境

3.2 选择策略

选择HashMap:
  • 大多数单线程场景
  • 不需要排序
  • 允许null键值
  • 追求最佳性能
选择TreeMap:
  • 需要按键排序
  • 需要范围查询等有序操作
  • 键对象有自然顺序或可比较
选择HashTable:
  • 简单的多线程场景
  • 遗留系统维护
  • 不需要null键值

3.3 现代替代方案

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;public class ModernAlternatives {public static void main(String[] args) {System.out.println("=== HashTable的现代替代方案 ===");// 1. 需要线程安全时使用ConcurrentHashMapMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();concurrentMap.put("A", 1);concurrentMap.put("B", 2);System.out.println("ConcurrentHashMap: " + concurrentMap);// 2. 使用Collections.synchronizedMap包装HashMapMap<String, Integer> synchronizedMap = Collections.synchronizedMap(new HashMap<>());synchronizedMap.put("X", 10);synchronizedMap.put("Y", 20);System.out.println("synchronizedMap: " + synchronizedMap);// 3. 需要排序时使用TreeMapMap<String, Integer> treeMap = new TreeMap<>();treeMap.put("Z", 3);treeMap.put("A", 1);treeMap.put("M", 2);System.out.println("TreeMap(自动排序): " + treeMap);}
}

总结

TreeMap和HashTable各自在特定的使用场景下发挥着重要作用:
  • TreeMap 提供了有序的键值对存储,适合需要排序和范围查询的场景
  • HashTable 提供了线程安全的键值对存储,但在现代Java开发中通常被ConcurrentHashMap替代
理解这些集合的特性和适用场景,能够帮助开发者在实际项目中做出更加合适的技术选型,编写出既高效又健壮的代码。

http://www.dtcms.com/a/577652.html

相关文章:

  • 什么是大数据,为什么它很重要?
  • asp网站配置伪静态做网站的
  • 顺序表vector--------杨辉三角
  • 阿里云 RDS PostgreSQL 可观测最佳实践
  • JVM堆的管理者——CodeCache
  • 目前哪个网站建设的最好wordpress 模板引入文件
  • Data+AI 时代,对象存储为 AI 应用注入全局动力
  • linux:io基础
  • WSL+openEuler云原生实践:Docker全流程部署与多容器编排深度评测
  • 个人笔记|单臂路由,子接口,VLAN标签
  • 罗湖商城网站设计推荐小程序服务开发公司
  • 赣州网站建设jx25网页开发用到的技术
  • 企业服务在产业平台领域的渗透率现状和发展未来
  • 【P27 回归算法及应用实践】有监督的机器学习、分类与回归、一元线性回归、最小二乘法、多元回归与梯度下降、学习率
  • Spring Boot 如何支持国际化
  • Excel斜线表头怎么做?合并单元格后添加对角线+两侧输入文字,新手也能秒会!
  • ara::core——Adaptive AUTOSAR
  • 大语言模型训推一体机:AI算力革命的“新引擎”,2031年市场规模突破123亿的黄金赛道
  • 百度网站降级的原因计算机一级考试网站怎么做
  • 复数的矩阵表示 | 欧拉恒等式的复数矩阵变换
  • Linux 系统调用在 ARM 上的实现与工作机制
  • 红松小课如何成为激活老年人生活的新引擎?从兴趣学习到价值重塑!
  • 怎么才能去定义自己的生活呢?
  • 嘉兴云建站模板重庆网站备案大厅
  • Java并发实战:ConcurrentHashMap原理与常见面试题
  • 前端FAQ: 在React中,如何优化⼤列表的渲染性能?
  • 华硕ROC奥创中心Armoury Crate服务崩溃解决办法
  • 工业软件国产替代:突破“卡脖子”,筑牢制造业升级基石
  • 大专生就业是否存在学历歧视?
  • Java 8 Stream API 进阶实战:从基础到业务落地的全解析​