Java 8 对象列表映射<String, List> 值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/50348166/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 23:34:23  来源:igfitidea点击:

Java 8 List of Objects to Map<String, List> of values

javajava-8

提问by Rinsen S

I am trying to convert List<Object>to Map<String, List>using Streams,

我正在尝试转换List<Object>Map<String, List>使用Streams

public class User{
   String name;
   String age;
   String org;
}

I have List<Users>, and need to collect into Map<String, Object> m,

我有List<Users>,需要收集到Map<String, Object> m

 m.put("names", List of names,);
 m.put("age", List of age);
 m.put("org", List of org);

to be use in named query -> eg: select * from table ... where names in (:names) and age in (:age) and org in (:org)

用于命名查询 -> 例如: select * from table ... where names in (:names) and age in (:age) and org in (:org)

as of now I am doing like

到目前为止,我正在做

List<String> names = userList.stream().map(User::getName).collect(Collectors.toList());
List<String> age= userList.stream().map(User::getAge).collect(Collectors.toList());
List<String> org= userList.stream().map(User::getName).collect(Collectors.toList());

How to collect all the values while streaming to the list only once ?

如何在仅流式传输到列表一次时收集所有值?

采纳答案by Eran

I believe something like this should work:

我相信这样的事情应该有效:

Map<String,List<String>> map =
    userList.stream()
            .flatMap(user -> {
                Map<String,String> um = new HashMap<>();
                um.put("names",user.getName());
                um.put("age",user.getAge());
                um.put("org",user.getOrg());
                return um.entrySet().stream();
            }) // produces a Stream<Map.Entry<String,String>>
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                                           Collectors.mapping(Map.Entry::getValue,
                                                              Collectors.toList())));

It converts each Userto a Map<String,String>(containing the 3 required properties indexed by the required keys), and then groups the entries of all the user maps by their keys.

它将每个转换User为 a Map<String,String>(包含由所需键索引的 3 个必需属性),然后按其键对所有用户映射的条目进行分组。

EDIT:

编辑:

Here's another alternative that creates the Map.Entrys directly instead of creating the small HashMaps, so it should be more efficient:

这是另一种Map.Entry直接创建s 而不是创建小HashMaps 的替代方法,因此它应该更有效:

Map<String,List<String>> map =
    userList.stream()
            .flatMap (user -> Stream.of (new SimpleEntry<>("names",user.getName()),
                                         new SimpleEntry<>("age",user.getAge()),
                                         new SimpleEntry<>("org",user.getOrg())))
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                                           Collectors.mapping(Map.Entry::getValue,
                                                              Collectors.toList())));

回答by Michael

Eran's showed youhow you can accomplish this with streams. As you can hopefully see, it's incredibly ugly.

Eran 向您展示了如何使用流来实现这一点。正如你所希望的那样,它非常丑陋。

If your issue with your procedural version is the amount of code duplication, there are other ways besides streams that we can use to solve that problem.

如果您的程序版本问题是代码重复的数量,那么除了流之外,我们还可以使用其他方法来解决该问题。

I would refactor the collection to its own method:

我会将集合重构为它自己的方法:

private static List<String> getProperty(List<User> users, Function<User, String> getter) {
    return users.stream().map(getter).collect(Collectors.toList());
}

Map<String,List<String>> map = new HashMap<>();
map.put("names", getProperty(userList, User::getName));
map.put("age",   getProperty(userList, User::getAge));
map.put("org",   getProperty(userList, User::getOrg));

回答by YCF_L

Generic Solution

通用解决方案

Both @Eran and @Michael gives a nice solution, I would like to solve your problem with a generic way :

@Eran 和 @Michael 都提供了一个很好的解决方案,我想用通用的方式解决您的问题:

public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
    List<User> listUsers = ...
    //Create a List which hold name of field and its value
    List<Map<String, Object>> listMapping = new ArrayList<>();
    for (User user : listUsers) {
        listMapping.add(fieldNameValue(user));
    }

    //Here group by the name of the field
    Map<String, List<Object>> result = listMapping.stream()
            .flatMap(a -> a.entrySet().stream())
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                            Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

}

//This method return a Map which hold names of attributes and its values.
static Map<String, Object> fieldNameValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
    Map<String, Object> mapping = new HashMap<>();
    for (Field field : obj.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        mapping.put(field.getName(), field.get(obj));
    }
    return mapping;
}

In this solution you don't care about the number of fields of the type.

在此解决方案中,您不关心类型字段的数量。

回答by JavaHelper

Try this one. Color bean used to separating map value and key from the list.

试试这个。用于从列表中分离映射值和键的颜色 bean。

    List<Color> colors = new ArrayList<Color>();

    colors.add(new Color("RED", "#FF0000"));
    colors.add(new Color("BLUE", "#0000FF"));
    colors.add(new Color("GREEN", "#008000"));

    // construct key-value pairs from name and code fields of Color
    Map<String, String> map = colors.stream()
                                .collect(Collectors.toMap(Color::getName, 
                                                        Color::getCode));