Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a map into another map using the java 8 stream API

Say I have the following map:

Map<Member, List<Message>> messages = ... //constructed somehow

I would like to use the java 8 stream api in order to obtain a:

SortedMap<Message, Member> latestMessages = ...

Where the comparator passed into the SortedMap/TreeMap would be based on the message sendDate field.

Furthermore, of the list of sent messages, I would select the latest message which would become the key to the sorted map.

How can I achieve that?

edit 1:

Comparator<Message> bySendDate = Comparator.comparing(Message::getSendDate);
SortedMap<Message, Member> latestMessages = third.entrySet().stream()
        .collect(Collectors.toMap(e -> e.getValue().stream().max(bySendDate).get(), Map.Entry::getKey, (x, y) -> {
            throw new AssertionError();
        }, () -> new TreeMap(bySendDate.thenComparing(Comparator.comparing(Message::getId)))));

I get the following compilation error:

The method collect(Collector<? super T,A,R>) in the type Stream<T> is not applicable for the arguments (Collector<Map.Entry<Member,List<Message>>,?,TreeMap>)
like image 698
balteo Avatar asked Mar 02 '15 16:03

balteo


People also ask

Can we convert map to stream in Java?

Converting complete Map<Key, Value> into Stream: This can be done with the help of Map. entrySet() method which returns a Set view of the mappings contained in this map. In Java 8, this returned set can be easily converted into a Stream of key-value pairs using Set. stream() method.

Can we use map stream in Java 8?

Java 8 Stream's map method is intermediate operation and consumes single element forom input Stream and produces single element to output Stream. It simply used to convert Stream of one type to another. Let's see method signature of Stream's map method.

How do you convert an entry to a map?

There is no inbuilt API in java for direct conversion between HashSet and HashMap , you need to iterate through set and using Entry fill in map. Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.


1 Answers

Let’s dissolve this into two parts.

First, transform Map<Member, List<Message>> messages into a Map<Message, Member> latestMessages by reducing the messages for a particular communication partner (Member) to the latest:

Map<Message, Member> latestMessages0 = messages.entrySet().stream()
    .collect(Collectors.toMap(
        e -> e.getValue().stream().max(Comparator.comparing(Message::getSendDate)).get(),
        Map.Entry::getKey));

Here, the resulting map isn’t sorted but each mapping will contain the latest message shared with that participant.


Second, if you want to have the resulting map sorted by sendDate, you have to add another secondary sort criteria to avoid losing Messages which happen to have the same date. Assuming that you have a Long ID that is unique, adding this ID as secondary sort criteria for messages with the same date would be sufficient:

Comparator<Message> bySendDate=Comparator.comparing(Message::getSendDate);
SortedMap<Message, Member> latestMessages = messages.entrySet().stream()
   .collect(Collectors.toMap(
       e -> e.getValue().stream().max(bySendDate).get(),
       Map.Entry::getKey, (x,y) -> {throw new AssertionError();},
       ()->new TreeMap<>(bySendDate.thenComparing(Comparator.comparing(Message::getId)))));

Since sorting by the unique IDs should solve any ambiguity, I provided a merge function which will unconditionally throw, as calling it should never be required.

like image 142
Holger Avatar answered Oct 10 '22 21:10

Holger