Gho*_*t93 8 java grouping java-stream
假设我有以下Student对象集合,包括Name(String),Age(int)和City(String).
我正在尝试使用Java的Stream API来实现以下类似sql的行为:
SELECT MAX(age)
FROM Students
GROUP BY city
Run Code Online (Sandbox Code Playgroud)
现在,我发现了两种不同的方法:
final List<Integer> variation1 =
students.stream()
.collect(Collectors.groupingBy(Student::getCity, Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge())))
.values()
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.map(Student::getAge)
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
而另一个:
final Collection<Integer> variation2 =
students.stream()
.collect(Collectors.groupingBy(Student::getCity,
Collectors.collectingAndThen(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()),
optional -> optional.get().getAge())))
.values();
Run Code Online (Sandbox Code Playgroud)
在这两种方式中,都必须.values() ...并过滤从收集器返回的空组.
有没有其他方法来实现这种必要的行为?
这些方法让我想起了over partition bysql语句......
谢谢
编辑:下面的所有答案都非常有趣,但不幸的是,这不是我想要的,因为我试图得到的只是价值观.我不需要键,只需要值.
Tag*_*eev 30
不要总是坚持下去groupingBy.有时toMap你需要的东西:
Collection<Integer> result = students.stream()
.collect(Collectors.toMap(Student::getCity, Student::getAge, Integer::max))
.values();
Run Code Online (Sandbox Code Playgroud)
在这里,您只需创建一个Map键,其中键是城市,值是年龄.如果几个学生拥有相同的城市,则使用合并功能,这里只选择最大年龄.它更快更清洁.
Hol*_*ger 15
作为Tagir的最佳答案的补充,使用toMap而不是groupingBy,这里是简短的解决方案,如果你想坚持groupingBy:
Collection<Integer> result = students.stream()
.collect(Collectors.groupingBy(Student::getCity,
Collectors.reducing(-1, Student::getAge, Integer::max)))
.values();
Run Code Online (Sandbox Code Playgroud)
请注意,这三个arg reducing收集器已经执行了映射操作,因此我们不需要将它与mapping收集器嵌套,进一步提供标识值以避免处理Optional.由于年龄总是积极的,因此提供-1就足够了,因为一个组总是至少有一个元素,所以身份值永远不会显示为结果.
不过,我认为Tagir的toMap解决方案在这种情况下更可取.
在groupingBy当你想获得具有最大年龄,例如学生实际基础的解决方案变得更加有趣
Collection<Student> result = students.stream().collect(
Collectors.groupingBy(Student::getCity, Collectors.reducing(null, BinaryOperator.maxBy(
Comparator.nullsFirst(Comparator.comparingInt(Student::getAge)))))
).values();
Run Code Online (Sandbox Code Playgroud)
实际上,即使这也可以使用toMap收集器表达:
Collection<Student> result = students.stream().collect(
Collectors.toMap(Student::getCity, Function.identity(),
BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge)))
).values();
Run Code Online (Sandbox Code Playgroud)
你可以用收集器表达几乎所有东西,但是groupingBy当你想要对值进行可变的减少时,它具有优势.
Here is my implementation
public class MaxByTest {
static class Student {
private int age;
private int city;
public Student(int age, int city) {
this.age = age;
this.city = city;
}
public int getCity() {
return city;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return " City : " + city + " Age : " + age;
}
}
static List<Student> students = Arrays.asList(new Student[]{
new Student(10, 1),
new Student(9, 2),
new Student(8, 1),
new Student(6, 1),
new Student(4, 1),
new Student(8, 2),
new Student(9, 2),
new Student(7, 2),
});
public static void main(String[] args) {
final Comparator<Student> comparator = (p1, p2) -> Integer.compare( p1.getAge(), p2.getAge());
final List<Student> studets =
students.stream()
.collect(Collectors.groupingBy(Student::getCity,
Collectors.maxBy(comparator))).values().stream().map(Optional::get).collect(Collectors.toList());
System.out.println(studets);
}
}
Run Code Online (Sandbox Code Playgroud)