java 8 流的收集器接口问题
Issue with java 8 stream's Collector interface
我有以下 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;
从一组消息 (Collection<Message> messages
) 中,我试图通过按收件人或发件人字段 进行分组来获得 Map<Member, List<Message>> messageMap
。 =16=]
为了进一步定义我的用例,连接的成员 (currentMember) 有许多发送和接收的消息附加到它的实例。我想检索消息并将其添加到一个集合中,该集合将包含所有已连接的成员消息(已发送或已接收),如下所示:
Collection<Message> messages = new ArrayList<Message>();
messages.addAll(currentMember.getSentMessages());
messages.addAll(currentMember.getReceivedMessages());
并且对于每个 other/opposite 成员,当前成员与其至少交换了一条消息,我想获得所有交换消息的列表。
然后我就可以构造上面的Map<Member, List<Message>> messageMap
.
流是否可以开箱即用 api 还是我需要实现自己的收集器?
如果我对你的问题的理解正确,你想通过使用接收者作为发送消息的键和使用发件人作为接收消息的键来统一处理消息。您可以通过在收集操作中使用条件来做到这一点:
Map<Member, List<Message>> map = Stream.concat(
currentMember.getSentMessages().stream(), currentMember.getReceivedMessages().stream())
.collect(Collectors.groupingBy(msg ->
msg.getSender()==currentMember? msg.getRecipient(): msg.getSender()));
根据与 currentMember
比较的结果,这使用发送方或接收方作为密钥,因此密钥将始终是“另一方”。它可能不是最有效的解决方案,因为它评估的是事先已知的信息,但它很简单。
如果你想避免已知信息的再生,你必须通过流来携带它。在没有标准 Pair
类型的情况下,您可以使用 Map.Entry
实例来封装成对的“所需密钥”和 Message
实例:
Map<Member, List<Message>> map =
Stream.concat(
currentMember.getSentMessages().stream().map(msg ->
new AbstractMap.SimpleImmutableEntry<>(msg.getRecipient(), msg)),
currentMember.getReceivedMessages().stream().map(msg ->
new AbstractMap.SimpleImmutableEntry<>(msg.getSender(), msg)))
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e->e.getValue(), Collectors.toList())));
在这里,不仅包装成对使代码复杂化,收集操作的代码也由于需要展开而变得更加复杂。
往哪个方向走取决于你……
我有以下 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;
从一组消息 ( 为了进一步定义我的用例,连接的成员 (currentMember) 有许多发送和接收的消息附加到它的实例。我想检索消息并将其添加到一个集合中,该集合将包含所有已连接的成员消息(已发送或已接收),如下所示: 并且对于每个 other/opposite 成员,当前成员与其至少交换了一条消息,我想获得所有交换消息的列表。 然后我就可以构造上面的 流是否可以开箱即用 api 还是我需要实现自己的收集器?Collection<Message> messages
) 中,我试图通过按收件人或发件人字段 进行分组来获得 Map<Member, List<Message>> messageMap
。 =16=]
Collection<Message> messages = new ArrayList<Message>();
messages.addAll(currentMember.getSentMessages());
messages.addAll(currentMember.getReceivedMessages());
Map<Member, List<Message>> messageMap
.
如果我对你的问题的理解正确,你想通过使用接收者作为发送消息的键和使用发件人作为接收消息的键来统一处理消息。您可以通过在收集操作中使用条件来做到这一点:
Map<Member, List<Message>> map = Stream.concat(
currentMember.getSentMessages().stream(), currentMember.getReceivedMessages().stream())
.collect(Collectors.groupingBy(msg ->
msg.getSender()==currentMember? msg.getRecipient(): msg.getSender()));
根据与 currentMember
比较的结果,这使用发送方或接收方作为密钥,因此密钥将始终是“另一方”。它可能不是最有效的解决方案,因为它评估的是事先已知的信息,但它很简单。
如果你想避免已知信息的再生,你必须通过流来携带它。在没有标准 Pair
类型的情况下,您可以使用 Map.Entry
实例来封装成对的“所需密钥”和 Message
实例:
Map<Member, List<Message>> map =
Stream.concat(
currentMember.getSentMessages().stream().map(msg ->
new AbstractMap.SimpleImmutableEntry<>(msg.getRecipient(), msg)),
currentMember.getReceivedMessages().stream().map(msg ->
new AbstractMap.SimpleImmutableEntry<>(msg.getSender(), msg)))
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e->e.getValue(), Collectors.toList())));
在这里,不仅包装成对使代码复杂化,收集操作的代码也由于需要展开而变得更加复杂。
往哪个方向走取决于你……