Java >> Java Program >  >> Java

Ta bort dubbletter från en lista med objekt baserat på egenskap i Java 8

Du kan få en stream från List och lägg in TreeSet där du tillhandahåller en anpassad komparator som jämför id unikt.

Om du sedan verkligen behöver en lista kan du lägga tillbaka den här samlingen i en 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));

Med tanke på exemplet:

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

Det kommer att mata ut:

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

En annan idé kan vara att använda ett omslag som omsluter en anställd och har metoden equals och hashcode baserat på dess id:

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());
    }
}

Sedan slår du in varje instans, ringer distinct() , packa upp dem och samla resultatet i en lista.

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

Jag tror faktiskt att du kan göra detta omslag generiskt genom att tillhandahålla en funktion som gör jämförelsen:

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));
    }
}

och mappningen blir:

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

Det enklaste sättet att göra det direkt i listan är

HashSet<Object> seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));
  • removeIf tar bort ett element om det uppfyller de angivna kriterierna
  • Set.add returnerar false om den inte ändrade Set , dvs. innehåller redan värdet
  • om man kombinerar dessa två tar det bort alla element (anställda) vars id har påträffats tidigare

Naturligtvis fungerar det bara om listan stöder borttagning av element.


Om du kan använda equals , filtrera sedan listan med distinct inom en ström (se svaren ovan). Om du inte kan eller vill åsidosätta equals metod kan du filter bäcken på följande sätt för någon fastighet, t.ex. för egenskapen Namn (samma för egenskapen Id etc.):

Set<String> nameSet = new HashSet<>();
List<Employee> employeesDistinctByName = employees.stream()
            .filter(e -> nameSet.add(e.getName()))
            .collect(Collectors.toList());

Java-tagg