I am trying to use java 8 streams in order to perform a manipulation on a list of Message
and Member
.
A Message
is a entity from my domain model. A Message
has a sender
field and a receiver
field of type Member
.
A Member
is the second entity from my domain model. A Member
has a collection of sent messages and a collection of received messages.
Member JPA entity :
@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 :
@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.
Can anyone please provice pointers or ideas on how to use java 8 streams in order to achieve this?
edit 1 : Here is a sample input/output as requested:
Input (content of message
table):
Output for member with #2 should be messages with #5 and #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 .
Other messages between member #2 and member #1 are older so not taken into account.
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.
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:
Message
in the stream 1->2
has to be the same mapping has 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:
[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.
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.
I'm not sure if the entire process can be done in a single Stream pipeline.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.