在 Java 8 中,从基于属性的对象列表中删除重复项

2022-08-31 09:57:04

我正在尝试从基于某些属性的对象列表中删除重复项。

我们可以用java 8以简单的方式做到这一点吗?

List<Employee> employee

我们可以根据员工的财产从中删除重复项吗?我见过从字符串的数组列表中删除重复字符串的帖子。id


答案 1

您可以从 中获取流,并将其放入,从中提供唯一比较 id 的自定义比较器。ListTreeSet

然后,如果你真的需要一个列表,你可以把这个集合放回一个ArrayList中。

import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

...
List<Employee> unique = employee.stream()
                                .collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparingInt(Employee::getId))),
                                                           ArrayList::new));

给定示例:

List<Employee> employee = Arrays.asList(new Employee(1, "John"), new Employee(1, "Bob"), new Employee(2, "Alice"));

它将输出:

[Employee{id=1, name='John'}, Employee{id=2, name='Alice'}]

另一个想法可能是使用包装员工的包装器,并具有基于其id的equals和哈希码方法:

class WrapperEmployee {
    private Employee e;

    public WrapperEmployee(Employee e) {
        this.e = e;
    }

    public Employee unwrap() {
        return this.e;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        WrapperEmployee that = (WrapperEmployee) o;
        return Objects.equals(e.getId(), that.e.getId());
    }

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

然后,包装每个实例,调用 ,解开它们,并将结果收集到列表中。distinct()

List<Employee> unique = employee.stream()
                                .map(WrapperEmployee::new)
                                .distinct()
                                .map(WrapperEmployee::unwrap)
                                .collect(Collectors.toList());

实际上,我认为您可以通过提供一个将进行比较的函数来使此包装器通用:

public class Wrapper<T, U> {
    private T t;
    private Function<T, U> equalityFunction;

    public Wrapper(T t, Function<T, U> equalityFunction) {
        this.t = t;
        this.equalityFunction = equalityFunction;
    }

    public T unwrap() {
        return this.t;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        Wrapper<T, U> that = (Wrapper<T, U>) o;
        return Objects.equals(equalityFunction.apply(this.t), that.equalityFunction.apply(that.t));
    }

    @Override
    public int hashCode() {
        return Objects.hash(equalityFunction.apply(this.t));
    }
}

映射将是:

.map(e -> new Wrapper<>(e, Employee::getId))

答案 2

直接在列表中执行此操作的最简单方法是

HashSet<Object> seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));
  • removeIf如果元素满足指定的条件,将删除该元素
  • Set.add如果它没有修改 ,即已经包含值,将返回falseSet
  • 将这两者结合起来,它将删除之前遇到过id的所有元素(雇员)

当然,仅当列表支持删除元素时,它才有效。