我想基于某个属性从对象列表中删除重复的元素,是否可以使用Java 8简单地实现?
List<Employee> employee
我们能否根据员工的id
属性从中删除重复项?我看过一些帖子,可以从字符串数组列表中删除重复的字符串。
List
中获取流并将其放入TreeSet
中,然后提供一个自定义比较器来唯一比较id。如果你确实需要一个列表,你可以将这个集合放回到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'}]
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))
collectingAndThen
,是否直接使用new ArrayList<(ArrayList(Collection<? extends E> c)
构造函数来包装解决方案会更有效率?因此最终代码将是List<Employee> unique = new ArrayList<>(employee.stream().collect(toCollection(() -> new TreeSet<>(comparingInt(Employee::getId)))));
。 - cbenderTreeSet
构造函数将接受任何 Comparator
。在 Java 8 及更高版本中,comparingInt
方法只是一种快速创建比较 int
字段的 Comparator 的方法。如果您想添加另一个字段进行比较,可以使用 thenComparing
链接到原始比较上,它看起来像 comparingInt(Employee::getId).thenComparing(Employee::getName)
。这篇文章似乎是解释 Comparators 的好文章 - https://www.baeldung.com/java-8-comparator-comparing。 - cbender在列表中直接完成它的最简单方法是
HashSet<Object> seen = new HashSet<>();
employee.removeIf(e -> !seen.add(e.getID()));
removeIf
将删除满足指定条件的元素Set.add
如果未修改 Set
,即已包含该值,则返回 false
当然,这只适用于支持元素的删除操作的列表。
equals
和hashCode
实现,例如:yourList.removeIf(e -> !seen.add(Arrays.asList(e.getFirstKeyPart(), e.getSecondKeyPart())));
通过Arrays.asList
组成的键可以处理任意数量的组件,而对于少量组件,专用的键类型可能更有效。 - Holgerequals
,则可以使用流内的 distinct
进行列表过滤(请参见上面的答案)。如果您无法或不想覆盖 equals
方法,则可以按以下方式对流进行 filter
以获取任何属性,例如名称属性(Id 属性相同):Set<String> nameSet = new HashSet<>();
List<Employee> employeesDistinctByName = employees.stream()
.filter(e -> nameSet.add(e.getName()))
.collect(Collectors.toList());
public static <T> Predicate<T> distinctBy(Function<? super T, ?> f) {
Set<Object> objects = new ConcurrentHashSet<>();
return t -> objects.add(f.apply(t));
}
然后只需在任何地方重用谓词即可:
employees.stream().filter(distinctBy(e -> e.getId));
stream().distinctBy(Employee::Id)
,将会非常方便。 - Arun Gowdanew ConcurrentHashSet
更改为ConcurrentHashMap.newKeySet()
。 - KeKru尝试这段代码:
Collection<Employee> nonDuplicatedEmployees = employees.stream()
.<Map<Integer, Employee>> collect(HashMap::new,(m,e)->m.put(e.getId(), e), Map::putAll)
.values();
这对我有用:
list.stream().distinct().collect(Collectors.toList());
当然,您需要实现equals方法。
如果顺序不重要并且并行运行效果更好,可以使用Collectors.toMap()方法将结果收集到Map中,然后获取值:
employee.stream().collect(Collectors.toConcurrentMap(Employee::getId, Function.identity(), (p, q) -> p)).values()
employee.stream().collect(Collectors.toConcurrentMap(Employee::getId, Function.identity(), (p, q) -> p)).values().stream().collect(Collectors.toList())
。至于并行,你可以在这里使用或不使用 - 我的意思是parallelStream API? - Rok T.这里有很多好的答案,但我没有找到关于使用reduce
方法的答案。所以针对您的情况,您可以按照以下方式应用它:
List<Employee> employeeList = employees.stream()
.reduce(new ArrayList<>(), (List<Employee> accumulator, Employee employee) ->
{
if (accumulator.stream().noneMatch(emp -> emp.getId().equals(employee.getId())))
{
accumulator.add(employee);
}
return accumulator;
}, (acc1, acc2) ->
{
acc1.addAll(acc2);
return acc1;
});
另一个简单版本
BiFunction<TreeSet<Employee>,List<Employee> ,TreeSet<Employee>> appendTree = (y,x) -> (y.addAll(x))? y:y;
TreeSet<Employee> outputList = appendTree.apply(new TreeSet<Employee>(Comparator.comparing(p->p.getId())),personList);
TreeSet<Employee> outputList = new TreeSet<>(Comparator.comparing(p->p.getId())); outputList.addAll(personList);
直接的代码要简单得多。 - Holger
Employee
正确实现equals
和hashCode
以正确识别重复项的情况下,才能起作用。 - Madbreaks