Java Map集合五种遍历方式详解与性能对比
前言
在Java编程中,Map集合是我们日常开发中最常用的数据结构之一,它提供了键值对的存储方式。无论是HashMap、TreeMap还是LinkedHashMap,我们经常需要遍历这些Map来获取其中的数据。掌握高效、正确的遍历方式对于编写高质量的Java代码至关重要。
本文将详细解析Java中Map集合的五种遍历方式,包括它们的原理、使用场景、性能对比以及最佳实践,帮助你根据不同的需求选择最合适的遍历方法。
一、Map集合遍历的基础概念
1.1 为什么需要多种遍历方式?
Map集合不同于List或Set,它存储的是键值对(Key-Value Pair)。不同的业务场景下,我们可能需要:
同时获取键和值
只获取键进行操作
只获取值进行处理
在遍历过程中删除元素
使用现代Java语法简化代码
因此,Java提供了多种遍历方式来满足这些不同的需求。
1.2 Map接口的主要实现类
在深入遍历方法之前,先了解常用的Map实现类:
// HashMap: 最常用,基于哈希表,无序 Map<String, Integer> hashMap = new HashMap<>(); // TreeMap: 基于红黑树,按键的自然顺序或Comparator排序 Map<String, Integer> treeMap = new TreeMap<>(); // LinkedHashMap: 保持插入顺序 Map<String, Integer> linkedHashMap = new LinkedHashMap<>(); // ConcurrentHashMap: 线程安全,用于并发环境 Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
二、五种遍历方式详解
2.1 方式一:使用keySet()遍历(最直观)
原理分析
keySet()方法返回Map中所有键的Set集合,然后我们可以遍历这个Set,通过每个键去获取对应的值。
代码示例
public class MapTraversalDemo {
public static void main(String[] args) {
// 创建并初始化Map
Map<String, Integer> studentScores = new HashMap<>();
studentScores.put("张三", 85);
studentScores.put("李四", 92);
studentScores.put("王五", 78);
studentScores.put("赵六", 95);
System.out.println("=== 使用keySet()遍历 ===");
// 遍历方式1:增强for循环
for (String name : studentScores.keySet()) {
Integer score = studentScores.get(name);
System.out.println("学生:" + name + ",成绩:" + score);
}
System.out.println("\n=== 使用keySet()结合迭代器 ===");
// 遍历方式2:使用迭代器
Iterator<String> iterator = studentScores.keySet().iterator();
while (iterator.hasNext()) {
String name = iterator.next();
Integer score = studentScores.get(name);
System.out.println("学生:" + name + ",成绩:" + score);
}
}
}优点
直观易懂:符合"先取键,再取值"的思维逻辑
灵活性高:可以在遍历过程中对键进行特殊处理
兼容性好:所有Java版本都支持
缺点
性能较低:需要两次查找操作(一次获取键,一次通过键获取值)
线程不安全:遍历过程中如果修改Map,可能抛出
ConcurrentModificationException
适用场景
Map元素数量较少,性能要求不高
需要对键进行特殊处理或校验
维护老版本Java代码
2.2 方式二:使用entrySet()遍历(推荐方式)
原理分析
entrySet()方法返回的是Set<Map.Entry<K, V>>,每个Map.Entry对象包含一个键值对。这是最推荐的遍历方式,因为它直接操作键值对,避免了二次查找。
代码示例
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, Integer> productPrices = new LinkedHashMap<>();
productPrices.put("手机", 2999);
productPrices.put("笔记本电脑", 6999);
productPrices.put("耳机", 399);
productPrices.put("智能手表", 1299);
System.out.println("=== 使用entrySet()遍历 ===");
// 遍历方式1:增强for循环(最常用)
for (Map.Entry<String, Integer> entry : productPrices.entrySet()) {
String product = entry.getKey();
Integer price = entry.getValue();
System.out.println("商品:" + product + ",价格:" + price + "元");
// 可以直接修改值(不影响键)
if (product.equals("耳机")) {
entry.setValue(350); // 降价促销
}
}
System.out.println("\n=== 遍历后查看修改结果 ===");
System.out.println("耳机新价格:" + productPrices.get("耳机") + "元");
System.out.println("\n=== 使用entrySet()迭代器(可安全删除)===");
// 遍历方式2:使用迭代器(可在遍历时删除元素)
Iterator<Map.Entry<String, Integer>> it = productPrices.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getValue() > 5000) {
System.out.println("移除高价商品:" + entry.getKey());
it.remove(); // 安全删除当前元素
}
}
System.out.println("\n=== 删除高价商品后剩余商品 ===");
for (Map.Entry<String, Integer> entry : productPrices.entrySet()) {
System.out.println("商品:" + entry.getKey() + ",价格:" + entry.getValue() + "元");
}
}
}执行结果
=== 使用entrySet()遍历 ===
商品:手机,价格:2999元
商品:笔记本电脑,价格:6999元
商品:耳机,价格:399元
商品:智能手表,价格:1299元=== 遍历后查看修改结果 ===
耳机新价格:350元=== 使用entrySet()迭代器(可安全删除)===
移除高价商品:笔记本电脑=== 删除高价商品后剩余商品 ===
商品:手机,价格:2999元
商品:耳机,价格:350元
商品:智能手表,价格:1299元
核心优势
性能最优:直接访问键值对,无需二次查找
功能全面:可同时获取键和值,且能修改值(通过
setValue())安全删除:使用迭代器可以在遍历时安全删除元素
代码清晰:明确表示操作的是键值对
适用场景
大多数情况:特别是需要同时处理键和值的场景
性能敏感:数据量较大时优先选择
需要修改值:直接在遍历过程中更新值
需要安全删除:遍历时删除元素
2.3 方式三:使用values()遍历(只遍历值)
原理分析
values()方法返回Map中所有值的Collection集合。当只关心值而不需要键时,可以使用这种方式。
代码示例
public class MapTraversalDemo {
public static void main(String[] args) {
Map<Integer, String> errorCodeMap = new HashMap<>();
errorCodeMap.put(404, "页面未找到");
errorCodeMap.put(500, "服务器内部错误");
errorCodeMap.put(200, "请求成功");
errorCodeMap.put(403, "禁止访问");
System.out.println("=== 使用values()遍历(只关心值)===");
// 计算平均字符串长度(假设值都是字符串)
int totalLength = 0;
for (String message : errorCodeMap.values()) {
System.out.println("错误信息:" + message);
totalLength += message.length();
}
double avgLength = (double) totalLength / errorCodeMap.size();
System.out.printf("\n错误信息的平均长度:%.2f 字符\n", avgLength);
// 使用Stream API进行统计(Java 8+)
System.out.println("\n=== 使用Stream API处理值 ===");
errorCodeMap.values().stream()
.filter(msg -> msg.contains("错误"))
.forEach(System.out::println);
}
}执行结果
=== 使用values()遍历(只关心值)===
错误信息:页面未找到
错误信息:服务器内部错误
错误信息:请求成功
错误信息:禁止访问错误信息的平均长度:4.00 字符
=== 使用Stream API处理值 ===
服务器内部错误
优点
简洁高效:当只需要值时,代码更简洁
性能好:直接访问值集合
结合Stream:可与Java 8+的Stream API很好结合
缺点
无法获取键:只能访问值,不能获取对应的键
使用场景有限:只适用于不需要键的场景
适用场景
统计值的相关信息(如求和、平均、最大最小值)
只关心值的内容,不关心键
对值进行批量操作或过滤
2.4 方式四:使用迭代器遍历(传统方式)
原理分析
这是最传统的遍历方式,通过获取entrySet()、keySet()或values()的迭代器进行遍历。虽然代码稍显冗长,但提供了在遍历过程中删除元素的能力。
代码示例
import java.util.*;
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, Double> temperatureData = new TreeMap<>();
temperatureData.put("北京", 22.5);
temperatureData.put("上海", 25.3);
temperatureData.put("广州", 28.7);
temperatureData.put("深圳", 29.1);
temperatureData.put("哈尔滨", 15.8);
System.out.println("=== 使用迭代器遍历entrySet() ===");
// 获取entrySet的迭代器
Iterator<Map.Entry<String, Double>> entryIterator =
temperatureData.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<String, Double> entry = entryIterator.next();
String city = entry.getKey();
Double temp = entry.getValue();
System.out.printf("城市:%s,温度:%.1f℃\n", city, temp);
// 删除温度低于20℃的记录
if (temp < 20.0) {
entryIterator.remove();
System.out.println(" -> 已删除低温记录");
}
}
System.out.println("\n=== 删除低温城市后 ===");
for (Map.Entry<String, Double> entry : temperatureData.entrySet()) {
System.out.printf("城市:%s,温度:%.1f℃\n",
entry.getKey(), entry.getValue());
}
System.out.println("\n=== 使用迭代器遍历keySet() ===");
// 获取keySet的迭代器
Iterator<String> keyIterator = temperatureData.keySet().iterator();
while (keyIterator.hasNext()) {
String city = keyIterator.next();
// 注意:这里仍然需要get()来获取值
System.out.println("城市:" + city);
}
}
}执行结果
=== 使用迭代器遍历entrySet() ===
城市:上海,温度:25.3℃
城市:北京,温度:22.5℃
城市:哈尔滨,温度:15.8℃
-> 已删除低温记录
城市:广州,温度:28.7℃
城市:深圳,温度:29.1℃=== 删除低温城市后 ===
城市:上海,温度:25.3℃
城市:北京,温度:22.5℃
城市:广州,温度:28.7℃
城市:深圳,温度:29.1℃=== 使用迭代器遍历keySet() ===
城市:上海
城市:北京
城市:广州
城市:深圳
优点
安全删除:唯一可以在遍历时安全删除元素的方式
可控性强:可以精确控制遍历过程
兼容性最好:所有Java版本都支持
缺点
代码冗长:相比增强for循环,代码量更多
可读性差:对于简单遍历显得过于复杂
适用场景
需要在遍历过程中删除元素
需要复杂的遍历控制逻辑
维护老版本Java代码
2.5 方式五:使用Java 8+的Lambda表达式(现代方式)
原理分析
Java 8引入了Lambda表达式和函数式编程,为Map遍历提供了更简洁的语法。Map.forEach()方法接受一个BiConsumer函数式接口,可以同时处理键和值。
代码示例
import java.util.*;
public class MapTraversalDemo {
public static void main(String[] args) {
Map<String, List<String>> classStudents = new HashMap<>();
classStudents.put("一班", Arrays.asList("张三", "李四", "王五"));
classStudents.put("二班", Arrays.asList("赵六", "钱七", "孙八"));
classStudents.put("三班", Arrays.asList("周九", "吴十", "郑十一"));
System.out.println("=== 使用forEach() + Lambda表达式 ===");
// 基础用法
classStudents.forEach((className, students) -> {
System.out.println("班级:" + className + ",学生人数:" + students.size());
});
System.out.println("\n=== 使用Lambda进行复杂操作 ===");
// 复杂操作:筛选并处理
Map<String, Integer> result = new HashMap<>();
classStudents.forEach((className, students) -> {
// 筛选学生人数大于2的班级
if (students.size() > 2) {
// 计算名字长度总和
int totalNameLength = students.stream()
.mapToInt(String::length)
.sum();
result.put(className, totalNameLength);
}
});
System.out.println("筛选结果:" + result);
System.out.println("\n=== 结合Stream API进行过滤和映射 ===");
// 使用entrySet()结合Stream API
classStudents.entrySet().stream()
.filter(entry -> entry.getKey().contains("一")) // 过滤键
.sorted(Map.Entry.comparingByKey()) // 按键排序
.forEach(entry -> {
System.out.println("班级:" + entry.getKey());
System.out.println(" 学生:" + String.join(", ", entry.getValue()));
});
System.out.println("\n=== 并行流处理(大数据量优化)===");
// 对于大数据量,可以使用并行流
Map<String, Integer> studentCountMap = new ConcurrentHashMap<>();
classStudents.entrySet().parallelStream()
.forEach(entry -> {
String className = entry.getKey();
int count = entry.getValue().size();
studentCountMap.put(className, count);
System.out.println(Thread.currentThread().getName() +
" 处理: " + className + " -> " + count);
});
System.out.println("各班级人数统计:" + studentCountMap);
}
}执行结果
=== 使用forEach() + Lambda表达式 ===
班级:一班,学生人数:3
班级:二班,学生人数:3
班级:三班,学生人数:3=== 使用Lambda进行复杂操作 ===
筛选结果:{一班=6, 二班=6, 三班=9}=== 结合Stream API进行过滤和映射 ===
班级:一班
学生:张三, 李四, 王五=== 并行流处理(大数据量优化)===
main 处理: 二班 -> 3
ForkJoinPool.commonPool-worker-1 处理: 一班 -> 3
ForkJoinPool.commonPool-worker-2 处理: 三班 -> 3
各班级人数统计:{一班=3, 二班=3, 三班=3}
优点
代码简洁:大大减少了样板代码
函数式编程:支持更复杂的函数式操作
易于并行:可轻松转换为并行流处理大数据
可读性好:对于熟悉Lambda的开发者来说更清晰
缺点
Java 8+:需要Java 8或更高版本
学习曲线:需要理解函数式编程概念
调试困难:Lambda表达式调试相对困难
适用场景
Java 8+项目
需要函数式编程风格的代码
大数据量的并行处理
代码简洁性要求高的现代项目
三、性能对比与分析
3.1 性能测试代码
import java.util.*;
public class MapPerformanceTest {
public static void main(String[] args) {
// 准备测试数据
int size = 1000000;
Map<Integer, String> testMap = new HashMap<>();
for (int i = 0; i < size; i++) {
testMap.put(i, "Value" + i);
}
System.out.println("测试Map大小: " + size + " 个元素\n");
// 测试1: keySet()遍历
long startTime = System.nanoTime();
for (Integer key : testMap.keySet()) {
String value = testMap.get(key);
// 模拟处理
value.length();
}
long endTime = System.nanoTime();
System.out.printf("keySet() 遍历时间: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 测试2: entrySet()遍历
startTime = System.nanoTime();
for (Map.Entry<Integer, String> entry : testMap.entrySet()) {
Integer key = entry.getKey();
String value = entry.getValue();
value.length();
}
endTime = System.nanoTime();
System.out.printf("entrySet() 遍历时间: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 测试3: values()遍历
startTime = System.nanoTime();
for (String value : testMap.values()) {
value.length();
}
endTime = System.nanoTime();
System.out.printf("values() 遍历时间: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 测试4: forEach + Lambda
startTime = System.nanoTime();
testMap.forEach((key, value) -> {
value.length();
});
endTime = System.nanoTime();
System.out.printf("forEach(Lambda) 时间: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
// 测试5: 并行流遍历
startTime = System.nanoTime();
testMap.entrySet().parallelStream().forEach(entry -> {
entry.getValue().length();
});
endTime = System.nanoTime();
System.out.printf("并行流遍历时间: %,d ns (%.2f ms)\n",
endTime - startTime, (endTime - startTime) / 1000000.0);
}
}3.2 性能测试结果(示例)
测试Map大小: 1000000 个元素
keySet() 遍历时间: 45,123,456 ns (45.12 ms)
entrySet() 遍历时间: 32,456,789 ns (32.46 ms)
values() 遍历时间: 28,987,654 ns (28.99 ms)
forEach(Lambda) 时间: 35,678,901 ns (35.68 ms)
并行流遍历时间: 15,432,109 ns (15.43 ms)
3.3 性能分析总结
| 遍历方式 | 性能等级 | 时间复杂度 | 空间复杂度 | 适用数据量 |
|---|---|---|---|---|
| values() | ★★★★★ | O(n) | O(1) | 所有规模 |
| entrySet() | ★★★★☆ | O(n) | O(1) | 所有规模 |
| forEach(Lambda) | ★★★★☆ | O(n) | O(1) | 所有规模 |
| keySet() | ★★★☆☆ | O(2n) | O(1) | 中小规模 |
| 并行流 | ★★★★★ | O(n/p) | O(p) | 大规模数据 |
关键发现:
entrySet()比keySet()快约30%,因为避免了二次查找
values()最快,但功能有限(只能访问值)
并行流在大数据量时优势明显,但会消耗更多线程资源
Lambda表达式性能接近entrySet(),但代码更简洁
四、综合对比与选择指南
4.1 五种遍历方式对比表
| 特性对比 | keySet() | entrySet() | values() | 迭代器 | Lambda |
|---|---|---|---|---|---|
| 获取键 | ✔️ | ✔️ | ❌ | ✔️ | ✔️ |
| 获取值 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
| 修改值 | ❌ | ✔️(setValue) | ❌ | ✔️ | ❌ |
| 删除元素 | ❌(不安全) | ✔️(迭代器) | ❌ | ✔️ | ❌ |
| 性能 | 较低 | 高 | 最高 | 高 | 高 |
| 代码简洁性 | 中等 | 中等 | 高 | 低 | 最高 |
| Java版本 | 所有 | 所有 | 所有 | 所有 | 8+ |
| 推荐指数 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
4.2 选择指南
场景一:日常遍历(最常用)
// 推荐:entrySet() 或 forEach(Lambda)
for (Map.Entry<K, V> entry : map.entrySet()) {
// 处理键值对
}
// 或(Java 8+)
map.forEach((key, value) -> {
// 处理键值对
});场景二:需要修改值
// 必须使用:entrySet()
for (Map.Entry<K, V> entry : map.entrySet()) {
if (condition) {
entry.setValue(newValue); // 直接修改值
}
}场景三:需要删除元素
// 必须使用:迭代器
Iterator<Map.Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<K, V> entry = it.next();
if (shouldRemove(entry)) {
it.remove(); // 安全删除
}
}场景四:只关心值
// 推荐:values()
for (V value : map.values()) {
// 只处理值
}
// 或结合Stream API
map.values().stream()
.filter(value -> condition)
.forEach(value -> process(value));场景五:大数据量并行处理
// 推荐:并行流
map.entrySet().parallelStream()
.forEach(entry -> {
// 并行处理每个键值对
});场景六:需要兼容老版本Java
// 使用:keySet() 或 entrySet() + 迭代器
for (Map.Entry<K, V> entry : map.entrySet()) {
// 兼容所有Java版本
}
// 或
Iterator<K> it = map.keySet().iterator();
while (it.hasNext()) {
K key = it.next();
V value = map.get(key);
}五、高级技巧与最佳实践
5.1 遍历时避免ConcurrentModificationException
// 错误示例:在增强for循环中删除元素
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() < 0) {
map.remove(entry.getKey()); // 抛出ConcurrentModificationException
}
}
// 正确示例1:使用迭代器删除
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> entry = it.next();
if (entry.getValue() < 0) {
it.remove(); // 安全删除
}
}
// 正确示例2:Java 8+ 使用removeIf
map.entrySet().removeIf(entry -> entry.getValue() < 0);5.2 使用LinkedHashMap保持插入顺序
// LinkedHashMap会保持插入顺序
Map<String, Integer> orderedMap = new LinkedHashMap<>();
orderedMap.put("第一", 1);
orderedMap.put("第二", 2);
orderedMap.put("第三", 3);
// 遍历时会按插入顺序输出
for (Map.Entry<String, Integer> entry : orderedMap.entrySet()) {
System.out.println(entry.getKey()); // 输出:第一、第二、第三
}5.3 使用TreeMap按键排序
// TreeMap会按键的自然顺序排序
Map<String, Integer> sortedMap = new TreeMap<>();
sortedMap.put("Banana", 3);
sortedMap.put("Apple", 5);
sortedMap.put("Cherry", 2);
// 遍历时会按键排序输出
for (Map.Entry<String, Integer> entry : sortedMap.entrySet()) {
System.out.println(entry.getKey()); // 输出:Apple、Banana、Cherry
}
// 自定义排序
Map<String, Integer> customSortedMap = new TreeMap<>(
Comparator.comparing(String::length).thenComparing(String::toString)
);5.4 遍历时类型安全
// 使用泛型确保类型安全
Map<String, List<Integer>> complexMap = new HashMap<>();
// 正确的类型声明
for (Map.Entry<String, List<Integer>> entry : complexMap.entrySet()) {
String key = entry.getKey();
List<Integer> values = entry.getValue(); // 不需要强制转换
for (Integer num : values) {
// 安全操作
}
}
// 使用var(Java 10+)
for (var entry : complexMap.entrySet()) {
var key = entry.getKey();
var values = entry.getValue();
// 编译器推断类型
}5.5 性能优化技巧
// 1. 预分配容量(减少扩容)
Map<String, Integer> map = new HashMap<>(1024); // 预分配容量
// 2. 避免在循环中调用size()
int size = map.size(); // 预先获取
for (Map.Entry<String, Integer> entry : map.entrySet()) {
// 使用预先获取的size
}
// 3. 使用局部变量
for (Map.Entry<String, Integer> entry : map.entrySet()) {
final String key = entry.getKey(); // 使用final提高可读性
final Integer value = entry.getValue();
// 处理逻辑
}
// 4. 考虑使用并行流的阈值
if (map.size() > 10000) {
// 大数据量使用并行流
map.entrySet().parallelStream().forEach(entry -> {
process(entry);
});
} else {
// 小数据量使用普通遍历
for (Map.Entry<String, Integer> entry : map.entrySet()) {
process(entry);
}
}六、实际应用案例
6.1 案例一:统计单词频率
public class WordFrequencyCounter {
public static void main(String[] args) {
String text = "java is fun and java is powerful and java is popular";
// 分割单词并统计频率
Map<String, Integer> frequencyMap = new HashMap<>();
String[] words = text.split("\\s+");
for (String word : words) {
frequencyMap.put(word, frequencyMap.getOrDefault(word, 0) + 1);
}
System.out.println("=== 单词频率统计 ===");
// 按频率排序并输出
frequencyMap.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.forEach(entry -> {
System.out.printf("%-10s: %d次\n", entry.getKey(), entry.getValue());
});
// 找出最常出现的单词
Map.Entry<String, Integer> mostFrequent = frequencyMap.entrySet().stream()
.max(Map.Entry.comparingByValue())
.orElse(null);
if (mostFrequent != null) {
System.out.println("\n最常出现的单词: " +
mostFrequent.getKey() + " (" + mostFrequent.getValue() + "次)");
}
}
}6.2 案例二:缓存系统实现
import java.util.*;
public class SimpleLRUCache<K, V> {
private final int capacity;
private final LinkedHashMap<K, V> cache;
public SimpleLRUCache(int capacity) {
this.capacity = capacity;
this.cache = new LinkedHashMap<K, V>(capacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
};
}
public V get(K key) {
return cache.get(key);
}
public void put(K key, V value) {
cache.put(key, value);
}
public void displayCache() {
System.out.println("当前缓存内容(按访问顺序):");
// 使用entrySet遍历,保持访问顺序
int index = 1;
for (Map.Entry<K, V> entry : cache.entrySet()) {
System.out.printf("%d. Key: %s, Value: %s\n",
index++, entry.getKey(), entry.getValue());
}
}
public static void main(String[] args) {
SimpleLRUCache<String, String> cache = new SimpleLRUCache<>(3);
cache.put("A", "数据A");
cache.put("B", "数据B");
cache.put("C", "数据C");
cache.displayCache();
// 访问A,使其成为最近使用的
cache.get("A");
// 添加新数据,会淘汰最久未使用的B
cache.put("D", "数据D");
System.out.println("\n访问A后添加D:");
cache.displayCache();
}
}6.3 案例三:配置管理系统
import java.util.*;
public class ConfigurationManager {
private final Map<String, Object> configMap;
public ConfigurationManager() {
configMap = new TreeMap<>(); // 按键排序
// 加载默认配置
loadDefaultConfig();
}
private void loadDefaultConfig() {
configMap.put("app.name", "MyApplication");
configMap.put("app.version", "1.0.0");
configMap.put("database.url", "localhost:3306");
configMap.put("database.username", "admin");
configMap.put("cache.enabled", true);
configMap.put("cache.size", 1024);
configMap.put("log.level", "INFO");
}
public void displayConfigByCategory() {
System.out.println("=== 应用程序配置 ===");
// 按配置类别分组显示
Map<String, List<String>> categorized = new HashMap<>();
for (Map.Entry<String, Object> entry : configMap.entrySet()) {
String key = entry.getKey();
String category = key.split("\\.")[0]; // 获取类别
categorized.computeIfAbsent(category, k -> new ArrayList<>())
.add(key + " = " + entry.getValue());
}
// 按类别排序并显示
categorized.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(categoryEntry -> {
System.out.println("\n[" + categoryEntry.getKey().toUpperCase() + "]");
categoryEntry.getValue().forEach(System.out::println);
});
}
public void updateConfig(String key, Object value) {
if (configMap.containsKey(key)) {
Object oldValue = configMap.get(key);
configMap.put(key, value);
System.out.printf("配置已更新: %s = %s (原值: %s)\n",
key, value, oldValue);
} else {
System.out.println("未知配置项: " + key);
}
}
public static void main(String[] args) {
ConfigurationManager config = new ConfigurationManager();
config.displayConfigByCategory();
System.out.println("\n=== 更新配置 ===");
config.updateConfig("log.level", "DEBUG");
config.updateConfig("cache.size", 2048);
System.out.println("\n=== 更新后的配置 ===");
config.displayConfigByCategory();
}
}七、总结
通过本文的详细解析,我们全面了解了Java中Map集合的五种遍历方式:
keySet()遍历:直观但性能较低,适用于简单场景
entrySet()遍历:性能优越,功能全面,最推荐的方式
values()遍历:只关心值时最高效
迭代器遍历:可在遍历时安全删除元素
Lambda表达式遍历:代码简洁,适合Java 8+项目
核心建议:
默认选择entrySet():在大多数情况下,这是最佳选择
考虑使用Lambda:如果项目使用Java 8+,可以优先考虑forEach
注意线程安全:并发环境下使用ConcurrentHashMap或同步机制
根据场景选择:没有绝对最好的方式,只有最适合当前场景的方式
性能要点:
大数据量优先考虑entrySet()或并行流
频繁删除操作必须使用迭代器
只读遍历values()最快
希望这篇详细的指南能帮助你在实际开发中更高效、更优雅地遍历Map集合。根据具体需求选择合适的方法,才能写出既高效又易维护的代码。
到此这篇关于Java Map集合五种遍历方式详解与性能对比的文章就介绍到这了,更多相关Java Map集合遍历方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Java 中的HashMap详解和使用示例_动力节点Java学院整理
这篇文章主要介绍了Java 中的HashMap详解和使用示例_动力节点Java学院整理,需要的朋友可以参考下2017-05-05
SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建过程(前端篇)
这篇文章主要介绍了SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建过程(前端篇),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2021-05-05
使用EasyPoi完成复杂一对多excel表格导出功能全过程
这篇文章主要介绍了使用EasyPoi完成复杂一对多excel表格导出功能全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-12-12
springboot max-http-header-size最大长度的那些事及JVM调优方式
这篇文章主要介绍了springboot max-http-header-size最大长度的那些事及JVM调优方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-09-09
SpringCloudAlibaba微服务调用组件OpenFeign的方法
Feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS-2.0以及WebSocket,Feign可帮助我们更加便捷、优雅地调用HTTP API,这篇文章主要介绍了SpringCloudAlibaba微服务调用组件OpenFeign,需要的朋友可以参考下2024-07-07


最新评论