在Java 8中限制groupBy

如何通过每个条目限制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> groupByTeachers = studentClasses .stream().collect( Collectors.groupingBy(StudentClass::getTeacher)); 

如果我想限制返回的集合怎么办? 让我们假设我只想要每个老师的前N个课程。 如何做呢?

可以引入一个新的收集器来限制结果列表中的元素数量。

此收集器将保留列表的头元素( 按遭遇顺序 )。 在收集期间达到限制时,累加器和组合器会丢弃每个元素。 组合器代码有点棘手,但这样做的好处是不会添加额外的元素,只是为了以后丢弃。

 private static  Collector> 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> groupByTeachers = studentClasses.stream() .collect(groupingBy( StudentClass::getTeacher, limitingList(2) )); 

您可以使用collectAndThen在结果列表中定义修整器操作。 这样你可以限制,过滤,排序……列表:

 int limit = 2; Map> 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> 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() ,则分组结果将是不正确的。 这是保证限制的最安全的方法。

编辑:

正如评论中所述,这限制了教师,而不是每位教师的class级。 在这种情况下,你可以这样做:

 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> groupByTeachers = studentClasses.stream().collect( groupingBy(StudentClass::getTeacher, HashMap::new, collectingAndThen(toList(), list -> list.subList(0, Math.min(list.size(), N)))));