首先创建一个TreeMap,其key是工资。 TreeMap 按其键对其条目进行排序。然后获取第一个条目,即薪水最低的条目并获取与之相关的值。此解决方案仅对列表进行一次迭代。这是它的外观。
List<Employee> empsWithLowestSalary = employees.stream()
.collect(Collectors.groupingBy(Employee::getSalary, TreeMap::new, Collectors.toList()))
.firstEntry()
.getValue();
TreeMap 将地图元素存储在红黑树中。红黑树中一个元素的插入成本为O(Log (n))。由于我们要插入n 元素,因此该解决方案的总时间复杂度为O(n Log (n))。对于firstEntry(),它需要恒定的时间O(1),因为它分别维护一个指向树中最左边和最右边叶子节点的指针。最左边的节点代表树中的最小值,而最右边的叶子节点代表最高值。
通过关注great answer,我想到了编写一个自定义收集器来满足我们的目的。该收集器仅对 List 进行一次迭代,其运行时复杂度为 O(n),明显优于上述方法。此外,它允许您在一个语句中编写您的客户端代码。这是它的外观。
static <T> Collector<T, ?, List<T>> minList(Comparator<? super T> comp) {
return Collector.of(ArrayList::new, (list, t) -> {
int c;
if (list.isEmpty() || (c = comp.compare(t, list.get(0))) == 0)
list.add(t);
else if (c < 0) {
/*
* We have found a smaller element than what we already have. Clear the list and
* add this smallest element to it.
*/
list.clear();
list.add(t);
}
}, (list1, list2) -> {
if (comp.compare(list1.get(0), list2.get(0)) < 0)
return list1;
else if (comp.compare(list1.get(0), list2.get(0)) > 0)
return list2;
else {
list1.addAll(list2);
return list1;
}
});
}
这是您的客户端代码。
Collection<Employee> empsWithLowestSalary = employees.stream()
.collect(minList(Comparator.comparing(Employee::getSalary)));