簡體   English   中英

在Java 8中限制groupBy

[英]Limit groupBy in Java 8

如何通過每個條目限制groupBy?

例如(基於此示例: stream groupBy ):

studentClasses.add(new StudentClass("Kumar", 101, "Intro to Web"));
studentClasses.add(new StudentClass("White", 102, "Advanced Java"));
studentClasses.add(new StudentClass("Kumar", 101, "Intro to Cobol"));
studentClasses.add(new StudentClass("White", 101, "Intro to Web"));
studentClasses.add(new StudentClass("White", 102, "Advanced Web"));
studentClasses.add(new StudentClass("Sargent", 106, "Advanced Web"));
studentClasses.add(new StudentClass("Sargent", 103, "Advanced Web"));
studentClasses.add(new StudentClass("Sargent", 104, "Advanced Web"));
studentClasses.add(new StudentClass("Sargent", 105, "Advanced Web"));

此方法返回簡單組:

   Map<String, List<StudentClass>> groupByTeachers = studentClasses
            .stream().collect(
                    Collectors.groupingBy(StudentClass::getTeacher));

如果我想限制返回的集合怎么辦? 讓我們假設我只想要每個老師的前N個課程。 怎么做到呢?

可以引入一個新的收集器來限制結果列表中的元素數量。

此收集器將保留列表的頭元素( 按遭遇順序 )。 在收集期間達到限制時,累加器和組合器會丟棄每個元素。 組合器代碼有點棘手,但這樣做的好處是不會添加額外的元素,只是為了以后丟棄。

private static <T> Collector<T, ?, List<T>> limitingList(int limit) {
    return Collector.of(
                ArrayList::new, 
                (l, e) -> { if (l.size() < limit) l.add(e); }, 
                (l1, l2) -> {
                    l1.addAll(l2.subList(0, Math.min(l2.size(), Math.max(0, limit - l1.size()))));
                    return l1;
                }
           );
}

然后像這樣使用它:

Map<String, List<StudentClass>> groupByTeachers = 
       studentClasses.stream()
                     .collect(groupingBy(
                          StudentClass::getTeacher,
                          limitingList(2)
                     ));

你可以使用collectingAndThen所得到的名單上定義的整理操作。 這樣你可以限制,過濾,排序......列表:

int limit = 2;

Map<String, List<StudentClass>> groupByTeachers =
    studentClasses.stream()
                  .collect(
                       groupingBy(
                           StudentClass::getTeacher,
                           collectingAndThen(
                               toList(),
                               l -> l.stream().limit(limit).collect(toList()))));

為此,您需要.stream() Map的結果。 你可以這樣做:

// Part that comes from your example
Map<String, List<StudentClass>> groupByTeachers = studentClasses
            .stream().collect(
                    Collectors.groupingBy(StudentClass::getTeacher));

// Create a new stream and limit the result
groupByTeachers =
    groupByTeachers.entrySet().stream()
        .limit(N) // The actual limit
        .collect(Collectors.toMap(
            e -> e.getKey(),
            e -> e.getValue()
        ));

這不是一種非常理想的方法。 但是如果你在初始列表中使用.limit() ,則分組結果將是不正確的。 這是保證限制的最安全的方法。

編輯:

正如評論中所述,這限制了教師,而不是每位教師的課程。 在這種情況下,你可以這樣做:

groupByTeachers =
        groupByTeachers.entrySet().stream()
            .collect(Collectors.toMap(
                e -> e.getKey(),
                e -> e.getValue().stream().limit(N).collect(Collectors.toList()) // Limit the classes PER teacher
            ));

這將為您提供所需的結果,但它仍然會對流的所有元素進行分類:

final int N = 10;
final HashMap<String, List<StudentClass>> groupByTeachers = 
        studentClasses.stream().collect(
            groupingBy(StudentClass::getTeacher, HashMap::new,
                collectingAndThen(toList(), list -> list.subList(0, Math.min(list.size(), N)))));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM