[英]Create a SortedMap from a List objects with the Value represented as a list of N lowest object's attributes mapped to a particular Key
我正在使用一个 CSV 文件,其中包含一些有关事故的信息。
我创建了Accident
类型:
private Integer driverAge;
private Integer vehicleAge;
public Accident(Integer driverAge, Integer vehicleAge) {
this.driverAge = driverAge;
this.vehicleAge = vehicleAge;
}
我还创建了一个读取所有 CSV 文件的函数,将所有事故转换为List<Accident>
并将其保存为这种类型AccidentArchive
:
private List<Accident> accidents;
public AccidentArchive(List<Accident> accidents) {
this.accidents = accidents;
}
所以,我们正在处理我还不完全理解的流,我一直在这个练习中陷入困境,我必须创建一个返回SortedMap<K, V>
的函数,其中键必须是driverAge
值,并且该值必须是按具有相同driverAge
值的n
最低vehicleAge
值的降序排序的列表:
public SortedMap<Integer, List<Integer>> getNMinVehicleAgesPerDriverAge(Integer n) {
return getAccidents().stream().
...
我曾尝试使用Collectors.toMap()
和Collectors.toList()
以某种方式使其工作,但我不知道该怎么做。
这个问题与通过部分排序找到N
个最大值(或最小值)的算法问题相关。 使用收集器的实现可能看起来很困难,因此我决定引入一个简化的解决方案。
我们可以使用需要三个参数的groupingBy()
风格:
classifier
函数mapFactory
(允许指定地图的结果类型) 作为groupingBy()
的下游收集器,我们可以使用 collectAndThen 与collectingAndThen
器mapping()
和toList()
的组合以及一个函数,该函数将对映射到每个key的整个结果列表进行排序,然后将删除不必要的值,仅保留n
最低vehicleAge
:
public SortedMap<Integer, List<Integer>> getNMinVehicleAgesPerDriverAge(Integer n) {
return getAccidents().stream()
.collect(Collectors.groupingBy(Accident::getDriverAge,
TreeMap::new,
Collectors.collectingAndThen(
Collectors.mapping(Accident::getVehicleAge, Collectors.toList()),
list -> list.stream()
.sorted(Comparator.reverseOrder())
.limit(n)
.collect(Collectors.toList()))));
}
正如我之前所说,当我们只需要其中一些值时,我们不需要对映射到每个键的所有值进行排序。 当n
与列表的总大小(例如每个键3
到100,000
)相比显得微不足道时,它将导致严重的性能损失。
我们可以通过使用PriorityQueue
(它是 JDK 内置的 Heap 数据结构的实现)来引入部分排序。
为了增强之前的解决方案,我们需要替换groupingBy()
的下游收集器,您可以结合使用mapping()
和由PriorityQueue
支持的自定义收集器,该收集器仅保留与每个driverAge
关联的n
最低vehicleAge
值:
public SortedMap<Integer, List<Integer>> getNMinVehicleAgesPerDriverAge(Integer n) {
return getAccidents().stream()
.collect(Collectors.groupingBy(Accident::getDriverAge,
TreeMap::new,
Collectors.mapping(Accident::getVehicleAge,
getMaxN(n, Comparator.<Integer>reverseOrder()))));
}
下面提供的方法负责根据提供的结果列表和比较器的最大大小生成自定义收集器。 此答案中详细解释了其背后的逻辑:
public static <T> Collector<T, ?, List<T>> getMaxN(int size, Comparator<T> comparator) {
return Collector.of(
() -> new PriorityQueue<>(comparator),
(Queue<T> queue, T next) -> tryAdd(queue, next, comparator, size),
(Queue<T> left, Queue<T> right) -> {
right.forEach(next -> tryAdd(left, next, comparator, size));
return left;
},
(Queue<T> queue) -> queue.stream().toList(),
Collector.Characteristics.UNORDERED);
}
public static <T> void tryAdd(Queue<T> queue, T next, Comparator<T> comparator, int size) {
if (queue.size() == size && comparator.compare(next, queue.element()) < 0) queue.remove(); // if next value is less than the smallest element in the queue and max size has been exceeded the largest element needs to be removed from the queue
if (queue.size() < size) queue.add(next);
}
顺便说一句,如果您的作业没有指定使用SortedMap
作为返回类型的要求。 最好使用NavigableMap
接口,它定义了更广泛的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.