0

0

Java代码重构:通过HashMap和forEach优化多列表循环判断

DDD

DDD

发布时间:2025-09-15 22:28:00

|

213人浏览过

|

来源于php中文网

原创

Java代码重构:通过HashMap和forEach优化多列表循环判断

本教程旨在解决Java程序中因使用多个列表和冗余循环进行数据查找和分配而导致的低效与维护难题。通过引入HashMap和HashSet这两种更优的数据结构,我们将展示如何将繁琐的多个for循环精简为一个简洁的forEach操作,从而显著提升代码的可读性、可维护性和执行效率,尤其适用于需要将特定值映射到不同类别的场景。

1. 问题背景与现有实现分析

在许多应用场景中,我们可能需要将一组特定的数据(如邮政编码)分配给不同的实体(如人员)。一个常见的、但效率低下的做法是为每个实体创建一个独立的列表来存储其对应的所有数据项,然后通过多个独立的循环来检查输入值是否属于某个实体的列表。

考虑以下Java代码片段,它展示了为不同人员(John, Mark, Luna)分配邮政编码的初始实现:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class ZipCodeAssigner {

    public static void main(String[] args) {
        Scanner scnr = new Scanner(System.in);

        // 为每个人员创建独立的邮政编码列表
        List pe1a = new ArrayList<>(Arrays.asList(1547, 1549)); // John的邮编
        List pe1c = new ArrayList<>(Arrays.asList(1606, 2458)); // Mark的邮编
        // 假设还有 pe1d, pe1e 等更多列表...

        System.out.print("Enter the zipcode: ");
        int zipCodeNumber = 0;
        if (scnr.hasNextInt()) {
            zipCodeNumber = scnr.nextInt();
        } else {
            System.out.println("Please enter a valid ZipCode:");
            scnr.close();
            return; // 退出程序
        }

        // 使用多个独立的循环进行查找
        for (Integer zip : pe1a) {
            if (zipCodeNumber == zip) {
                System.out.println("John");
                break; // 找到后可以提前退出
            }
        }
        for (Integer zip : pe1c) {
            if (zipCodeNumber == zip) {
                System.out.println("Mark");
                break; // 找到后可以提前退出
            }
        }
        // 假设还有针对 pe1d, pe1e 等列表的更多循环...
        scnr.close();
    }
}

这种实现方式存在以下几个主要问题:

  • 代码冗余: 每增加一个实体,就需要新增一个列表和一个相应的循环,导致大量重复代码。
  • 维护困难: 当需要修改或添加分配规则时,必须修改多个地方,容易出错。
  • 效率低下: 尽管单个列表查找效率尚可,但随着列表数量的增加,整体查找时间会线性增长。
  • 可读性差: 随着逻辑的扩展,代码变得难以理解和管理。

2. 优化方案:选择合适的数据结构

为了解决上述问题,核心在于选择一个能够更好地表示“人员-邮编集合”这种映射关系的数据结构。HashMap 是一个理想的选择,它允许我们通过一个键(Key)来快速查找对应的值(Value)。

在这里,我们可以将:

立即学习Java免费学习笔记(深入)”;

  • 键 (Key):设为人员的姓名(String 类型)。
  • 值 (Value):设为该人员所负责的所有邮政编码的集合。

对于值的集合,HashSet 是比 ArrayList 更优的选择,原因如下:

  • 查找效率: HashSet 内部基于哈希表实现,其 contains() 方法的平均时间复杂度为 O(1),这意味着无论集合中有多少个元素,查找速度都非常快。而 ArrayList 的 contains() 方法需要遍历整个列表,时间复杂度为 O(n)。
  • 唯一性: HashSet 自动保证元素的唯一性,虽然在这个特定场景下邮编通常不会重复分配给同一个人,但这是其固有的优点。

下面是使用 HashMap> 重构数据结构的代码:

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Scanner;

public class OptimizedZipCodeAssigner {

    public static void main(String[] args) {
        Scanner scnr = new Scanner(System.in);

        // 使用 HashMap 存储人员及其对应的邮政编码集合
        HashMap> zipcodeMap = new HashMap<>();

        // 初始化数据:将人员姓名作为键,其邮编集合作为值
        zipcodeMap.put("John", new HashSet<>(Arrays.asList(1547, 1549)));
        zipcodeMap.put("Mark", new HashSet<>(Arrays.asList(1606, 2458)));
        zipcodeMap.put("Luna", new HashSet<>(Arrays.asList(3058, 2214, 3895)));
        // 添加更多人员和邮编只需在此处put新的键值对即可

        System.out.print("Enter the zipcode: ");
        int zipCodeNumber = 0;
        if (scnr.hasNextInt()) {
            zipCodeNumber = scnr.nextInt();
        } else {
            System.out.println("Please enter a valid ZipCode:");
            scnr.close();
            return;
        }

        // ... (接下来的查找逻辑将在下一节介绍)
        scnr.close();
    }
}

3. 简化查找逻辑:告别冗余循环

有了 HashMap 这种高效的数据结构后,我们可以将原来多个 for 循环替换为一个简洁的 forEach 迭代,遍历 HashMap 中的每个键值对

HashMap 的 forEach 方法接受一个 BiConsumer 类型的 lambda 表达式,允许我们对每个键值对执行指定的操作。在我们的场景中,这个操作就是检查当前人员的 HashSet 中是否包含输入的邮政编码。

// ... (接续 OptimizedZipCodeAssigner 的 main 方法)

        // 使用 forEach 遍历 HashMap,查找匹配的邮编
        boolean found = false; // 用于标记是否找到匹配项
        for (java.util.Map.Entry> entry : zipcodeMap.entrySet()) {
            String personName = entry.getKey();
            HashSet zipcodes = entry.getValue();

            if (zipcodes.contains(zipCodeNumber)) {
                System.out.println(personName);
                found = true;
                // 如果只需要找到第一个匹配项就停止,可以使用Stream API的findFirst
                // 但对于所有匹配项都打印,或者需要一个标记,for-each循环或forEach是合适的
                break; // 找到一个匹配项后,如果只关心第一个,可以跳出循环
            }
        }

        if (!found) {
            System.out.println("No person assigned to this zipcode.");
        }

        // 或者使用更简洁的lambda表达式 forEach (如果不需要提前跳出循环)
        // 注意:forEach无法直接实现break,如果需要找到第一个并停止,Stream API更合适
        /*
        zipcodeMap.forEach((personName, zipcodes) -> {
            if (zipcodes.contains(zipCodeNumber)) {
                System.out.println(personName);
            }
        });
        */

// ... (main 方法结束)

在这个 for-each 循环中:

Open Voice OS
Open Voice OS

OpenVoiceOS是一个社区驱动的开源语音AI平台

下载
  1. zipcodeMap.entrySet() 返回 HashMap 中所有键值对的集合。
  2. 循环遍历每个 Entry 对象,从中获取 personName (键) 和 zipcodes (值)。
  3. zipcodes.contains(zipCodeNumber) 利用 HashSet 的高效查找特性,判断当前人员的邮编集合是否包含输入的邮编。
  4. 如果找到匹配项,则打印对应的 personName。

4. 完整示例代码

以下是整合了数据结构优化和查找逻辑简化的完整Java教程代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;

public class OptimizedZipCodeAssignerTutorial {

    public static void main(String[] args) {
        Scanner scnr = new Scanner(System.in);

        // 1. 优化数据结构:使用 HashMap 存储人员及其对应的邮政编码集合
        // 键:String (人员姓名)
        // 值:HashSet (该人员负责的所有邮政编码)
        HashMap> zipcodeAssignmentMap = new HashMap<>();

        // 初始化数据
        zipcodeAssignmentMap.put("John", new HashSet<>(Arrays.asList(1547, 1549)));
        zipcodeAssignmentMap.put("Mark", new HashSet<>(Arrays.asList(1606, 2458)));
        zipcodeAssignmentMap.put("Luna", new HashSet<>(Arrays.asList(3058, 2214, 3895)));
        // 轻松添加更多人员和邮编,无需修改核心逻辑
        zipcodeAssignmentMap.put("Alice", new HashSet<>(Arrays.asList(1001, 1002)));

        System.out.print("Enter the zipcode: ");
        int zipCodeNumber = 0;

        // 输入验证
        if (scnr.hasNextInt()) {
            zipCodeNumber = scnr.nextInt();
        } else {
            System.out.println("Invalid input. Please enter a valid integer ZipCode.");
            scnr.close();
            return;
        }

        // 2. 简化查找逻辑:告别冗余循环,使用单个循环遍历 HashMap
        boolean foundAssignment = false;

        // 遍历 HashMap 的 entrySet 来获取键值对
        for (java.util.Map.Entry> entry : zipcodeAssignmentMap.entrySet()) {
            String personName = entry.getKey();
            HashSet assignedZipcodes = entry.getValue();

            // 利用 HashSet 的 O(1) 平均时间复杂度进行高效查找
            if (assignedZipcodes.contains(zipCodeNumber)) {
                System.out.println("Assigned to: " + personName);
                foundAssignment = true;
                // 如果一个邮编只分配给一个人,找到后即可退出循环
                break; 
            }
        }

        if (!foundAssignment) {
            System.out.println("No person found for zipcode: " + zipCodeNumber);
        }

        scnr.close(); // 关闭 Scanner 资源
    }
}

5. 注意事项与最佳实践

  • 输入验证: 在实际应用中,始终要对用户输入进行严格的验证,确保其符合预期格式和业务规则。本教程示例中包含了基本的 hasNextInt() 检查。

  • 错误处理: 对于未找到匹配项的情况,应给出明确的用户反馈,而不是静默失败。

  • 性能考量: HashMap 和 HashSet 在大多数情况下提供了优异的平均性能(O(1) 查找),但在极端哈希冲突的情况下,性能可能退化到 O(n)。然而,对于整数和字符串等常见类型,这种情况很少发生。

  • 可扩展性: 这种基于 HashMap 的设计极大地提高了代码的可扩展性。未来如果需要添加新的负责人或修改邮编分配,只需修改 zipcodeAssignmentMap 的初始化部分,而无需触碰核心查找逻辑。

  • 代码可读性: 重构后的代码逻辑更清晰,意图更明确,易于理解和维护。

  • Java 8 Stream API: 对于更复杂的查找或聚合需求,Java 8 的 Stream API 提供了更强大的功能。例如,如果需要找出所有负责该邮编的人员列表,可以这样做:

    List responsiblePersons = zipcodeAssignmentMap.entrySet().stream()
        .filter(entry -> entry.getValue().contains(zipCodeNumber))
        .map(java.util.Map.Entry::getKey)
        .collect(java.util.Collectors.toList());
    
    if (!responsiblePersons.isEmpty()) {
        System.out.println("Responsible persons: " + responsiblePersons);
    } else {
        System.out.println("No person found for zipcode: " + zipCodeNumber);
    }

    这种方式在某些场景下更为函数式和简洁。

6. 总结

通过本教程,我们学习了如何将Java代码中冗余的、基于多个列表和循环的数据查找逻辑,重构为使用 HashMap 和 HashSet 的高效、简洁实现。这种优化不仅解决了代码重复、维护困难和效率低下的问题,还显著提升了代码的可读性和可扩展性。在处理类似数据映射和查找的场景时,选择合适的数据结构是构建健壮、高性能应用程序的关键。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

6

2026.01.15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 46.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号