Swa*_*esh 13 java aggregate-functions java-8 java-stream
我们在Java中有任何聚合器函数来执行以下聚合吗?
Person {
String name;
String subject;
String department;
Long mark1;
Long mark2;
Long mark3;
}
Run Code Online (Sandbox Code Playgroud)
列表包含如下数据.
Name |Subject |Department |Mark1 |Mark2 |Mark3 --------|-----------|-----------|-------|-------|----- Clark |English |DEP1 |7 |8 |6 Michel |English |DEP1 |6 |4 |7 Dave |Maths |DEP2 |3 |5 |6 Mario |Maths |DEP1 |9 |7 |8
聚合标准是Subject&Dep.结果对象需要
Subject |Department |Mark1 |Mark2 |Mark3 ----------- |-----------|-------|-------|----- English |DEP1 |13 |12 |13 Maths |DEP2 |3 |5 |6 Maths |DEP1 |9 |7 |8
可以通过手动迭代列表并创建聚合列表来实现此聚合.示例如下.
private static List<Person> getGrouped(List<Person> origList) {
Map<String, Person> grpMap = new HashMap<String, Person>();
for (Person person : origList) {
String key = person.getDepartment() + person.getSubject();
if (grpMap.containsKey(key)) {
Person grpdPerson = grpMap.get(key);
grpdPerson.setMark1(grpdPerson.getMark1() + person.getMark1());
grpdPerson.setMark2(grpdPerson.getMark2() + person.getMark2());
grpdPerson.setMark3(grpdPerson.getMark3() + person.getMark3());
} else {
grpMap.put(key, person);
}
}
return new ArrayList<Person>(grpMap.values());
}
Run Code Online (Sandbox Code Playgroud)
但是我们可以利用Java 8的任何聚合功能或特性吗?
您可以使用减少。聚合 mark1 的示例如下。
public class Test {
static class Person {
Person(String name, String subject, String department, Long mark1, Long mark2, Long mark3) {
this.name = name;
this.subject = subject;
this.department = department;
this.mark1 = mark1;
this.mark2 = mark2;
this.mark3= mark3;
}
String name;
String subject;
String department;
Long mark1;
Long mark2;
Long mark3;
String group() {
return subject+department;
}
Long getMark1() {
return mark1;
}
}
public static void main(String[] args)
{
List<Person> list = new ArrayList<Test.Person>();
list.add(new Test.Person("Clark","English","DEP1",7l,8l,6l));
list.add(new Test.Person("Michel","English","DEP1",6l,4l,7l));
list.add(new Test.Person("Dave","Maths","DEP2",3l,5l,6l));
list.add(new Test.Person("Mario","Maths","DEP1",9l,7l,8l));
Map<String, Long> groups = list.stream().collect(Collectors.groupingBy(Person::group, Collectors.reducing(
0l, Person::getMark1, Long::sum)));
//Or alternatively as suggested by Holger
Map<String, Long> groupsNew = list.stream().collect(Collectors.groupingBy(Person::group, Collectors.summingLong(Person::getMark1)));
System.out.println(groups);
}
}
Run Code Online (Sandbox Code Playgroud)
仍在研究通过单个函数生成输出。完成后将更新。
使用JDK中的标准收集器,您可以这样做(假设创建了一个Tuple3<E1, E2, E3>类):
Map<String, Map<String, Tuple3<Long, Long, Long>>> res =
persons.stream().collect(groupingBy(p -> p.subject,
groupingBy(p -> p.department,
reducing(new Tuple3<>(0L, 0L, 0L),
p -> new Tuple3<>(p.mark1, p.mark2, p.mark3),
(t1, t2) -> new Tuple3<>(t1.e1 + t2.e1, t1.e2 + t2.e2, t1.e3 + t2.e3)))));
Run Code Online (Sandbox Code Playgroud)
这将首先按主题对元素进行分组,然后按部门对元素进行分组,并通过对它们的标记求和来减少第二个地图中的结果值。
在示例中的人员列表上运行它,您将得到输出:
Maths => DEP2 => (3, 5, 6)
Maths => DEP1 => (9, 7, 8)
English => DEP1 => (13, 12, 13)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您可能还想使用另一个使用toMap收集器的变体。逻辑保持不变,映射值的函数将创建一个包含部门作为键、学生成绩作为值的映射。合并函数将负责添加或更新映射。
Map<String, Map<String, Tuple3<Long, Long, Long>>> res3 =
persons.stream()
.collect(toMap(p -> p.subject,
p -> {
Map<String, Tuple3<Long, Long, Long>> value = new HashMap<>();
value.put(p.department, new Tuple3<>(p.mark1, p.mark2, p.mark3));
return value;
},
(v1, v2) -> {
v2.forEach((k, v) -> v1.merge(k, v, (t1, t2) -> new Tuple3<>(t1.e1 + t2.e1, t1.e2 + t2.e2, t1.e3 + t2.e3)));
return v1;
}
));
Run Code Online (Sandbox Code Playgroud)
当然,您可以质疑自己这些解决方案的“美感”,也许您想引入自定义收集器或自定义类以使意图更加清晰。
| 归档时间: |
|
| 查看次数: |
8298 次 |
| 最近记录: |