简体   繁体   English

创建 Map <string, set<string> &gt; 从列表中<myobject>使用 Stream API</myobject></string,>

[英]Create a Map<String, Set<String>> from a List<MyObject> using Stream API

I have a list of LoggerMessageDto objects.我有一个LoggerMessageDto对象列表。

LoggerMessageDto has two String fields: message and type . LoggerMessageDto有两个String字段: messagetype

I want to convert this List into a Map with the following contents:我想将此List转换为具有以下内容的Map

 key: "types",     value: Set.of(LoggerMessageDto::gettype) 
 key: "messages" , value: Set.of(LoggerMessageDto::getMessage)

My attempt:我的尝试:

List<LoggerMessageDto> result = getSomeResult();
Set<String> journalTypes = new HashSet<>();
Set<String> messages = new HashSet<>();

result.forEach(item -> {
    journalTypes.add(item.getType());
    messages.add(item.getMessage());
});

String typesKey = "types";
String messagesKey = "messages";

Map<String, Set<String>> map = Map.of(
    typesKey, journalTypes,
    messagesKey, messages
);

How can I achieve this using Stream API?如何使用 Stream API 实现这一目标?

You can use Java 12 collector teeing , which expects three arguments : two collectors and a function .您可以使用 Java 12 个收集器teeing ,它需要三个 arguments :两个收集器和一个function Each stream element gets consumed by both of the provided collectors and when they are done the function merges their results.每个 stream 元素都会被两个提供的收集器消耗,当它们完成时, function 会合并它们的结果。

As both downstream collectors of teeing we can use collector mapping() in conjunction with the collector toSet() .作为teeing的两个下游收集器,我们可以将收集器mapping()收集器toSet()结合使用。

public static Map<String, Set<String>> toMessagesByType(List<LoggerMessageDto> loggerMessageList,
                                                        String typesKey,
                                                        String messagesKey) {
    return  loggerMessageList.stream()
        .collect(Collectors.teeing(
            Collectors.mapping(LoggerMessageDto::getType, Collectors.toSet()),
            Collectors.mapping(LoggerMessageDto::getMessage, Collectors.toSet()),
            (types, messages) -> Map.of(
                typesKey, types,
                messagesKey, messages
            )
        ));
}

Also, it's worth to mention that enums are more handy and reliable than strings, as @Joop Eggen has point out in the comment .此外,值得一提的是,枚举比字符串更方便和可靠,正如@Joop Eggen评论中指出的那样。 Apart from saving you from typo which might occur while using strings, enums have an extensive language support (specialized collections: EnumMap , EnumSet ; they can be used in custom annotations; and in switch expression/statements, etc.).除了避免使用字符串时可能出现的拼写错误之外,枚举还具有广泛的语言支持(专门的 collections: EnumMapEnumSet ;它们可用于自定义注释;以及switch表达式/语句等)。

The Answer by Ivanchenko is correct and quite clever.伊万琴科的回答是正确的,非常聪明。 But that solution requires Java 12+, and you later commented that you must use Java 11.但是该解决方案需要 Java 12+, 您后来评论说您必须使用 Java 11。

list.stream().map( getter ).collect( …

Here is another solution, this one working in Java 11.这是另一种解决方案,这个解决方案在 Java 11 中工作。

Here we run through the list twice.在这里,我们遍历列表两次。 On the first run, we use the getter method type to extract the type member field on the Log class.在第一次运行时,我们使用 getter 方法type来提取Log class 上的type成员字段。 We collect those strings into a Set .我们将这些字符串收集到一个Set中。 Then we make a second run through the List of Log objects, this time extracting the message member field by calling the getter message .然后我们对Log对象List进行第二次运行,这次通过调用 getter message来提取message成员字段。 Those strings are also collected into a Set .这些字符串也被收集到一个Set中。

We put each of the two sets into a Map .我们将两组中的每一个放入Map中。

Map < String, Set < String > > map =
        Map.of(
                "types" , list.stream().map( Log :: type ).collect( Collectors.toSet() ) ,
                "messages" , list.stream().map( Log :: message ).collect( Collectors.toSet() )
        );

Caveat: This approach is a little bit inefficient in that it loops the list twice, once for "types" and once for "messages".警告:这种方法有点低效,因为它循环列表两次,一次用于“类型”,一次用于“消息”。 But unless you are doing this many times for huge amounts of data, the impact on performance should be insignificant.但除非您针对大量数据多次执行此操作,否则对性能的影响应该是微不足道的。

Generally best to work with unmodifiable collections until you know otherwise.通常最好与不可修改的 collections 一起使用,除非您另有了解。 So let's make those sets unmodifiable .所以让我们让这些集合不可修改 The Map.of is already producing an unmodifiable Map object. Map.of已经在生产不可修改的Map object。

Map < String, Set < String > > map =
        Map.of(
                "types" , list.stream().map( Log :: type ).collect( Collectors.toUnmodifiableSet() ) ,
                "messages" , list.stream().map( Log :: message ).collect( Collectors.toUnmodifiableSet() )
        );

Here is a complete example.这是一个完整的例子。 For brevity I used the records feature in Java 16+, but you could just as well define a conventional class.为简洁起见,我使用了 Java 16+ 中的记录功能,但您也可以定义一个常规的 class。 By the way, note that in Java 16+, records, enums, and interfaces can all be declared locally.顺便提一下,在 Java 16+ 中,记录、枚举和接口都可以在本地声明。

record Log( String type , String message ) { }
List < Log > list =
        List.of(
                new Log( "INFO" , "dog" ) ,
                new Log( "ERROR" , "dog" ) ,
                new Log( "INFO" , "cat" ) ,
                new Log( "INFO" , "cat" ) ,
                new Log( "DEBUG" , "cat" )
        );

Map < String, Set < String > > map =
        Map.of(
                "types" , list.stream().map( Log :: type ).collect( Collectors.toUnmodifiableSet() ) ,
                "messages" , list.stream().map( Log :: message ).collect( Collectors.toUnmodifiableSet() )
        );

System.out.println( "map = " + map );

map = {types=[INFO, DEBUG, ERROR], messages=[cat, dog]} map = {类型=[信息,调试,错误],消息=[猫,狗]}

By the way, be aware that Set and Map by definition may iterate in any arbitrary order .顺便说一句,请注意SetMap根据定义可以以任意顺序迭代 The order may change even between two iterations done one after another.即使在一个接一个地完成的两次迭代之间,顺序也可能发生变化。 If order matters to you, use a NavigableSet implementation and a NavigableMap implementation.如果顺序对您很重要,请使用NavigableSet实现和NavigableMap实现。

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

相关问题 转换列表<myobject>至 Map <string, map<string,string> &gt; 使用 Java Stream API</string,></myobject> - Convert List<MyObject> to Map<String, Map<String,String>> using Java Stream API 使用列表中的 Stream 创建 Map <stream<string> &gt; </stream<string> - Create a Map using Stream from List<Stream<String>> 是否可以从Map中创建String [] [] <String, String> 使用Stream API? - Is it possible to make a String[][] from a Map<String, String> using the Stream API? Java 流收集到 Map <String, Map<Integer, MyObject> &gt; - Java stream collect to Map<String, Map<Integer, MyObject>> 地图 <String, MyObject> 在jaxb - Map<String, MyObject> in jaxb 如何使用java中的流API从字符串的映射值转换字符串集 - How to convert set of string from map values of string using stream API in java 将List转换<A>为Map</a> <String,List<A> <A>&gt;使用java 8流API</a> - converting List<A> to Map<String,List<A>> using java 8 stream API 如何放置(字符串,列表 <MyObject> )进入地图 <String, List<Object> &gt; - How to put(String, List<MyObject>) into a Map<String, List<Object>> 创建 Map <string, list<object> &gt; 列表元素排序的地方,使用 Stream API</string,> - Create Map<String, List<Object>> where the elements of the lists are sorted, using Stream API Java地图 <String, MyObject> 到地图 <String, String> - Java Map<String, MyObject> to Map<String, String>
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM