简体   繁体   English

映射条目的每个键的流,包含列表作为值

[英]Stream for each key of map entry, containing list as a value

I have a map of <String, List<T>> and want to create a stream for each entry's value, in which I will be able to find the maximum element of List<T> and after that map <String, List<T>> to <String, T> . 我有一个<String, List<T>>的映射,并且想为每个条目的值创建一个流,在其中我将能够找到List<T>的最大元素,然后在该映射<String, List<T>><String, T> How do I do that? 我怎么做? Spent over 3 hours trying to figure out. 花了3多个小时试图找出答案。

Update Forgot to tell that I was asked to do it two ways: with Collector.toMap and without it and I'm most curious about the way without it. 更新忘了告诉我,我被要求这样做两种方式:使用Collector.toMap和不使用它,我对不使用它的方式最好奇。

The step where I'm stuck is: 我遇到的问题是:

employees.stream()
            .flatMap(e -> e.getJobHistory().stream()
                    .map(j -> new PersonPositionDuration(e.getPerson(), j.getPosition(), j.getDuration())))
            .collect(Collectors.groupingBy(p -> p.getPosition()))
            .entrySet().

employees is a List of Employee object, each has jobHistory property, which is a List as well and each entry of it has employer property, which is String . employees是一个List Employee对象,每个人都有jobHistory属性,它是一个List ,以及和它的每一项都有employer的财产,这是String So I turn this List<Employee> to a Map<String, List<PersonPositionDuration>> , where key is emploer and value is a class, containing info about a person and how long he was working as someone for this emploer. 因此,我将这个List<Employee>转换为Map<String, List<PersonPositionDuration>> ,其中的键是emploer,value是一个类,其中包含有关一个人的信息以及他为该雇主工作的时间。 Keys aren't unique. 键不是唯一的。

What I want to do is to find a PersonPositionDuration with a longest duration, we can get it by getDuration() . 我想做的是找到一个持续时间最长的PersonPositionDuration ,我们可以通过getDuration()获得它。 So what do I do after I get and entry set? 那么,我在获得并设置条目后该怎么办?

Also, I am adviced to use Collectors.maxBy and Collectors.collectingAndThen , but I have no idea how to do it. 另外,建议我使用Collectors.maxByCollectors.collectingAndThen ,但是我不知道该怎么做。

Assuming T implements Comparable (because you need to be able to calculate the maximum of two values) you can wrap your code in a method such as this: 假设T实现了Comparable (因为您需要能够计算两个值中的最大值),则可以将代码包装在这样的方法中:

public <T extends Comparable<T>> Map<String, T> convert(Map<String, List<T>> data) {
    // variable for readability, you can return it right away if you prefer
    Map<String, T> result = data.entrySet().stream().collect(Collectors.toMap(
            // for each key in our map...
            e -> e.getKey(),
            // ... we determine the maximum and store it as new value
            e -> Collections.max(e.getValue())
    ));

    return result;
}

It creates a Set<Map.Entry<String, List<T>>> of which you can create a Stream which is converted into a Map<String, T> by converting the value type using Collections.max() which uses Comparable.compareTo to compare two objects and determine which one is greater than the other. 它创建一个Set<Map.Entry<String, List<T>>> ,您可以创建一个Stream ,该Stream可以通过使用Comparable.compareTo Collections.max()转换值类型来转换为Map<String, T> Comparable.compareTo比较两个对象并确定哪个对象大于另一个对象。 Note that you can just use it as is for boxed types like Integer or Double (and many more) as they all implement Comparable . 请注意,由于它们都实现了Comparable ,因此可以像IntegerDouble (还有更多)之类的盒装类型一样直接使用它。

If your map is of type <String, List<Integer>> it can be done as 如果您的map类型为<String, List<Integer>>则可以通过以下方式完成

Map<String, List<Integer>> map = new HashMap<>();
map.put("a", Arrays.asList(5, 3, 7));
map.put("b", Arrays.asList(1, 5));
map.put("c", Arrays.asList(45, 2));
map.put("d", Arrays.asList(4, 6, 2, 34));

map.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> {
    return entry.getValue().stream().max(Integer::compareTo).orElse(0);
}));

output : 输出:

{a=7, b=5, c=45, d=34}

You can modify the entry.getValue().stream().max(Integer::compareTo).orElse(0) with the comparator for Type T or your class Type. 您可以使用T型比较器或类Type的比较器修改entry.getValue().stream().max(Integer::compareTo).orElse(0)

It is not totally clear what you want to achieve or how your data structure looks like. 尚不清楚要实现什么或数据结构如何。 What you wrote and the code you showed doesn't match together. 您编写的内容与显示的代码不一致。 They achieve very different things. 他们实现了截然不同的事情。

But you wished for a solution by using Collectors.maxBy and without Collectors.toMap . 但是你用希望的解决方案Collectors.maxBy并没有Collectors.toMap The other answers didn't provide such a solution. 其他答案没有提供这样的解决方案。 Therefore I will try to give one. 因此,我将尝试给出一个。


Your Data: 您的资料:

As your data structure I assume you have employers which each employ multiple employees. 作为您的数据结构,我假设您有每个雇用多名雇员的雇主。 Every employee has some personal information, the position he works/ed in and the duration he worked in this position until know. 每位员工都有一些个人信息,他所从事的工作/担任过的职位以及他在该职位上的工作期限,直到知道为止。

For example (Map employerEmployeeMap ): 例如(Map ownerEmployeeMap ):

CompanyA: [{Max, Developer, 5.4}, {Mari, Designer, 6.8}, {Paul, Manager, 1.2}]
CompanyB: [{Lea, CEO, 7.1}, {Tom, Admin, 4.5}, {Ria, Marketing, 0.4}]
CompanyC: [{Alex, Secretary, 1.5}, {Lisa, Developer, 3.3}, {Mia, Developer, 2.7}]

What you want to achieve: 您想要实现的目标:

You want to have the PersonPositionDuration with the longest duration. 您想使PersonPositionDuration具有最长的持续时间。 I wasn't sure if you want only the person with the longest duration over all employers or per each employer. 我不确定您是否只希望在所有雇主中或每个雇主中任职时间最长的人。 Therefore I will provide multiple solutions. 因此,我将提供多种解决方案。


Solution with maxBy maxBy解决方案

If you only want the person with the longest duration over all employers you can just iterate over the employees and get the one with the highest duration: 如果您只希望在所有雇主中任职时间最长的人,则可以对雇员进行迭代并获得其任职时间最长的人:

Optional<PersonPositionDuration> maxDurationPerson = employerEmployeeMap.values().stream()
                .flatMap(List::stream)
                .collect(maxBy(comparing(PersonPositionDuration::getDuration)));

This would be equal to: 这将等于:

Optional<PersonPositionDuration> maxDurationPerson = employerEmployeeMap.values().stream()
                .flatMap(List::stream)
                .max(comparing(PersonPositionDuration::getDuration));

Result: {Lea, CEO, 7.1} 结果: {Lea, CEO, 7.1}

If you want only the person with the highest duration but do not want to lose the employer information: 如果您只想要持续时间最长的人,而又不想失去雇主信息:

Optional<Pair<String, PersonPositionDuration>> maxDurationPersonCompany = employerEmployeeMap.entrySet().stream()
                .flatMap(entry -> entry.getValue().stream().map(p -> new Pair<>(entry.getKey(), p)))
                .max(comparing(entry -> entry.getValue().getDuration()));

Result: CompanyB: {Lea, CEO, 7.1} 结果: CompanyB: {Lea, CEO, 7.1}

If you want the person with the highest duration for every company you can do the following: 如果您希望每个公司的工作时间最长的人,则可以执行以下操作:

Map<String, Optional<Pair<String, PersonPositionDuration>>> maxDurationPerCompany = employerEmployeeMap.entrySet().stream()
                .flatMap(entry -> entry.getValue().stream().map(p -> new Pair<>(entry.getKey(), p)))
                .collect(groupingBy(Pair::getKey, maxBy(comparing(entry -> entry.getValue().getDuration()))));

Result: 结果:

CompanyA: {Mari, Designer, 6.8}
CompanyB: {Lea, CEO, 7.1}
Companyc: {Lisa, Developer, 3.3}

I hope this covers your question and your need. 我希望这能涵盖您的问题和需求。 If not, if something is missing or of if something is unclear, please feel free to comment. 如果不是,如果缺少某些内容或不清楚的内容,请随时发表评论。


Full working example: 完整的工作示例:

import javafx.util.*;

import java.util.*;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.*;

public class Main {

    public static void main(String[] args) {
        class PersonPositionDuration {
            private final String person;
            private final String position;
            private final Double duration;

            public PersonPositionDuration(String person, String position, Double duration) {
                this.person = person;
                this.position = position;
                this.duration = duration;
            }

            public Double getDuration() {
                return duration;
            }
        }

        Map<String, List<PersonPositionDuration>> employerEmployeeMap = new HashMap<>();
        employerEmployeeMap.put("CompanyA", Arrays.asList(new PersonPositionDuration("Max", "Developer", 5.4), new PersonPositionDuration("Mari", "Designer", 6.8), new PersonPositionDuration("Paul", "Manager", 1.2)));
        employerEmployeeMap.put("CompanyB", Arrays.asList(new PersonPositionDuration("Lea", "CEO", 7.1), new PersonPositionDuration("Tom", "Admin", 4.5), new PersonPositionDuration("Ria", "Marketing", 0.4)));
        employerEmployeeMap.put("CompanyC", Arrays.asList(new PersonPositionDuration("Alex", "Secretary", 1.5), new PersonPositionDuration("Lisa", "Developer", 3.3), new PersonPositionDuration("Mia", "Developer", 2.7)));

        Optional<PersonPositionDuration> maxDurationPerson = employerEmployeeMap.values()
                .stream()
                .flatMap(List::stream)
                .max(comparing(PersonPositionDuration::getDuration));

        Optional<Pair<String, PersonPositionDuration>> maxDurationPersonCompany = employerEmployeeMap.entrySet()
                .stream()
                .flatMap(entry -> entry.getValue()
                        .stream()
                        .map(p -> new Pair<>(entry.getKey(), p)))
                .max(comparing(entry -> entry.getValue()
                        .getDuration()));

        Map<String, Optional<Pair<String, PersonPositionDuration>>> maxDurationPerCompany = employerEmployeeMap.entrySet()
                .stream()
                .flatMap(entry -> entry.getValue()
                        .stream()
                        .map(p -> new Pair<>(entry.getKey(), p)))
                .collect(groupingBy(Pair::getKey, maxBy(comparing(entry -> entry.getValue()
                        .getDuration()))));
    }
}

暂无
暂无

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

相关问题 将地图转换为列表<string> - 作为每个列表条目的“键值” - convert map to list<string> - as “key-value” to each list entry 在Java中将List <Map.Entry <Key,Value >>转换为List <Key> - Converting List<Map.Entry<Key, Value>> to List<Key> in Java 如何使用Java 8转换Map的每个条目集的键和值? - How to transform the key and value of a each entry set of a Map using Java 8? Java 8流映射 <String, List<String> &gt;每个键的值总和 - Java 8 stream Map<String, List<String>> sum of values for each key 使用Java流从List获取包含密钥的映射以及该密钥的出现次数 - Using Java stream to get map containing a key and the number of occurrences of that key from a List 将列表值流式传输到Map.Entry.values - Stream List values into Map.Entry.values 如果列表尚未包含包含键值对的 map,如何有条件地将 Map 插入 Java 中的列表 - How to conditionally insert a Map into a list in Java if the list does not already include a map containing a key value pair 休眠:获取Map,将列的值作为“键”,并将包含“键”的行的列表作为“值” - Hibernate: Obtaining Map with a column's value as “key”, and as “value” a list of rows containing “key” Java 8 stream 分组列表<map<> &gt; 同样<key, value>到新列表<map<> &gt; </map<></key,></map<> - Java 8 stream grouping a List<Map<>> by the same <Key, Value> to a new List<Map<>> 如何使用Jackson反序列化将每个映射表项的键注入相应的值对象? - How to inject the key of each map entry into the corresponding value object with Jackson deserialization?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM