Transforming/GroupingBy 列表中的值

Transforming/GroupingBy values inside List

给定 List<Student> 其中每个 StudentList<Book>,按 Book 分组 List<Student>

工作解决方案(使用 Java 9) 我已经有下面提到的工作代码

public class Demo {
    public static void main(String[] args) {

    List<Student> studs = createStudents();

        var data = studs.stream()
                .flatMap(s -> s.getBooks()
                        .stream()
                        .map(b -> Pair.of(b, s))) // <------ is possible without using external class?
                .collect(Collectors.groupingBy(
                        p -> p.getBook(),
                        Collectors.mapping(p -> p.getStud().getFirstName(), Collectors.toList())));
        System.out.println("All books: " + data);
    }

    private static List<Student> createStudents() {
        Book javaCompleteReference = new Book("Java Complete Reference");
        Book ocjp = new Book("Java OCJP");
        Book sql = new Book("SQL");
        Book spring = new Book("SPRING in Action");

        return List.of(
                new Student(1, "A", List.of(javaCompleteReference, spring)),
                new Student(2, "B", List.of(spring, sql, ocjp)),
                new Student(3, "C", List.of(javaCompleteReference, sql))
        );
    }
}

正在寻找:

  1. 我使用了 flatMap 和中间 class 对。是否可以使用 Pair class
  2. 不使用中间转换来实现最终结果

代码库:

public class Book {
    private String title;
    private String id;
    private Integer pages;

    public Book(String title, String id, Integer pages) {
        this.title = title;
        this.id = id;
        this.pages = pages;
    }

    public Book(String title) { this.title = title; }

    public String getTitle() { return title; }

    public String getId() { return id; }

    public Integer getPages() { return pages; }

    @Override
    public String toString() { return title; }
}

class Pair {
    Book book;
    Student stud;

    private Pair(Book book, Student stud) {
        this.book = book;
        this.stud = stud;
    }

    static Pair of(Book book, Student stud) { return new Pair(book, stud); }

    public Book getBook() { return book; }

    public Student getStud() { return stud; }
}

public class Student {
    private Integer id;
    private String firstName;
    private List<Book> books;

    public Student(Integer id, String firstName, List<Book> books) {
        this.id = id;
        this.firstName = firstName;
        this.books = books;
    }

    public Integer getId() { return id; }

    public String getFirstName() { return firstName; }

    public List<Book> getBooks() { return books; }
}

输出:All books: {Java Complete Reference=[A, C], SQL=[B, C], SPRING in Action=[A, B], Java OCJP=[B]}

is possible without using external class?

是的,甚至在 java 的早期版本中也是可能的。只需使用 AbstractMap.SimpleEntry<K, V> 代替:

.map(b -> new AbstractMap.SimpleEntry<>(b, s)))

然后您可以通过调用 getKey()getValue() 来访问这些值。

不可能 使用 Stream API 只要您需要保留 both 关于学生姓名的信息 上下文中的每个 Book 以形成一个输出映射。平面映射到单个元素(对)是唯一可行的方法。

一个证明和替代(我想不是更好)解决方案是使用 Collectors.flatMappingCollectors.toMap 的组合,您可以在其中定义所有 keyMappervalueMappermergeFunction。你可以清楚地看到,在Collectors.grouping中,你需要组合所有Student的名字每个Book的关系。

var data = studs.stream().collect(Collectors.flatMapping(
    student -> student.getBooks()
                      .stream()
                      .map(book -> new AbstractMap.SimpleEntry<>(
                              student.getFirstName(), book)),
    Collectors.toMap(
        AbstractMap.SimpleEntry::getValue,
        entry -> new ArrayList<>(List.of(entry.getKey())),
        (leftList, rightList) -> { leftList.addAll(rightList); return leftList; })));