按列表中的多个枚举值对对象列表进行分组

Group a list of objects by multiple enum values in a list

上下文:

我有一个 Topic 枚举:

public enum Topic {
    RELATIONSHIP, DATING, EDUCATION, FITNESS, NEWS, RENT, EVENTS, GIVEAWAY, SALE
}

和一个 class 成员,其字段是主题列表。一名成员至少有一个,最多 9 个主题,he/she 已订阅。

@AllArgsConstructor
@Getter
@ToString
static class Member {
    String id;
    List<Topic> topics;
    String email;
}

我每周收到一次消息列表。每条消息都属于一种主题类型。消息列表不包含具有相同主题的消息(没有重复的主题且从不为空,1 到 9 个条目)

@AllArgsConstructor
@Getter
@ToString
static class Message {
    String id;
    Topic topic;
    String content;
}

问题:

给定一个消息列表和一个成员列表,创建一个映射 Map<Message, List<Member>> 用于向每个成员发送通知。每个成员应该只收到 he/she 已订阅的主题的通知。

我的做法:

  1. 从消息列表创建一个 Map<Topic, Message>(如您在下面的示例中看到的那样完成)
  2. 从成员列表创建 Map<Topic, List<Member>>。 (我被困在这里,不知道如何从成员主题列表中提取单个主题来创建我想要的地图)
  3. 在我得到上面的两个映射后创建一个最终映射 <Message, List<Member>> 将第一个映射的值映射到第二个映射的相应值。应该很简单。

示例设置:

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

public class Example {
    public static void main(String[] args) {
        List<Message> messages = List.of(new Message("KW41",Topic.EDUCATION,"Some educational content published"),
                                         new Message("KW41",Topic.FITNESS,"Some fitness content published"),
                                         new Message("KW41",Topic.DATING,"Some dating content published"),
                                         new Message("KW41",Topic.RENT,"Some rent content published"));

        List<Member> memberList = List.of(new Member("1", List.of(Topic.SALE, Topic.RENT), "john@gmail.com"),
                                          new Member("2", List.of(Topic.DATING, Topic.NEWS), "jess@gmail.com"),
                                          new Member("3", List.of(Topic.EDUCATION), "jane@gmail.com"),
                                          new Member("4", List.of(Topic.FITNESS, Topic.RENT, Topic.EDUCATION), "jojo@gmail.com"),
                                          new Member("5", List.of(Topic.RELATIONSHIP), "jina@gmail.com"),
                                          new Member("6", List.of(Topic.SALE, Topic.NEWS), "jimi@gmail.com"),
                                          new Member("7", List.of(Topic.NEWS, Topic.EVENTS), "jd@gmail.com"));

        Map<Topic,Message> messageMap = messages.stream().collect(Collectors.toMap(Message::getTopic, Function.identity()));
        messageMap.entrySet().forEach(System.out::println);
    }

    @AllArgsConstructor
    @Getter
    @ToString
    static class Member {
        String id;
        List<Topic> topics;
        String email;
    }

    @AllArgsConstructor
    @Getter
    @ToString
    static class Message {
        String id;
        Topic topic;
        String content;
    }

    enum Topic {
        RELATIONSHIP, DATING, EDUCATION, FITNESS, NEWS, RENT, EVENTS, GIVEAWAY, SALE
    }
}

如何从成员列表中获得按主题分组的 Map<Topic, List<Member>>。 (我不能轻易做到 stream.collect(Collectors.groupingBy(Member::getTopics)) 因为它是一个列表。我需要先以某种方式提取它们)。所需的地图应包含(仅添加成员 ID 以提高可读性)

Topic.RELATIONSHIP=[5] 
Topic.DATING=[2]  
Topic.EDUCATION=[3, 4] 
Topic.FITNESS=[4]  
Topic.NEWS=[2, 6, 7]  
Topic.RENT=[1, 4]  
Topic.EVENTS=[7]   
Topic.SALE=[1, 6] 

展平成元组可能会有所帮助。

例如:

@Value
class Tuple<L, R> {
    L left;
    R right;
}

你可以:

Stream<Member> stream = ...;
Map<Topic, List<Member>> result = stream
        .flatMap(member -> member.getTopics().stream().map(topic -> new Tuple<>(topic, member)))
        .collect(groupingBy(Tuple::getLeft, mapping(Tuple::getRight, toList())));