简体   繁体   English

Java 8流高级用法的问题

[英]Issue with advanced java 8 stream usage

I am trying to use java 8 streams in order to perform a manipulation on a list of Message and Member . 我试图使用Java 8流,以便对MessageMember列表执行操作。

A Message is a entity from my domain model. Message是我的域模型中的实体。 A Message has a sender field and a receiver field of type Member . Message具有类型为Membersender字段和receiver字段。

A Member is the second entity from my domain model. Member是我的域模型中的第二个实体。 A Member has a collection of sent messages and a collection of received messages. Member具有已发送消息的集合和已接收消息的集合。

Member JPA entity : 会员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<Message> sentMessages;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "recipient")
    private Collection<Message> receivedMessages;

    @Version
    private Integer version;

Message JPA entity : 消息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;

From a Collection<Message> (the combined list of sent and received messages from a given member) I want to obtain a SortedMap<Message, Member> that contains the latest sent or received message from another member as the key and that other member as the value. 我想从Collection<Message> (来自给定成员的已发送和已接收消息的组合列表)中获取SortedMap<Message, Member> ,其中包含来自另一个成员的最新发送或接收消息作为键,而另一个成员为价值。

Can anyone please provice pointers or ideas on how to use java 8 streams in order to achieve this? 任何人都可以请教如何使用Java 8流的指针或想法,以实现此目标?

edit 1 : Here is a sample input/output as requested: 编辑1 :这是请求的示例输入/输出:

Input (content of message table): 输入( message表的内容):

在此处输入图片说明

Output for member with #2 should be messages with #5 and #4 #2成员的输出应该是#5和#4的消息

Explanation: the latest sent or received messages involving member #2 with member #1 is message #4 and the latest message involving member #2 with member #6 is message #5 . 说明:最新的发送或涉及部件#2构件#1接收到的消息是消息#4和涉及部件#2构件#6的最新消息是消息#5。

Other messages between member #2 and member #1 are older so not taken into account. 成员#2和成员#1之间的其他消息较旧,因此未考虑在内。

The ultimate goal is to achieve a message box such as whatsapp/hangout or fb whereby I display the last sent or received message between the currently logged user and each of the other users. 最终目标是实现一个消息框,例如whatsapp / hangout或fb,通过该消息框,我可以显示当前登录的用户与其他每个用户之间最后发送或接收的消息。

If I understood well, this (not a one-liner), is a possible implementation: 如果我很好理解,这(不是单线的)是可能的实现:

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

If you really want a one-liner, this should do it: 如果您真的想要单线,则应该这样做:

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

The toMap collector can be decompose as the following: toMap收集器可以按以下方式分解:

  • for each Message in the stream 对于流中的每个Message
  • construct a new entry with the message as the key and the recipient member as the value 构造一个新条目,将消息作为关键字,并将接收者成员作为值
  • which you would put in a TreeMap that is comparing the message as their send date 您将其放置在TreeMap中,该MapMap将消息作为发送日期进行比较
  • if two messages have the same send date, keep only one 如果两条消息的发送日期相同,则仅保留一条


Since your edit, the mapping is actually more complex because you have to take in fact the sending date of the message and the mapping between the sender and the receiver. 自进行编辑以来,映射实际上更加复杂,因为实际上您必须考虑消息的发送日期以及发送者和接收者之间的映射。 So you have to create a function that returns a unique mapping (warning: you need that 1->2 has to be the same mapping has 2->1 ). 因此,您必须创建一个返回唯一映射的函数(警告:您需要1->2必须与具有2->1映射相同)。

This the mapping method I used (you may want to implement your own; it's just for the example): 这是我使用的映射方法(您可能想实现自己的映射;仅用于示例):

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

Once done, put the messages in the map, and use a downstream collector to get the latest message: 完成后,将消息放入地图中,并使用下游收集器获取最新消息:

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]

which results in: 结果是:

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

The key is not important here since you are only interested in the messages. 该键在这里并不重要,因为您仅对消息感兴趣。 What you can do is get a List<Message> from this map, filtering the values that are empty first: 您可以做的是从此映射中获取List<Message> ,首先过滤为空的值:

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

Final output: 最终输出:

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

I created this small gist if you want to see a small implementation. 如果您想看到一个小的实现,我就创建了这个小要点


I don't know how those collections are filled; 我不知道这些收藏品是怎么装的。 but maybe it's worth to implement a caching map for each member so that when a new message is sent/received, you update the mapping. 但是也许值得为每个成员实现一个缓存映射,以便在发送/接收新消息时,您可以更新该映射。 This would be more efficient than getting all the combined messages. 这比获取所有合并的消息更有效。

Hope it helps! 希望能帮助到你! :) :)

You can group the sent messages by recipient and then find the latest message for each recipient (not tested, might have some typos) : 您可以按收件人对发送的邮件进行分组,然后查找每个收件人的最新邮件(未经测试,可能有错字):

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

This gives you the key and value reversed from what you wanted, but it should get you started. 这为您提供了与您想要的相反的键和值,但这应该可以帮助您入门。 For example, you can create the reversed map by creating a Stream of this map's entries and then collecting it with toMap , reversing the key and value in the process. 例如,您可以通过创建此映射条目的Stream,然后使用toMap收集它,在过程中反转键和值,从而创建反向映射。

I'm not sure if the entire process can be done in a single Stream pipeline. 我不确定整个过程是否可以在单个Stream管道中完成。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM