簡體   English   中英

從 List 對象創建一個 SortedMap,其 Value 表示為映射到特定 Key 的 N 個最低對象屬性的列表

[英]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 與collectingAndThenmapping()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與列表的總大小(例如每個鍵3100,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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM