Java Map接口深度解析:HashMap实战、TreeMap与Map进阶、集合工具类与泛型进阶
·
1. Map接口概述与核心方法
Map(映射)是Java集合框架中用于存储键值对(Key-Value)的数据结构接口。与Collection接口不同,Map中的每个元素都包含一个键和一个值,键是唯一的,值可以重复。
1.1 Map接口的核心方法
import java.util.*;
public class MapBasicDemo {
public static void main(String[] args) {
// 创建Map实例(使用HashMap实现)
Map<String, Integer> studentScores = new HashMap<>();
// 1. put() - 添加键值对
studentScores.put("张三", 85);
studentScores.put("李四", 92);
studentScores.put("王五", 78);
System.out.println("添加后: " + studentScores);
// 2. get() - 根据键获取值
Integer liScore = studentScores.get("李四");
System.out.println("李四的成绩: " + liScore);
// 3. containsKey() - 检查键是否存在
boolean hasWang = studentScores.containsKey("王五");
System.out.println("是否包含王五: " + hasWang);
// 4. containsValue() - 检查值是否存在
boolean hasScore92 = studentScores.containsValue(92);
System.out.println("是否有92分: " + hasScore92);
// 5. remove() - 删除键值对
Integer removedScore = studentScores.remove("张三");
System.out.println("删除张三后: " + studentScores);
// 6. size() - 获取元素数量
System.out.println("当前元素数量: " + studentScores.size());
// 7. keySet() - 获取所有键的集合
Set<String> keys = studentScores.keySet();
System.out.println("所有键: " + keys);
// 8. values() - 获取所有值的集合
Collection<Integer> values = studentScores.values();
System.out.println("所有值: " + values);
// 9. entrySet() - 获取所有键值对的集合
Set<Map.Entry<String, Integer>> entries = studentScores.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
System.out.println("键: " + entry.getKey() + ", 值: " + entry.getValue());
}
// 10. clear() - 清空Map
studentScores.clear();
System.out.println("清空后大小: " + studentScores.size());
}
}
1.2 Map的三种遍历方式
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, String> countryCapital = new HashMap<>();
countryCapital.put("中国", "北京");
countryCapital.put("美国", "华盛顿");
countryCapital.put("日本", "东京");
countryCapital.put("英国", "伦敦");
System.out.println("=== 方式1: 使用keySet()遍历 ===");
for (String country : countryCapital.keySet()) {
String capital = countryCapital.get(country);
System.out.println(country + " -> " + capital);
}
System.out.println("\n=== 方式2: 使用entrySet()遍历(推荐)===");
for (Map.Entry<String, String> entry : countryCapital.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
System.out.println("\n=== 方式3: 使用forEach + Lambda表达式(Java 8+)===");
countryCapital.forEach((key, value) ->
System.out.println(key + " -> " + value)
);
System.out.println("\n=== 方式4: 使用迭代器 ===");
Iterator<Map.Entry<String, String>> iterator = countryCapital.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
}
2. HashMap实战详解
HashMap是基于哈希表实现的Map接口,它提供了常数时间复杂度的基本操作(get和put),但不保证元素的顺序。
2.1 HashMap的基本使用
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
public static void main(String[] args) {
// 创建HashMap
HashMap<String, Employee> employeeMap = new HashMap<>();
// 添加元素
employeeMap.put("E001", new Employee("张三", "开发部", 15000));
employeeMap.put("E002", new Employee("李四", "测试部", 12000));
employeeMap.put("E003", new Employee("王五", "产品部", 13000));
// 获取元素
Employee emp = employeeMap.get("E002");
System.out.println("E002员工信息: " + emp);
// 检查键是否存在
if (employeeMap.containsKey("E001")) {
System.out.println("E001存在");
}
// 更新元素
employeeMap.put("E001", new Employee("张三", "开发部", 16000));
System.out.println("更新后E001: " + employeeMap.get("E001"));
// 删除元素
employeeMap.remove("E003");
System.out.println("删除E003后大小: " + employeeMap.size());
// 遍历HashMap
System.out.println("\n=== 所有员工信息 ===");
for (Map.Entry<String, Employee> entry : employeeMap.entrySet()) {
System.out.println("工号: " + entry.getKey() + ", 信息: " + entry.getValue());
}
}
}
class Employee {
private String name;
private String department;
private double salary;
public Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
@Override
public String toString() {
return String.format("Employee{name='%s', department='%s', salary=%.2f}",
name, department, salary);
}
}
2.2 HashMap的哈希冲突与扩容机制
public class HashMapInternalDemo {
public static void main(String[] args) {
// 演示HashMap的初始容量和负载因子
HashMap<String, Integer> map = new HashMap<>(16, 0.75f);
// 添加16个元素,观察扩容
for (int i = 0; i < 16; i++) {
map.put("key" + i, i);
System.out.printf("添加第%d个元素,当前大小: %d%n", i + 1, map.size());
}
// 第13个元素会触发扩容(16 * 0.75 = 12)
System.out.println("\nHashMap内部结构说明:");
System.out.println("1. 初始容量: 16");
System.out.println("2. 负载因子: 0.75");
System.out.println("3. 扩容阈值: 容量 * 负载因子 = 12");
System.out.println("4. 当元素数量超过12时,HashMap会自动扩容为原来的2倍(32)");
// 演示哈希冲突
System.out.println("\n=== 哈希冲突演示 ===");
HashMap<BadHashKey, String> conflictMap = new HashMap<>();
// 创建哈希值相同的键
BadHashKey key1 = new BadHashKey(1);
BadHashKey key2 = new BadHashKey(17); // 16 + 1,哈希冲突
BadHashKey key3 = new BadHashKey(33); // 32 + 1,哈希冲突
conflictMap.put(key1, "值1");
conflictMap.put(key2, "值2");
conflictMap.put(key3, "值3");
System.out.println("冲突Map内容: " + conflictMap);
System.out.println("key1哈希值: " + key1.hashCode());
System.out.println("key2哈希值: " + key2.hashCode());
System.out.println("key3哈希值: " + key3.hashCode());
}
}
// 故意制造哈希冲突的键类
class BadHashKey {
private int id;
public BadHashKey(int id) {
this.id = id;
}
@Override
public int hashCode() {
// 所有实例返回相同的哈希值,制造严重冲突
return 1;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
BadHashKey that = (BadHashKey) obj;
return id == that.id;
}
@Override
public String toString() {
return "BadHashKey{id=" + id + "}";
}
}
2.3 HashMap的性能优化实践
import java.util.HashMap;
public class HashMapOptimization {
// 优化1: 合理设置初始容量,避免频繁扩容
public static void optimizedHashMap() {
// 已知要存储1000个元素,设置初始容量为1024(2的幂)
// 1024 * 0.75 = 768,足够存储1000个元素而不会触发扩容
HashMap<String, String> optimizedMap = new HashMap<>(1024);
for (int i = 0; i < 1000; i++) {
optimizedMap.put("key" + i, "value" + i);
}
System.out.println("优化后的Map大小: " + optimizedMap.size());
}
// 优化2: 使用合适的键对象,实现良好的hashCode()和equals()
static class OptimizedKey {
private final String id;
private final String type;
public OptimizedKey(String id, String type) {
this.id = id;
this.type = type;
}
@Override
public int hashCode() {
// 使用Objects.hash()生成复合哈希码
return 31 * id.hashCode() + type.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
OptimizedKey that = (OptimizedKey) obj;
return id.equals(that.id) && type.equals(that.type);
}
}
// 优化3: 使用computeIfAbsent()进行条件性计算
public static void computeIfAbsentDemo() {
HashMap<String, Integer> wordCount = new HashMap<>();
String text = "hello world hello java world java";
String[] words = text.split(" ");
for (String word : words) {
// 如果键不存在,使用提供的函数计算值
wordCount.computeIfAbsent(word, k -> 0);
// 递增计数
wordCount.computeIfPresent(word, (k, v) -> v + 1);
}
System.out.println("单词计数: " + wordCount);
}
// 优化4: 使用merge()进行合并操作
public static void mergeDemo() {
HashMap<String, Integer> map1 = new HashMap<>();
map1.put("A", 1);
map1.put("B", 2);
HashMap<String, Integer> map2 = new HashMap<>();
map2.put("B", 3);
map2.put("C", 4);
// 合并两个Map
map2.forEach((key, value) ->
map1.merge(key, value, Integer::sum)
);
System.out.println("合并后的Map: " + map1);
}
public static void main(String[] args) {
System.out.println("=== HashMap性能优化实践 ===");
optimizedHashMap();
computeIfAbsentDemo();
mergeDemo();
}
}
3. TreeMap与SortedMap接口
TreeMap是基于红黑树(Red-Black Tree)实现的NavigableMap,它保证了元素按照键的自然顺序或自定义比较器顺序排列。
3.1 TreeMap的基本使用
import java.util.*;
public class TreeMapDemo {
public static void main(String[] args) {
System.out.println("=== 1. 自然顺序的TreeMap ===");
TreeMap<Integer, String> naturalOrderMap = new TreeMap<>();
naturalOrderMap.put(3, "Three");
naturalOrderMap.put(1, "One");
naturalOrderMap.put(4, "Four");
naturalOrderMap.put(2, "Two");
System.out.println("自然顺序: " + naturalOrderMap);
System.out.println("第一个键: " + naturalOrderMap.firstKey());
System.out.println("最后一个键: " + naturalOrderMap.lastKey());
System.out.println("\n=== 2. 自定义比较器的TreeMap ===");
// 按字符串长度排序
TreeMap<String, Integer> lengthOrderMap = new TreeMap<>(
Comparator.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder())
);
lengthOrderMap.put("apple", 1);
lengthOrderMap.put("banana", 2);
lengthOrderMap.put("cat", 3);
lengthOrderMap.put("dog", 4);
lengthOrderMap.put("elephant", 5);
System.out.println("按长度排序: " + lengthOrderMap);
System.out.println("\n=== 3. TreeMap的范围查询 ===");
TreeMap<Integer, String> scoreMap = new TreeMap<>();
for (int i = 1; i <= 10; i++) {
scoreMap.put(i * 10, "Student" + i);
}
System.out.println("原始Map: " + scoreMap);
System.out.println("大于等于50: " + scoreMap.tailMap(50));
System.out.println("小于70: " + scoreMap.headMap(70));
System.out.println("30到80之间: " + scoreMap.subMap(30, 80));
System.out.println("\n=== 4. 导航方法演示 ===");
System.out.println("小于40的最大键: " + scoreMap.lowerKey(40));
System.out.println("小于等于40的最大键: " + scoreMap.floorKey(40));
System.out.println("大于40的最小键: " + scoreMap.higherKey(40));
System.out.println("大于等于40的最小键: " + scoreMap.ceilingKey(40));
System.out.println("\n=== 5. 逆序视图 ===");
NavigableMap<Integer, String> descendingMap = scoreMap.descendingMap();
System.out.println("逆序Map: " + descendingMap);
System.out.println("\n=== 6. 学生成绩排序示例 ===");
TreeMap<Student, Integer> studentScores = new TreeMap<>(
Comparator.comparing(Student::getScore).reversed()
.thenComparing(Student::getName)
);
studentScores.put(new Student("张三", 85), 1);
studentScores.put(new Student("李四", 92), 2);
studentScores.put(new Student("王五", 85), 3); // 同分按姓名排序
studentScores.put(new Student("赵六", 78), 4);
System.out.println("按成绩降序排列:");
studentScores.forEach((student, rank) ->
System.out.println("排名" + rank + ": " + student.getName() + " - " + student.getScore())
);
}
}
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() { return name; }
public int getScore() { return score; }
}
3.2 TreeMap与HashMap的性能对比
import java.util.*;
public class MapPerformanceComparison {
public static void performanceTest() {
final int ELEMENT_COUNT = 100000;
Random random = new Random();
// 准备测试数据
List<Integer> keys = new ArrayList<>();
List<String> values = new ArrayList<>();
for (int i = 0; i < ELEMENT_COUNT; i++) {
keys.add(random.nextInt(1000000));
values.add("Value" + i);
}
// HashMap性能测试
long hashMapStart = System.currentTimeMillis();
HashMap<Integer, String> hashMap = new HashMap<>();
for (int i = 0; i < ELEMENT_COUNT; i++) {
hashMap.put(keys.get(i), values.get(i));
}
long hashMapPutTime = System.currentTimeMillis() - hashMapStart;
long hashMapGetStart = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
hashMap.get(keys.get(random.nextInt(ELEMENT_COUNT)));
}
long hashMapGetTime = System.currentTimeMillis() - hashMapGetStart;
// TreeMap性能测试
long treeMapStart = System.currentTimeMillis();
TreeMap<Integer, String> treeMap = new TreeMap<>();
for (int i = 0; i < ELEMENT_COUNT; i++) {
treeMap.put(keys.get(i), values.get(i));
}
long treeMapPutTime = System.currentTimeMillis() - treeMapStart;
long treeMapGetStart = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
treeMap.get(keys.get(random.nextInt(ELEMENT_COUNT)));
}
long treeMapGetTime = System.currentTimeMillis() - treeMapGetStart;
// LinkedHashMap性能测试
long linkedHashMapStart = System.currentTimeMillis();
LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();
for (int i = 0; i < ELEMENT_COUNT; i++) {
linkedHashMap.put(keys.get(i), values.get(i));
}
long linkedHashMapPutTime
更多推荐


所有评论(0)