[英]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.