Solución:
Puede obtener una transmisión desde el List
y poner en el TreeSet
a partir del cual proporcionas un comparador personalizado que compara id de forma única.
Luego, si realmente necesita una lista, puede volver a colocar esta colección en una 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));
Dado el ejemplo:
List<Employee> employee = Arrays.asList(new Employee(1, "John"), new Employee(1, "Bob"), new Employee(2, "Alice"));
Saldrá:
[Employee{id=1, name="John"}, Employee{id=2, name="Alice"}]
Otra idea podría ser usar un contenedor que envuelva a un empleado y tenga el método equals y hashcode basado en su 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());
}
}
Luego envuelve cada instancia, llama distinct()
, desenvolverlos y recopilar el resultado en una lista.
List<Employee> unique = employee.stream()
.map(WrapperEmployee::new)
.distinct()
.map(WrapperEmployee::unwrap)
.collect(Collectors.toList());
De hecho, creo que puede hacer que este contenedor sea genérico proporcionando una función que hará la comparación:
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));
}
}
y el mapeo será:
.map(e -> new Wrapper<>(e, Employee::getId))
La forma más sencilla de hacerlo directamente en la lista es
HashSet<Object> seen=new HashSet<>();
employee.removeIf(e->!seen.add(e.getID()));
-
removeIf
eliminará un elemento si cumple con los criterios especificados -
Set.add
volveráfalse
si no modificó elSet
, es decir, ya contiene el valor - combinando estos dos, eliminará todos los elementos (empleados) cuya identificación se haya encontrado antes
Por supuesto, solo funciona si la lista admite la eliminación de elementos.
Si puedes hacer uso de equals
, luego filtre la lista usando distinct
dentro de una secuencia (vea las respuestas arriba). Si no puede o no quiere anular la equals
método, puedes filter
el flujo de la siguiente manera para cualquier propiedad, por ejemplo, para el Nombre de la propiedad (lo mismo para el Id de la propiedad, etc.):
Set<String> nameSet = new HashSet<>();
List<Employee> employeesDistinctByName = employees.stream()
.filter(e -> nameSet.add(e.getName()))
.collect(Collectors.toList());