限制 groupBy 在 Java 8
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"));
本方法return简单组:
Map<String, List<StudentClass>> groupByTeachers = studentClasses
.stream().collect(
Collectors.groupingBy(StudentClass::getTeacher));
如果我想限制 returned 集合怎么办?
假设我只想要每位老师的前 N 类。怎么做到的?
为此,您需要 .stream()
地图的结果。你可以这样做:
// 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()
在初始列表上,那么分组结果就会不正确。这是保证限额最安全的方法。
编辑:
如评论中所述,这限制了教师,而不是每个教师 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<String, List<StudentClass>> groupByTeachers =
studentClasses.stream().collect(
groupingBy(StudentClass::getTeacher, HashMap::new,
collectingAndThen(toList(), list -> list.subList(0, Math.min(list.size(), 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()))));
如何限制每个条目的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"));
本方法return简单组:
Map<String, List<StudentClass>> groupByTeachers = studentClasses
.stream().collect(
Collectors.groupingBy(StudentClass::getTeacher));
如果我想限制 returned 集合怎么办? 假设我只想要每位老师的前 N 类。怎么做到的?
为此,您需要 .stream()
地图的结果。你可以这样做:
// 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()
在初始列表上,那么分组结果就会不正确。这是保证限额最安全的方法。
编辑:
如评论中所述,这限制了教师,而不是每个教师 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<String, List<StudentClass>> groupByTeachers =
studentClasses.stream().collect(
groupingBy(StudentClass::getTeacher, HashMap::new,
collectingAndThen(toList(), list -> list.subList(0, Math.min(list.size(), 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()))));