服务器测评网
我们一直在努力

Java List如何高效去重且保持顺序?

在 Java 开发中,List 是一种常用的集合数据结构,用于存储有序且可重复的元素,在实际业务场景中,我们经常需要去除 List 中的重复元素,以保证数据的唯一性,本文将详细介绍 Java List 去重的多种方法,包括其原理、适用场景及代码示例,帮助开发者根据实际需求选择最合适的解决方案。

Java List如何高效去重且保持顺序?

使用 HashSet 去重(基于哈希表的去重)

HashSet 是 Java 集合框架中基于哈希表实现的 Set 接口类,其核心特点是存储的元素不能重复,且添加、删除、查询操作的平均时间复杂度为 O(1),利用 HashSet 的这一特性,可以快速实现 List 去重。

实现原理

通过将 List 中的元素添加到 HashSet 中,利用 HashSet 自动去重的特性,再将 HashSet 中的元素重新存入 List,即可完成去重,需要注意的是,HashSet 不保证元素的插入顺序,因此若需保留 List 原始顺序,需结合 LinkedHashSet 使用。

代码示例

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public List<String> removeDuplicatesWithHashSet(List<String> list) {
    // 使用 HashSet 去重,不保留原始顺序
    Set<String> set = new HashSet<>(list);
    return new ArrayList<>(set);
}
public List<String> removeDuplicatesWithLinkedHashSet(List<String> list) {
    // 使用 LinkedHashSet 去重,保留原始顺序
    Set<String> set = new LinkedHashSet<>(list);
    return new ArrayList<>(set);
}

适用场景

  • 当不关心 List 中元素的原始顺序时,直接使用 HashSet 即可。
  • 若需保留元素插入顺序,需选择 LinkedHashSet(基于哈希表和链表实现,维护了元素的插入顺序)。
  • 此方法的时间复杂度为 O(n),空间复杂度为 O(n),适合大数据量去重场景。

使用 Java 8 Stream 去重(函数式编程风格)

Java 8 引入的 Stream API 提供了函数式编程的便捷方式,distinct() 方法可轻松实现 List 去重,该方法基于 hashCode()equals() 方法判断元素是否重复,底层通过 HashSet 实现。

实现原理

Stream 的 distinct() 方法会对流中的元素进行去重,返回一个新流,再通过 collect() 方法将流重新收集为 List,与 HashSet 类似,默认情况下不保证原始顺序,但可通过 forEachOrdered() 或使用 LinkedHashSet 保留顺序。

Java List如何高效去重且保持顺序?

代码示例

import java.util.List;
import java.util.stream.Collectors;
public List<String> removeDuplicatesWithStream(List<String> list) {
    // 去重并保留原始顺序(Stream 默认保留顺序)
    return list.stream()
            .distinct()
            .collect(Collectors.toList());
}

适用场景

  • 适用于 Java 8 及以上版本,代码简洁,可读性高。
  • 适合在函数式编程风格的项目中使用,可与 Stream 的其他操作(如 filter、map)无缝衔接。
  • 时间复杂度和空间复杂度均为 O(n),性能与 HashSet 方法接近。

使用循环遍历去重(手动控制去重逻辑)

若项目环境较低(如 Java 7 及以下),或需要自定义去重逻辑(如根据对象的特定属性去重),可通过循环遍历 List 的方式手动去重。

实现原理

创建一个新的 List,遍历原始 List 中的每个元素,若新 List 中不存在该元素,则将其添加到新 List 中,最终返回新 List 即可完成去重。

代码示例

import java.util.ArrayList;
import java.util.List;
public List<String> removeDuplicatesWithLoop(List<String> list) {
    List<String> result = new ArrayList<>();
    for (String item : list) {
        if (!result.contains(item)) {
            result.add(item);
        }
    }
    return result;
}

适用场景

  • 适用于 Java 7 或更早版本,不依赖额外集合类。
  • 可结合自定义逻辑实现复杂去重,例如根据对象的某个字段去重(需重写 equals()hashCode() 方法)。
  • 时间复杂度为 O(n²)(因 contains() 方法底层为线性遍历),空间复杂度为 O(n),当数据量较大时,性能较差,建议优先选择前两种方法。

使用 List.contains() 方法优化(基于 ArrayList 的去重)

若 List 本身就是 ArrayList(或随机访问性能较好的 List),可通过 contains() 方法优化手动去重的效率。

实现原理

与循环遍历去重类似,但利用 ArrayList 的随机访问特性,通过索引遍历替代增强 for 循环,减少 contains() 方法的调用开销。

Java List如何高效去重且保持顺序?

代码示例

import java.util.ArrayList;
import java.util.List;
public List<String> removeDuplicatesWithContains(List<String> list) {
    List<String> result = new ArrayList<>();
    for (int i = 0; i < list.size(); i++) {
        if (!result.contains(list.get(i))) {
            result.add(list.get(i));
        }
    }
    return result;
}

适用场景

  • 适用于 ArrayList 或随机访问性能较好的 List,比增强 for 循环稍高效,但时间复杂度仍为 O(n²)。
  • 当数据量较小时(如元素少于 1000),可作为简单实现方案。

自定义对象去重(重写 equals() 和 hashCode())

若 List 中存储的是自定义对象,需根据对象的业务属性去重时,必须重写 equals()hashCode() 方法。

实现原理

HashSet 和 Stream 的 distinct() 方法均依赖 hashCode()equals() 判断元素是否重复,若未重写这两个方法,默认使用 Object 类的实现(基于内存地址),导致即使对象内容相同也会被视为不同元素。

代码示例

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
class User {
    private String name;
    private int age;
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}
public List<User> removeDuplicateUsers(List<User> users) {
    return users.stream()
            .distinct()
            .collect(Collectors.toList());
}

注意事项

  • 重写 equals()hashCode() 时,需保证两个方法的一致性:若 equals() 返回 true,hashCode() 必须返回相同值。
  • 通常选择对象的业务关键字段(如主键)作为比较依据,而非内存地址。

不同去重方法的对比

方法 时间复杂度 空间复杂度 是否保留顺序 适用场景
HashSet O(n) O(n) 大数据量,不关心顺序
LinkedHashSet O(n) O(n) 大数据量,需保留顺序
Java 8 Stream O(n) O(n) Java 8+,函数式编程风格
循环遍历 O(n²) O(n) 小数据量,自定义逻辑
ArrayList.contains O(n²) O(n) ArrayList,小数据量

选择建议

  • 优先使用 Java 8 StreamLinkedHashSet:代码简洁,性能高效,且可保留顺序。
  • 若需自定义去重逻辑(如按对象属性去重),确保重写 equals()hashCode(),再结合 Stream 或 HashSet 使用。
  • 避免在大数据量场景下使用循环遍历或 contains() 方法,以免因 O(n²) 的时间复杂度导致性能问题。

通过合理选择去重方法,可以有效提升 Java List 数据处理的效率和代码的可维护性。

赞(0)
未经允许不得转载:好主机测评网 » Java List如何高效去重且保持顺序?