0

0

Java 中高效比对两个列表:库存检查与购物清单管理

霞舞

霞舞

发布时间:2025-08-12 17:08:15

|

952人浏览过

|

来源于php中文网

原创

Java 中高效比对两个列表:库存检查与购物清单管理

本教程旨在解决在Java中比对两个ArrayList,以检查一个列表(如购物清单)中的所有元素是否都存在于另一个列表(如库存)中的常见问题。文章将深入探讨传统线性搜索的局限性,并重点介绍如何利用HashSet的快速查找特性,以O(1)的平均时间复杂度实现高效且准确的元素存在性检查,同时提供完整的代码示例和最佳实践。

理解问题:清单比对与库存检查

在许多应用场景中,我们需要比对两个集合的数据。例如,一个常见的任务是检查用户提交的“购物清单”中的所有商品是否都已存在于“库存清单”中。如果存在缺失项,则需要明确指出哪些商品需要额外采购。

这个问题的核心在于:对于购物清单中的每一个商品,我们都需要快速判断它是否在库存清单中。

常见误区与低效方案

初学者在处理此类问题时,常会遇到以下误区和低效方案:

  1. 直接比较列表对象:

    if (pantry == input) { // 错误!
        // ...
    }

    == 运算符在Java中用于比较对象的引用地址,而不是它们包含的内容。因此,pantry == input 永远不会为真,除非它们是同一个对象。即使使用 pantry.equals(input),也只是比较两个列表是否完全相同(顺序和内容都一致),而不是检查一个列表的元素是否包含在另一个列表中。

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

  2. 嵌套循环进行线性搜索: 一个常见的思路是使用嵌套循环:

    // 假设要检查 input 中的每个元素是否在 pantry 中
    for (String itemNeeded : input) {
        boolean found = false;
        for (String itemInPantry : pantry) {
            if (itemNeeded.equals(itemInPantry)) {
                found = true;
                break;
            }
        }
        if (!found) {
            System.out.println("你还缺少: " + itemNeeded);
        }
    }

    这种方法虽然逻辑正确,但效率较低。对于每个需要检查的元素,它都需要遍历整个库存列表。如果购物清单有 M 个元素,库存清单有 N 个元素,那么最坏情况下的时间复杂度将是 O(M * N)。当列表非常大时,这种性能瓶颈会非常明显。

高效解决方案:利用 HashSet 进行快速查找

为了高效地解决上述问题,我们可以利用 java.util.Set 接口及其实现类 java.util.HashSet。HashSet 的核心优势在于其基于哈希表的实现,允许在平均 O(1) 的时间复杂度内执行元素的添加、删除和查找(contains() 方法)。

基本思路:

Text-To-Song
Text-To-Song

免费的实时语音转换器和调制器

下载
  1. 将“库存清单”(pantry)转换为一个 HashSet。由于 HashSet 内部使用哈希表,它可以提供非常快速的元素查找能力。
  2. 遍历“购物清单”(input)中的每一个元素。
  3. 对于购物清单中的每个元素,使用 HashSet 的 contains() 方法来检查它是否存在于库存中。

代码示例:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.Set;

public class ShoppingListChecker {

    /**
     * 检查购物清单中的所有物品是否都在库存中。
     * 如果有缺失,则返回缺失物品的列表;否则返回空列表。
     *
     * @param pantryList 预设的库存物品列表
     * @param ingredientList 用户输入的购物清单
     * @return 缺失物品的列表
     */
    public static List<String> checkMissingItems(List<String> pantryList, List<String> ingredientList) {
        // 将库存列表转换为 HashSet,以便进行 O(1) 平均时间复杂度的查找
        Set<String> pantrySet = new HashSet<>(pantryList);
        List<String> missingItems = new ArrayList<>();

        // 遍历购物清单,检查每个物品是否在库存中
        for (String ingredient : ingredientList) {
            // 使用 contains() 方法进行快速查找
            if (!pantrySet.contains(ingredient)) {
                missingItems.add(ingredient); // 如果不在库存中,则添加到缺失列表
            }
        }
        return missingItems;
    }

    /**
     * 接收用户输入的多个食材,并将其添加到列表中。
     *
     * @param scanner 用于读取用户输入的Scanner对象
     * @param numItemsToEnter 期望用户输入的物品数量
     * @return 用户输入的食材列表
     */
    public static List<String> getUserIngredients(Scanner scanner, int numItemsToEnter) {
        List<String> ingredients = new ArrayList<>();
        System.out.println("请逐一输入您的购物清单物品(共 " + numItemsToEnter + " 项):");
        for (int i = 0; i < numItemsToEnter; i++) {
            System.out.print("请输入第 " + (i + 1) + " 项物品: ");
            String item = scanner.nextLine().trim(); // 读取输入并去除首尾空格
            if (!item.isEmpty()) { // 确保输入不为空
                ingredients.add(item);
            } else {
                System.out.println("输入不能为空,请重新输入。");
                i--; // 重新输入当前项
            }
        }
        return ingredients;
    }

    public static void main(String[] args) {
        // 1. 创建预设的库存物品列表
        List<String> pantry = new ArrayList<>();
        pantry.add("Bread");
        pantry.add("Peanut Butter");
        pantry.add("Chips");
        pantry.add("Jelly");
        pantry.add("Milk");
        pantry.add("Eggs");
        System.out.println("当前库存: " + pantry);

        Scanner ingredientScan = new Scanner(System.in);

        // 2. 接收用户输入的购物清单
        // 假设用户需要输入3项物品,可以根据实际需求调整
        List<String> inputIngredients = getUserIngredients(ingredientScan, 3);
        System.out.println("您的购物清单: " + inputIngredients);

        // 3. 执行检查并打印结果
        List<String> missingItems = checkMissingItems(pantry, inputIngredients);

        if (missingItems.isEmpty()) {
            System.out.println("你拥有所需的一切!");
        } else {
            System.out.println("你仍然需要以下物品: " + missingItems);
        }

        ingredientScan.close();
    }
}

完善用户输入逻辑

在原始问题中,用户输入的食材被硬编码为四个独立的字符串变量,且未添加到 ArrayList 中。在上面的完整示例中,getUserIngredients 方法演示了如何动态地从用户那里获取输入,并将其添加到 List 中。

关键改进点:

  • 使用 Scanner.nextLine() 读取整行输入。
  • 使用 trim() 方法去除用户输入字符串前后的空白字符,避免因为空格导致匹配失败。
  • 将读取到的字符串直接添加到 ArrayList 中,而不是声明独立的变量。
  • 增加循环,允许用户输入指定数量的物品,或者根据需要调整为循环直到用户输入特定指令(如“done”)。

注意事项与最佳实践

  1. 字符串比较的健壮性:

    • 大小写敏感: 默认情况下,String.equals() 是大小写敏感的。如果希望忽略大小写,可以使用 ingredient.equalsIgnoreCase(itemInPantry)。在将库存添加到 HashSet 或处理用户输入时,可以统一转换为小写或大写(例如 item.toLowerCase()),以确保比较的一致性。
    • 空白字符: 用户输入可能包含多余的空格。在将用户输入添加到列表中之前,使用 String.trim() 方法去除字符串两端的空白字符,可以避免因空格导致的匹配失败。
  2. 性能考量:

    • 当列表规模较小(例如几十个元素)时,O(M*N) 的嵌套循环可能不会导致明显的性能问题。但一旦列表规模达到数百、数千甚至更多,HashSet 的 O(M + N)(转换为 Set 的时间 + 遍历查找的时间)性能优势将非常显著。
    • 如果库存列表在程序运行期间几乎不变,将其转换为 HashSet 是一次性开销,后续的查找操作都会非常快。
  3. 处理重复项:

    • HashSet 本身不存储重复元素。如果你的“库存清单”中可能存在重复项(例如,你有两袋“Bread”),但你只关心是否有“Bread”这种物品,那么将其转换为 HashSet 是合适的。
    • 如果你的需求是检查“购物清单”中的 每个实例 是否都能在“库存”中找到(例如,购物清单要2个苹果,库存只有1个),那么简单的 HashSet.contains() 就不够了。你需要更复杂的逻辑,例如使用 HashMap 来存储每个物品的数量。但对于本教程的“是否有”的需求,HashSet 是最简洁高效的方案。
  4. 代码可读性与模块化: 将比对逻辑封装在一个单独的方法(如 checkMissingItems)中,可以提高代码的可读性和复用性。将用户输入逻辑也封装起来,使 main 方法保持简洁。

通过采纳这些建议和使用 HashSet,你可以构建出高效、健壮且易于维护的列表比对功能。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1010

2023.08.02

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1565

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

241

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

148

2025.10.17

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

760

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1565

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

649

2023.11.24

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

3

2026.03.11

热门下载

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

精品课程

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

共21课时 | 4.1万人学习

Git版本控制工具
Git版本控制工具

共8课时 | 1.6万人学习

Git中文开发手册
Git中文开发手册

共0课时 | 94人学习

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

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