Java 实例 – HashMap遍历(一文讲透)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

前言

在 Java 开发中,HashMap 是一个高频使用的数据结构,它通过键值对(Key-Value)实现高效的数据存储与检索。然而,当需要遍历 HashMap 中的所有元素时,开发者常会面临多种遍历方式的选择,以及潜在的线程安全或性能问题。本文将以“Java 实例 – HashMap遍历”为核心,从基础概念到实战案例,系统性地讲解如何高效、安全地遍历 HashMap,并解答常见疑问。无论是编程初学者还是中级开发者,都能从中获得实用的技术洞察。


一、HashMap 的基础概念与存储原理

1.1 HashMap 的核心特性

HashMap 是 Java 集合框架中基于哈希表实现的 Key-Value 存储结构,其核心特性包括:

  • 键唯一性:每个键(Key)必须是唯一的,值(Value)可以重复。
  • 无序性:元素存储顺序不依赖插入顺序,而是由哈希算法决定。
  • 允许 null 值:可以存储一个 null 键和多个 null 值。

形象比喻
可以将 HashMap 想象为一个“智能仓库”,每个物品(Value)都被分配一个独一无二的储物柜编号(Key)。当需要查找物品时,只需通过编号快速定位,而非逐个翻找。

1.2 哈希表的工作原理

HashMap 的底层通过哈希表实现,其核心步骤如下:

  1. 哈希计算:通过 hashCode() 方法计算键的哈希值。
  2. 索引定位:将哈希值与表长取模,确定键值对在数组中的存储位置。
  3. 链表或红黑树处理冲突:当哈希冲突(不同键的哈希值相同)发生时,使用链表或红黑树(当链表长度超过阈值时)存储冲突元素。

二、HashMap 的遍历方法详解

遍历 HashMap 的核心目标是访问所有键、值或键值对。根据不同的需求,Java 提供了多种遍历方式,以下将逐一讲解。

2.1 使用迭代器(Iterator)遍历

迭代器 是一种面向对象的遍历工具,通过 keySet()entrySet() 方法配合 Iterator 实现。

2.1.1 遍历键集合(Key)

HashMap<String, Integer> map = new HashMap<>();  
map.put("Apple", 1);  
map.put("Banana", 2);  

Iterator<String> keyIterator = map.keySet().iterator();  
while (keyIterator.hasNext()) {  
    String key = keyIterator.next();  
    System.out.println("Key: " + key);  
}  

关键点:通过 keySet() 获取所有键的集合,再通过迭代器逐个访问键。

2.1.2 遍历键值对(Entry)

Iterator<Map.Entry<String, Integer>> entryIterator = map.entrySet().iterator();  
while (entryIterator.hasNext()) {  
    Map.Entry<String, Integer> entry = entryIterator.next();  
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());  
}  

优势:直接访问键值对,适合需要同时操作键和值的场景。


2.2 使用增强 for 循环(for-each)

增强 for 循环 是 Java 5 引入的简化语法,适用于遍历集合或数组。

2.2.1 遍历键集合

for (String key : map.keySet()) {  
    System.out.println("Key: " + key);  
}  

2.2.2 遍历值集合

for (Integer value : map.values()) {  
    System.out.println("Value: " + value);  
}  

2.2.3 遍历键值对

for (Map.Entry<String, Integer> entry : map.entrySet()) {  
    System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());  
}  

注意:增强 for 循环本质上是对迭代器的封装,因此同样支持安全删除元素(通过迭代器的 remove() 方法)。


2.3 使用 Java 8 的 Lambda 表达式

Java 8 引入的 Lambda 表达式Stream API 可以进一步简化代码逻辑。

map.forEach((key, value) -> {  
    System.out.println("Key: " + key + ", Value: " + value);  
});  

优势:代码简洁,适合简单的遍历和处理逻辑。


2.4 对比遍历方式的性能与适用场景

遍历方式适用场景性能特点
迭代器(entrySet)需要同时操作键和值,或需要删除元素较高,支持安全删除操作
增强 for 循环简单遍历键或值,代码可读性高中等,语法简洁
Lambda + Stream需要链式操作或并行处理可能引入额外开销,适合复杂逻辑

三、遍历 HashMap 时的注意事项

3.1 遍历时修改 HashMap 的风险

在遍历过程中直接修改 HashMap(如添加或删除元素)会导致 ConcurrentModificationException 异常。例如:

// 错误示例:遍历过程中删除键  
for (String key : map.keySet()) {  
    if (key.equals("Banana")) {  
        map.remove(key); // 抛出异常  
    }  
}  

解决方案

  • 使用迭代器的 remove() 方法:
    Iterator<String> iterator = map.keySet().iterator();  
    while (iterator.hasNext()) {  
        String key = iterator.next();  
        if (key.equals("Banana")) {  
            iterator.remove(); // 安全删除  
        }  
    }  
    
  • 在遍历前创建临时集合,避免直接修改原 Map:
    List<String> keysToRemove = new ArrayList<>();  
    for (String key : map.keySet()) {  
        if (key.equals("Banana")) {  
            keysToRemove.add(key);  
        }  
    }  
    map.keySet().removeAll(keysToRemove);  
    

3.2 线程安全问题

HashMap 是非线程安全的,若在多线程环境下遍历,可能因其他线程修改数据而引发异常。此时可考虑使用 ConcurrentHashMap 替代。


四、实战案例:用户管理系统

4.1 需求背景

假设需要实现一个用户管理系统,存储用户 ID(String)与年龄(Integer),并支持以下操作:

  1. 遍历所有用户并输出信息。
  2. 删除年龄小于 18 岁的用户。

4.2 完整代码示例

import java.util.*;  

public class UserManager {  
    private HashMap<String, Integer> userMap = new HashMap<>();  

    public void addUser(String id, Integer age) {  
        userMap.put(id, age);  
    }  

    // 使用 entrySet 迭代器遍历并删除  
    public void removeMinors() {  
        Iterator<Map.Entry<String, Integer>> iterator = userMap.entrySet().iterator();  
        while (iterator.hasNext()) {  
            Map.Entry<String, Integer> entry = iterator.next();  
            if (entry.getValue() < 18) {  
                iterator.remove();  
            }  
        }  
    }  

    // 使用 Lambda 表达式遍历输出  
    public void printAllUsers() {  
        userMap.forEach((id, age) -> {  
            System.out.println("User ID: " + id + ", Age: " + age);  
        });  
    }  

    public static void main(String[] args) {  
        UserManager userManager = new UserManager();  
        userManager.addUser("U001", 25);  
        userManager.addUser("U002", 16);  
        userManager.addUser("U003", 30);  

        System.out.println("Before removal:");  
        userManager.printAllUsers();  

        userManager.removeMinors();  

        System.out.println("\nAfter removal:");  
        userManager.printAllUsers();  
    }  
}  

4.3 运行结果

Before removal:  
User ID: U001, Age: 25  
User ID: U002, Age: 16  
User ID: U003, Age: 30  

After removal:  
User ID: U001, Age: 25  
User ID: U003, Age: 30  

五、结论

通过本文的讲解,开发者应掌握以下核心要点:

  1. HashMap 的遍历方法:迭代器、增强 for 循环、Lambda 表达式各有适用场景,需根据需求选择。
  2. 安全操作原则:遍历时修改 Map 必须通过迭代器的 remove() 方法,或使用临时集合规避异常。
  3. 性能与线程安全:在高并发场景下,优先使用 ConcurrentHashMap 替代普通 HashMap

Java 实例 – HashMap遍历 是开发中的高频操作,深入理解其原理与最佳实践,不仅能提升代码质量,还能为复杂场景(如分布式系统或大数据处理)打下坚实基础。建议读者通过本文提供的代码示例,动手实践并探索更多进阶用法。

最新发布