我有一个类似下面的课程。
class Student {
public Student(Set<String> seminars) {
this.seminar = seminars;
}
Set<String> seminar;
public Set<String> getSeminar()
{
return seminar;
}
}
并创建了一组像下面这样的学生。
List<Student> students = new ArrayList<Student>();
Set<String> seminars = new HashSet<String>();
seminars.add("SeminarA");
seminars.add("SeminarB");
seminars.add("SeminarC");
students.add(new Student(seminars)); //Student 1 - 3 seminars
students.add(new Student(seminars)); //Student 2 - 3 seminars
seminars = new HashSet<String>();
seminars.add("SeminarA");
seminars.add("SeminarB");
students.add(new Student(seminars)); //Student 3 - 2 seminars
seminars = new HashSet<String>();
students.add(new Student(seminars)); //Student 4 - 0 seminars
现在的问题是“我正在努力让那些参加过maximus研讨会的学生数量众多”正如你所看到的那样,有2名学生参加过3次(最多)研讨会,所以我需要了解这一点。
我使用流使用2个不同的语句实现了相同的功能
OptionalInt max = students.stream()
.map(Student::getSeminar)
.mapToInt(Set::size)
.max();
long count = students.stream()
.map(Student::getSeminar)
.filter(size -> size.size() == max.getAsInt())
.count();
有没有办法用一个声明来实现同样的目的?
要解决此问题,请使用以下代码:
students.stream()
.map(Student::getSeminar)
.collect(Collectors.groupingBy(Set::size, Collectors.counting()));
你的输出将是:
{0=1, 2=1, 3=2}
正如你所看到的,有two students
将达到最多的三个研讨会。
首先,我们使用实际集合更改了流中的所有学生对象,然后我们使用Collectors groupingBy()
方法按大小对集合进行分组。
如果您只想获得学生人数,请使用以下代码:
students.stream()
.map(Student::getSeminar)
.collect(Collectors.groupingBy(Set::size, Collectors.counting()))
.values().stream()
.max(Comparator.comparing(a->a))
.get();
你的输出将是:2
。
在大多数实际情况中,您遍历列表两次的方法是最好的。它很简单,代码很容易理解。打击保存陈述的冲动 - 你不会按分号收取费用!
但是,当源数据无法遍历两次时,存在一些可想到的情况。也许它来自一些缓慢或一次性的来源,如网络流。
如果您遇到这种情况,您可能需要定义一个自定义收集器allMaxBy
,它将所有max元素收集到下游收集器中。
然后你就可以写了
long maxCount = students.stream()
.collect(allMaxBy(
comparingInt(s -> s.getSeminar().size()),
counting()
));
这是allMaxBy
的一个实现:
public static <T, A, R> Collector<T, ?, R> allMaxBy(Comparator<? super T> cmp, Collector<? super T, A, R> downstream) {
class AllMax {
T val;
A acc = null; // null means empty
void add(T t) {
int c = acc == null ? 1 : cmp.compare(t, val);
if (c > 0) {
val = t;
acc = downstream.supplier().get();
downstream.accumulator().accept(acc, t);
} else if (c == 0) {
downstream.accumulator().accept(acc, t);
}
}
AllMax merge(AllMax other) {
if (other.acc == null) {
return this;
} else if (this.acc == null) {
return other;
}
int c = cmp.compare(this.val, other.val);
if (c == 0) {
this.acc = downstream.combiner().apply(this.acc, other.acc);
}
return c >= 0 ? this : other;
}
R finish() {
return downstream.finisher().apply(acc);
}
}
return Collector.of(AllMax::new, AllMax::add, AllMax::merge, AllMax::finish);
}
这很难看,但以下情况可行:
long count = students.stream()
.filter(s -> s.getSeminar().size() == students
.stream().mapToInt(a -> a.getSeminar().size())
.max().orElse(0))
.count();
说明:这会对学生列表进行流式处理并过滤学生,以便只有具有最大研讨会数量(这是嵌套的lambda是什么)的学生保留,然后记录该流的计数。
很少修改@ Alex的解决方案,在进行分组时对键进行排序
TreeMap<Integer, Long> agg = students.stream()
.map(Student::getSeminar)
.collect(Collectors.groupingBy(Set::size, TreeMap::new, Collectors.counting()));
System.out.println(agg);
System.out.println(agg.lastEntry().getKey() + " - " + agg.lastEntry().getValue());
产量
3 - 2