使用Java 8 Stream API将一个地图转换为另一个地图

6

假设我有以下地图:

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

我希望使用Java 8 Stream API来获取以下内容:

SortedMap<Message, Member> latestMessages = ...

需要传递给SortedMap/TreeMap的比较器应基于消息的sendDate字段。

此外,在发送消息列表中,我将选择最新的消息作为排序映射的键。

我该如何实现这个功能?

编辑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)))));

我遇到了以下编译错误:

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

1
你确定要这样做吗?这将意味着即使存在相同日期但发送者和接收者不同的不同消息,也只能有一个具有特定“sendDate”的“Message”。 - Holger
是的。基本上,我将拥有发送给或从给定成员接收的最新消息。 - balteo
@Holger 我认为这与这个问题相同:http://stackoverflow.com/questions/28810301/issue-with-advanced-java-8-stream-usage 我已经想出了一些东西,但也许你有更好的实现方法(起初我对问题不是很清楚)。;-) - Alexis C.
嗨ZouZou。实际上这个问题稍有不同,尽管你提供的回答非常有用,但我还需要回答这个问题。 :-) - balteo
@Holger:你建议我使用哪种集合? - balteo
显示剩余2条评论
1个回答

13

让我们将其分为两部分。

首先,通过“缩减”来将 Map<Member, List<Message>> messages 转换为 Map<Message, Member> latestMessages,以便将特定通信伙伴(Member)的消息缩减到最新状态:

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

这里,生成的map没有排序,但每个映射将包含与该参与者共享的最新消息。


其次,如果您希望按照发送日期对生成的地图进行排序,您必须添加另一个次要排序标准,以避免丢失在同一日期发生的Messages。假设您有一个唯一的Long ID,则将此ID作为具有相同日期的消息的次要排序标准就足够了:

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

由于按唯一ID排序应该解决任何歧义,因此我提供了一个合并函数,如果调用它是不必要的,它将无条件抛出异常。


非常感谢。然而我遇到了上述错误...我已经相应地编辑了我的帖子。 - balteo
2
你使用哪个编译器?请注意,new TreeMap(…) 应该是 new TreeMap<>(…),我忘记了这个 <> 的东西,但我的编译器只会产生一个警告(仅在 -Xlint 下),但也许你的编译器更挑剔... - Holger
今天正是我需要的东西;-) - GhostCat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接