在Java中,对List集合去重有多种方法,以下是一些常见的实现方式:

  1. 使用HashSet(无序去重):

    1
    2
    3
    List<String> listWithDuplicates = ...; // 假设这是包含重复元素的列表
    Set<String> set = new HashSet<>(listWithDuplicates);
    List<String> distinctList = new ArrayList<>(set);

    这种方法利用了HashSet自动去除重复元素的特性。先将List转换为HashSet,然后创建一个新的ArrayList来存储不重复的元素。

  2. 使用LinkedHashSet(有序去重):

    1
    2
    3
    List<String> listWithDuplicates = ...;
    LinkedHashSet<String> linkedSet = new LinkedHashSet<>(listWithDuplicates);
    List<String> distinctList = new ArrayList<>(linkedSet);

    如果需要保持原顺序,可以使用LinkedHashSet,它会按照插入顺序保留元素。

  3. 使用TreeSet(排序后去重):

    1
    2
    3
    List<String> listWithDuplicates = ...;
    TreeSet<String> treeSet = new TreeSet<>(listWithDuplicates);
    List<String> sortedDistinctList = new ArrayList<>(treeSet);

    使用TreeSet时,除了去重外,还会根据元素的自然排序或自定义比较器进行排序。

  4. Java 8 Stream API(有序去重):

    1
    2
    3
    List<String> distinctList = listWithDuplicates.stream()
    .distinct()
    .collect(Collectors.toList());

    利用Stream API的distinct()方法进行流式处理去重,最后通过collect()方法收集到新的List中。

  5. 遍历并判断添加(有序去重):

    1
    2
    3
    4
    5
    6
    7
    List<String> listWithDuplicates = ...;
    List<String> distinctList = new ArrayList<>();
    for (String item : listWithDuplicates) {
    if (!distinctList.contains(item)) {
    distinctList.add(item);
    }
    }

    这种方法是逐个检查新列表中是否已包含当前元素,如果未包含则添加进去。

  6. 迭代器去重(无序去重,效率较低,不推荐):

    1
    2
    3
    4
    5
    6
    7
    Iterator<String> iterator = listWithDuplicates.iterator();
    while (iterator.hasNext()) {
    String current = iterator.next();
    while (iterator.hasNext() && current.equals(iterator.next())) {
    iterator.remove(); // 移除下一个相同的元素
    }
    }

    这种方法使用迭代器删除所有连续出现的重复项,但不保证整体上的顺序且效率较低,一般不建议使用。

每种方法都有其适用场景,根据实际需求选择合适的方法。

对象去重

在Java中,如果List集合中的元素是对象,并且想要根据对象的某个属性去重,可以使用Java 8及更高版本提供的Stream API来实现。以下是一个通用示例:

假设我们有一个用户类User,它有一个名为id的属性:

1
2
3
4
5
6
7
8
public class User {
private String id;
// 其他属性和getter、setter方法...

public String getId() {
return id;
}
}

现在我们有一个包含重复用户的列表,并希望根据id属性去除重复项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
List<User> usersWithDuplicates = ...; // 假设这是包含重复用户对象的列表

List<User> distinctUsersById = usersWithDuplicates.stream()
.distinct()
.collect(Collectors.toList());

// 上述代码并不能直接根据id属性去重,因为默认的equals()和hashCode()方法没有覆盖。

// 若要根据id属性去重,需要自定义一个Comparator或者创建一个新的类(例如UserByIdWrapper)来比较。
Set<User> distinctUsersById = usersWithDuplicates.stream()
.map(user -> new UserByIdWrapper(user.getId(), user)) // 包装对象以根据id进行比较
.collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(UserByIdWrapper::getId))));

// 或者自定义一个Comparator,该Comparator基于User对象的id属性进行比较
Set<User> setById = new TreeSet<User>((o1, o2) -> o1.getId().compareTo(o2.getId()));
setById.addAll(usersWithDuplicates);
List<User> listById = new ArrayList<>(setById);

// 或者使用Collectors.toMap()方法将用户列表转换为Map,然后从Map的values()获取不重复的User对象。但是,这种方法要求你选择一个不会冲突的键
List<User> distinctUsersById = usersWithDuplicates.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(User::getId, Function.identity(), (u1, u2) -> u1),
map -> new ArrayList<>(map.values())
));

distinctUsersById.forEach(user -> System.out.println(user.getId()));

// 或者在User类中覆盖equals()和hashCode()方法,使其基于id属性判断相等性,然后使用如下方式:
usersWithDuplicates.stream()
.distinct()
.collect(Collectors.toList());

// 在User类中覆盖equals()和hashCode():
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}

在上述代码中,若要根据id属性去重,有几种选择:

  1. 使用自定义包装类并结合TreeSet与Comparator的方式,当自定义Comparator时,确保其按照id字段进行排序和比较。
  2. 直接修改User类,覆盖equals()hashCode()方法,使得两个User对象如果它们的id相同则认为是相等的。这样,在调用distinct()方法时会自动按照这个逻辑去重。

请注意,第二种方法会改变整个类的相等性判断逻辑,仅当id是决定对象唯一性的关键属性时才适合这样做。

UserByIdWrapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class User {
private String id;
// 其他属性、构造函数和getter/setter...

// 如果不想修改User类本身的equals()和hashCode()
// 可以创建一个辅助类来包装User对象并基于id进行比较

public static class UserByIdWrapper implements Comparable<UserByIdWrapper> {
private final String id;
private final User user;

public UserByIdWrapper(String id, User user) {
this.id = id;
this.user = user;
}

public String getId() {
return id;
}

public User getUser() {
return user;
}

@Override
public int compareTo(UserByIdWrapper other) {
return this.id.compareTo(other.id);
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
UserByIdWrapper that = (UserByIdWrapper) obj;
return Objects.equals(id, that.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}
}
}

// 然后在实际代码中使用:
List<User> usersWithDuplicates = ...; // 假设这是包含重复用户的列表

Set<User> distinctUsersById = usersWithDuplicates.stream()
.map(user -> new UserByIdWrapper(user.getId(), user))
.collect(Collectors.toCollection(LinkedHashSet::new)); // 使用LinkedHashSet保持插入顺序

List<User> uniqueUsers = new ArrayList<>(distinctUsersById);
uniqueUsers.forEach(userWrapper -> System.out.println(userWrapper.getUser().getId()));