首页 > Java > java教程 > 正文

Java中基于关联键移除列表对象教程

霞舞
发布: 2025-11-02 15:31:21
原创
631人浏览过

Java中基于关联键移除列表对象教程

本教程详细介绍了如何在java中高效地从一个对象列表中移除那些其特定键值在另一个对象列表中不存在的项。文章涵盖了java 8及更高版本中利用stream api的现代化解决方案,以及java 8之前版本通过传统循环和迭代器实现的方法,并对不同方法的性能和适用场景进行了深入分析,旨在帮助开发者选择最优化策略。

1. 定义数据模型

首先,我们定义两个用于示例的Java类,它们分别代表不同类型的数据实体。

public class RetailerExcelConversionDto {
    private String retailerCode; // 零售商编码
    private Integer isActive;    // 是否活跃

    // 构造函数、Getter和Setter
    public RetailerExcelConversionDto(String retailerCode, Integer isActive) {
        this.retailerCode = retailerCode;
        this.isActive = isActive;
    }

    public String getRetailerCode() {
        return retailerCode;
    }

    public void setRetailerCode(String retailerCode) {
        this.retailerCode = retailerCode;
    }

    public Integer getIsActive() {
        return isActive;
    }

    public void setIsActive(Integer isActive) {
        this.isActive = isActive;
    }
}

public class RetailerDto {
    private String code; // 零售商编码
    private Integer age; // 年龄
    private String name; // 名称

    // 构造函数、Getter和Setter
    public RetailerDto(String code, Integer age, String name) {
        this.code = code;
        this.age = age;
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
登录后复制

假设我们有以下两个列表实例:

List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
// 填充数据...
retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));

List<RetailerDto> retailerDtoList = new ArrayList<>();
// 填充数据...
retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
登录后复制

2. 核心问题阐述

我们的目标是从 retailerConversionDtoList 中移除所有 retailerCode 不存在于 retailerDtoList 中任何 code 的项。简而言之,我们希望保留 retailerConversionDtoList 中与 retailerDtoList 存在关联(通过编码匹配)的项。

3. 解决方案一:使用 Java 8 Stream API (推荐)

在 Java 8 及更高版本中,Stream API 提供了一种简洁且高效的方式来处理集合数据。这种方法利用 Set 的快速查找特性,优化了移除操作。

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

实现步骤:

硅基智能
硅基智能

基于Web3.0的元宇宙,去中心化的互联网,高质量、沉浸式元宇宙直播平台,用数字化重新定义直播

硅基智能 62
查看详情 硅基智能
  1. 从 retailerDtoList 中提取所有 code 属性,并将它们收集到一个 Set<String> 中。使用 Set 的目的是为了实现 O(1) 的平均查找时间复杂度,而非 List 的 O(N),这在数据量较大时能显著提升性能。
  2. 使用 Stream API 过滤 retailerConversionDtoList。对于 retailerConversionDtoList 中的每个元素,检查其 retailerCode 是否存在于第一步创建的 Set 中。
  3. 将过滤后的元素收集到一个新的 List 中。

示例代码:

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashSet; // 导入 HashSet 以便构建示例数据

// 假设 RetailerExcelConversionDto 和 RetailerDto 类已定义

public class ListRemovalWithStream {

    public static void main(String[] args) {
        // 示例数据
        List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));
        System.out.println("原始 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));

        List<RetailerDto> retailerDtoList = new ArrayList<>();
        retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
        retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
        System.out.println("参照 retailerDtoList codes: " + retailerDtoList.stream().map(RetailerDto::getCode).collect(Collectors.toList()));

        // 步骤 1: 提取所有存在的 retailer codes 到一个 Set 中
        Set<String> retailerCodes = retailerDtoList.stream()
                                                    .map(RetailerDto::getCode)
                                                    .collect(Collectors.toSet());

        // 步骤 2 & 3: 过滤 retailerConversionDtoList
        retailerConversionDtoList = retailerConversionDtoList.stream()
                                                            .filter(t -> retailerCodes.contains(t.getRetailerCode()))
                                                            .collect(Collectors.toList());

        System.out.println("过滤后的 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));
        // 预期输出: [R001]
    }
}
登录后复制

优点:

  • 代码简洁: Stream API 提供了声明式编程风格,使代码更易读、更简洁。
  • 效率高: 利用 Set 进行查找,时间复杂度接近 O(N + M),其中 N 和 M 分别是两个列表的大小。
  • 函数式编程: 更好地支持函数式编程范式,易于并行化(尽管在此场景下可能不需要)。

4. 解决方案二:Java 8 之前的传统方法

对于不支持 Java 8 Stream API 的旧版本 Java 环境,我们可以通过传统的循环和迭代器来实现相同的功能。

4.1 方法一:构建新列表

这种方法与 Stream API 的逻辑类似,都是先构建一个用于查找的 Set,然后遍历原列表,将符合条件的元素添加到一个新列表中。

实现步骤:

  1. 创建一个 HashSet<String> 用于存储 retailerDtoList 中所有 code。
  2. 遍历 retailerDtoList,将每个 RetailerDto 对象的 code 添加到 HashSet 中。
  3. 创建一个新的 List<RetailerExcelConversionDto> 来存储过滤后的结果。
  4. 遍历 retailerConversionDtoList。对于每个元素,检查其 retailerCode 是否存在于 HashSet 中。如果存在,则将其添加到新列表中。

示例代码:

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

public class ListRemovalTraditionalNewList {

    public static void main(String[] args) {
        // 示例数据 (同上)
        List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));
        System.out.println("原始 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));

        List<RetailerDto> retailerDtoList = new ArrayList<>();
        retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
        retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
        System.out.println("参照 retailerDtoList codes: " + retailerDtoList.stream().map(RetailerDto::getCode).collect(Collectors.toList()));

        // 步骤 1 & 2: 提取所有存在的 retailer codes 到一个 Set 中
        Set<String> retailerCodes = new HashSet<>();
        for (RetailerDto retailer : retailerDtoList) {
            retailerCodes.add(retailer.getCode());
        }

        // 步骤 3 & 4: 遍历并构建新列表
        List<RetailerExcelConversionDto> newRetailerConversionDtoList = new ArrayList<>();
        for (RetailerExcelConversionDto item : retailerConversionDtoList) {
            if (retailerCodes.contains(item.getRetailerCode())) {
                newRetailerConversionDtoList.add(item);
            }
        }
        retailerConversionDtoList = newRetailerConversionDtoList; // 更新引用到新列表

        System.out.println("过滤后的 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));
        // 预期输出: [R001]
    }
}
登录后复制

优点:

  • 兼容性好: 适用于所有 Java 版本。
  • 逻辑清晰: 循环和条件判断直观易懂。

缺点:

  • 内存开销: 需要创建一个新的列表,如果原始列表很大,可能会有额外的内存开销。
  • 代码冗长: 相较于 Stream API,代码量更多。

4.2 方法二:使用迭代器原地移除

如果需要直接修改原始列表而不是创建新列表,可以使用迭代器(Iterator)的 remove() 方法。注意:在循环遍历集合时,直接使用增强for循环(for-each)或普通 for 循环通过索引移除元素会导致 ConcurrentModificationException 或跳过元素。 迭代器的 remove() 方法是唯一安全的在遍历时修改集合的方式。

实现步骤:

  1. 创建一个 HashSet<String> 用于存储 retailerDtoList 中所有 code。
  2. 获取 retailerConversionDtoList 的迭代器。
  3. 使用 while (iterator.hasNext()) 循环遍历列表。
  4. 在循环中,获取当前元素,并检查其 retailerCode 是否存在于 HashSet 中。
  5. 如果 retailerCode 不存在于 HashSet 中,则使用 iterator.remove() 方法将当前元素从列表中移除。

示例代码:

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

public class ListRemovalTraditionalIterator {

    public static void main(String[] args) {
        // 示例数据 (同上)
        List<RetailerExcelConversionDto> retailerConversionDtoList = new ArrayList<>();
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R001", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R002", 1));
        retailerConversionDtoList.add(new RetailerExcelConversionDto("R003", 0));
        System.out.println("原始 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));

        List<RetailerDto> retailerDtoList = new ArrayList<>();
        retailerDtoList.add(new RetailerDto("R001", 30, "Retailer A"));
        retailerDtoList.add(new RetailerDto("R004", 25, "Retailer D"));
        System.out.println("参照 retailerDtoList codes: " + retailerDtoList.stream().map(RetailerDto::getCode).collect(Collectors.toList()));

        // 步骤 1: 提取所有存在的 retailer codes 到一个 Set 中
        Set<String> retailerCodes = new HashSet<>();
        for (RetailerDto retailer : retailerDtoList) {
            retailerCodes.add(retailer.getCode());
        }

        // 步骤 2-5: 使用迭代器原地移除
        for (Iterator<RetailerExcelConversionDto> it = retailerConversionDtoList.iterator(); it.hasNext(); ) {
            RetailerExcelConversionDto next = it.next();
            if (!retailerCodes.contains(next.getRetailerCode())) {
                it.remove(); // 如果不存在,则移除
            }
        }

        System.out.println("过滤后的 retailerConversionDtoList: " + retailerConversionDtoList.stream().map(RetailerExcelConversionDto::getRetailerCode).collect(Collectors.toList()));
        // 预期输出: [R001]
    }
}
登录后复制

优点:

  • 节省内存: 直接修改原始列表,无需创建新列表。
  • 兼容性好: 适用于所有 Java 版本。

缺点:

  • 代码相对复杂: 迭代器操作不如 Stream API 直观。
  • 易错性: 如果不正确使用迭代器(例如,在增强for循环中尝试修改),很容易导致运行时错误。

5. 性能考量与最佳实践

  • 使用 Set 进行查找: 无论采用哪种方法,将参照列表的键值收集到 HashSet 中都是至关重要的优化步骤。HashSet 提供了平均 O(1) 的查找时间复杂度,而 ArrayList 或 LinkedList 的 contains() 方法是 O(N),这将极大地影响整体性能,尤其是在列表数据量较大时。
  • Java 8+ 优先选择 Stream API: Stream API 结合 filter 和 collect 提供了最简洁、可读性高且性能优越的解决方案。它避免了手动管理迭代器,并且在内部实现上通常经过优化。
  • 旧版本 Java 的选择:
    • 如果内存不是瓶颈,并且希望代码更直观,可以选择“构建新列表”的方法。
    • 如果对内存使用有严格要求,且愿意处理迭代器逻辑,可以选择“使用迭代器原地移除”的方法。务必正确使用 Iterator.remove()。

6. 总结

本文详细介绍了在 Java 中根据关联键从一个对象列表中移除项的多种方法。对于 Java 8 及更高版本,推荐使用 Stream API 结合 Set 进行高效过滤,它提供了最佳的简洁性和性能平衡。对于旧版本 Java,通过将参照键存储在 HashSet 中,然后选择构建新列表或使用迭代器原地移除,也能实现相同的目标。理解不同方法的优缺点和适用场景,有助于开发者在实际项目中做出明智的选择,编写出更健壮、高效的代码。

以上就是Java中基于关联键移除列表对象教程的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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