简体   繁体   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

I am working with a CSV file which includes some information about accidents.我正在使用一个 CSV 文件,其中包含一些有关事故的信息。

I've created the Accident type:我创建了Accident类型:

private Integer driverAge;
private Integer vehicleAge;

public Accident(Integer driverAge, Integer vehicleAge) {
    this.driverAge = driverAge;
    this.vehicleAge = vehicleAge;
}

I've also created a function that reads all the CSV file, converts all the accidents to a List<Accident> and saves it to this type AccidentArchive :我还创建了一个读取所有 CSV 文件的函数,将所有事故转换为List<Accident>并将其保存为这种类型AccidentArchive

private List<Accident> accidents;

public AccidentArchive(List<Accident> accidents) {
    this.accidents = accidents;
}

So, we are working with streams which I don't understand entirely yet, and I've been stuck in this exercise where I have to make a function that returns a SortedMap<K, V> in which the key has to be the driverAge values and the value has to be a list sorted in descending order of the n lowest vehicleAge values with the same driverAge value:所以,我们正在处理我还不完全理解的流,​​我一直在这个练习中陷入困境,我必须创建一个返回SortedMap<K, V>的函数,其中必须是driverAge值,并且该值必须是按具有相同driverAge值的n最低vehicleAge降序排序的列表

public SortedMap<Integer, List<Integer>> getNMinVehicleAgesPerDriverAge(Integer n) {
    return getAccidents().stream().
...

I have tried using Collectors.toMap() and Collectors.toList() to somehow make it work, but I have no idea how to do it.我曾尝试使用Collectors.toMap()Collectors.toList()以某种方式使其工作,但我不知道该怎么做。

Simplified approach简化方法

This problem correlates with the algorithmic question of finding N maximum (or minimum) values via partial sorting.这个问题与通过部分排序找到N个最大值(或最小值)的算法问题相关。 It's implementation using collectors might seem tough, therefore I've decided to introduce a simplified solution.使用收集器的实现可能看起来很困难,因此我决定引入一个简化的解决方案。

We can use a flavor of groupingBy() that expects three arguments :我们可以使用需要三个参数groupingBy()风格:

  • a classifier function , classifier函数
  • a supplier mapFactory (which allows to specify the resulting type of map)供应商mapFactory (允许指定地图的结果类型)
  • and a downstream collector .和一个下游收集器

As a downstream collector of groupingBy() we can utilize collectingAndThen with a combination of collectors mapping() and toList() and a function that will sort the whole resulting list mapped to each key , and then will drop unnecessary values retaining only n lowest vehicleAge :作为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()))));
}

More performant version性能更高的版本

As I've said before, we don't need to sort all values mapped to each key when we need only some of them.正如我之前所说,当我们只需要其中一些值时,我们不需要对映射到每个的所有值进行排序。 When n is dwarfish in comparison to the total size of the list (like 3 to 100,000 for every key) it will cause a serious performance hit.n与列表的总大小(例如每个键3100,000 )相比显得微不足道时,它将导致严重的性能损失。

We can introduce a partial sorting by using PriorityQueue (which is an implementation of the Heap data structure built-in in the JDK).我们可以通过使用PriorityQueue (它是 JDK 内置的 Heap 数据结构的实现)来引入部分排序。

To enhance the previous solution, we need to replace the downstream collector of groupingBy() you can utilize a combination of mapping() and a custom collector backed by the PriorityQueue which retain only n lowest vehicleAge values associated with each driverAge :为了增强之前的解决方案,我们需要替换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()))));
}

The method provided below is responsible for generating a custom collector based on the provided maximum size of the resulting list and a comparator .下面提供的方法负责根据提供的结果列表和比较器的最大大小生成自定义收集器。 The logic behind it was explained in great detail in this answer :此答案中详细解释了其背后的逻辑:

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);
}

By the way, if your assignment doesn't specify the requirement to utilize SortedMap as a return type.顺便说一句,如果您的作业没有指定使用SortedMap作为返回类型的要求。 It better to use NavigableMap interface, which defines a wider range of methods, instead.最好使用NavigableMap接口,它定义了更广泛的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 创建键值对象列表 - Create a list of key value objects MapStruct:对象的映射列表,当对象从两个对象映射时 - MapStruct: Map List of objects, when object is mapped from two objects 如何将 List 转换为 Map 并使 map 成为对象属性的键 - How to convert List to Map and make the map key the object's attributes 使用地图值中的前N个对象从地图创建列表 - Create List from Map with first N objects from Map values 哈希图 <Key,List<Object> &gt;来自包含相同属性的对象列表 - HashMap<Key,List<Object>> from list of objects containing same property Java 8:根据对象属性从列表中迭代和分组对象 - Java 8: Iterate and group objects from list based on object attributes 如何将关键对象(s3)列表筛选(包含特定字符串)到新列表中 - How to filter(contains particular string) list of key objects (s3) into a new list 如何创建具有多个值映射到列表中相同键的映射 <Map<String,Object> ? - How to create a map having multiple values mapped to same key out of List<Map<String,Object>? 根据属性的值,从单个对象列表中重建列表 - Rebuilding lists from a single list of objects, based on the value of attributes 从另一个对象列表创建一个对象元素的列表 - Create list of an element of an Object from another list of Objects
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM