高级java 8流使用问题

我试图使用java 8流,以便在MessageMember列表上执行操作。

Message是我的域模型中的实体。 Message具有sender字段和Member类型的receiver字段。

Member是我的域模型中的第二个实体。 Member具有已发送消息的集合和已接收消息的集合。

成员JPA实体

 @Entity public class Member implements UserDetails { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; ... @OneToMany(fetch = FetchType.LAZY, mappedBy = "sender") private Collection sentMessages; @OneToMany(fetch = FetchType.LAZY, mappedBy = "recipient") private Collection receivedMessages; @Version private Integer version; 

消息JPA实体

 @Entity public class Message { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotNull @ManyToOne(fetch = FetchType.LAZY) private Member sender; @NotNull @ManyToOne(fetch = FetchType.LAZY) private Member recipient; @NotNull @Temporal(TemporalType.TIMESTAMP) @DateTimeFormat(pattern = "dd/MM/yyyy HH:mm:ss") private Date sendDate; private boolean messageRead; @NotNull @Size(min = 5, max = 500) @Column(length = 500) private String text; @Version private Integer version; 

Collection (来自给定成员的已发送和已接收消息的组合列表)我想获得一个SortedMap ,其中包含来自另一个成员的最新发送或接收的消息作为密钥,而另一个成员作为密钥价值。

任何人都可以请求如何使用java 8流来实现这一点的指针或想法?

编辑1 :这是一个请求的输入/输出示例:

输入( message表的内容):

在此处输入图像描述

#2成员的输出应为#5和#4的消息

说明:涉及成员#2的成员#1的最新发送或接收消息是消息#4 ,涉及成员#2的成员#2的最新消息是消息#5

成员#2和成员#1之间的其他消息较旧,因此不予考虑。

最终目标是实现一个消息框,例如whatsapp / hangout或fb,我在当前登录的用户和每个其他用户之间显示最后发送或接收的消息。

如果我理解得很好,这可能是一个可能的实现:

 SortedMap sortedMap = new TreeMap<>(Comparator.comparing(Message::getSendDate).reversed()); myCombinedCollection.forEach(m -> sortedMap.put(m, m.getRecipient())); 

如果你真的想要一个单行,那应该这样做:

 SortedMap sort = myCombinedCollection.stream() .collect(Collectors.toMap(m -> m, Message::getRecipient, (m1, m2) -> m2, () -> new TreeMap<>(Comparator.comparing(Message::getSendDate).reversed()))); 

toMap收集器可以分解如下:

  • 对于流中的每条Message
  • 构造一个新的条目,其中消息作为键,接收者成员作为值
  • 你将把它放在一个TreeMap中,它将消息作为发送日期进行比较
  • 如果两条消息具有相同的发送日期,则只保留一条消息

自编辑以来,映射实际上更复杂,因为您必须实际获取消息的发送日期以及发送方和接收方之间的映射。 因此,您必须创建一个返回唯一映射的函数(警告:您需要1->2必须是相同的映射具有2->1 )。

这是我使用的映射方法(你可能想要实现自己的;它仅用于示例):

 public String mapping() { long minId = Math.min(sender.id, recipient.id); long maxId = Math.max(sender.id, recipient.id); return (maxId + "->" + minId); } 

完成后,将消息放入映射中,并使用下游收集器获取最新消息:

  Map> map = myCombinedCollection.stream() .collect(groupingBy(Message::mapping, maxBy(Comparator.comparing(Message::getSendDate)))); 

这导致:

 6->2 => Optional[5-Thu Apr 01 00:00:00 CEST 3915:Hi there dude] 2->1 => Optional[4-Fri Apr 02 09:45:00 CEST 3915:Je suis disponible] 

关键在这里并不重要,因为您只对消息感兴趣。 您可以做的是从此地图获取List ,首先过滤空的值:

 List messages = map.entrySet().stream() .filter(e -> e.getValue().isPresent()) .map(e -> e.getValue().get()) .collect(toList()); 

最终输出:

 [5-Thu Apr 01 00:00:00 CEST 3915:Hi there dude, 4-Fri Apr 02 09:45:00 CEST 3915:Je suis disponible] 

如果你想看一个小实现,我创建了这个小小的要点 。


我不知道这些collections是如何填补的; 但也许值得为每个成员实现缓存映射,以便在发送/接收新消息时更新映射。 这比获取所有组合消息更有效。

希望能帮助到你! 🙂

您可以按收件人对发送的邮件进行分组,然后找到每个收件人的最新邮件(未测试,可能有一些拼写错误):

 Map memberMessages = sentMessages.stream() .collect(Collectors.groupingBy(Message::getRecipient, Collectors.maxBy(Comparator.comparingLong(m -> m.getSendDate().getTime()))))); 

这为您提供了与您想要的相反的关键和价值,但它应该让您开始。 例如,您可以通过创建此地图条目的流,然后使用toMap收集它来创建反转地图,从而反转流程中的键和值。

我不确定整个过程是否可以在单个Stream管道中完成。