我是 Java 8 的新手,在实施已提供的类似问题解决方案时遇到了麻烦。请帮忙。
在 Java 8 中,如何对三个字段进行分组,该三个字段返回多个行,这些行必须对其余两个整数字段求和。 在下面的 dto/pojo 类中,需要根据 uuid、msgDate 和通道组合的唯一键对传入计数和传出计数字段进行求和。
public class ReportData {
private String uuid;
private String msgDate;
private String channel;
private Integer incomingCount;
private Integer outgoingCount;
}
//初始化列表作为示例。
List<ReportData> list1 = new ArrayList<>();
list1.add(new ReportData("c9c3a519","December 2023", "digital", 5, 0 ));
list1.add(new ReportData("c9c3a519","December 2023", "digital", 3, 0 ));
list1.add(new ReportData("c9c3a519","December 2023", "digital", 0, 3 ));
list1.add(new ReportData("c9c3a519","November 2023", "digital", 4, 0 ));
list1.add(new ReportData("c9c3a519","November 2023", "digital", 0, 4 ));
list1.add(new ReportData("c9c3a519","December 2023", "manual", 5, 0 ));
list1.add(new ReportData("c9c3a519","December 2023", "manual", 3, 0 ));
list1.add(new ReportData("c9c3a519","December 2023", "manual", 0, 3 ));
list1.add(new ReportData("c9c3a519","November 2023", "manual", 4, 0 ));
list1.add(new ReportData("c9c3a519","November 2023", "manual", 0, 4 ));
list1.add(new ReportData("3de4c44f","December 2023", "digital", 5, 0 ));
list1.add(new ReportData("3de4c44f","December 2023", "digital", 0, 3 ));
list1.add(new ReportData("3de4c44f","November 2023", "digital", 4, 0 ));
list1.add(new ReportData("3de4c44f","November 2023", "digital", 0, 4 ));
list1.add(new ReportData("3de4c44f","December 2023", "manual", 5, 0 ));
list1.add(new ReportData("3de4c44f","December 2023", "manual", 0, 3 ));
list1.add(new ReportData("3de4c44f","November 2023", "manual", 4, 0 ));
list1.add(new ReportData("3de4c44f","November 2023", "manual", 0, 4 ));
输出对象应具有如下数据:
uuid msgDate 通道传入计数传出计数
c9c3a519 2023 年 12 月数字 8 3
c9c3a519 2023 年 11 月 数字 4 4
c9c3a519 2023 年 12 月手册 8 3
c9c3a519 2023 年 11 月手册 4 4
...
...
...
将结果收集到地图中。此示例将使用 Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapFactory)。另外,为了简洁起见,我使用 lombok 注释。
首先创建类来表示要分组的键和聚合数据:
@AllArgsConstructor
@Getter
public class Count {
private final int in;
private final int out;
public Count merge(Count other) {
return new Count(this.in + other.in, this.out + other.out);
}
@Override
public String toString() {
return in + " " + out;
}
}
@AllArgsConstructor
public class Key {
private final String uuid;
private final String date;
private final String channel;
@Override
public int hashCode() {
return Objects.hash(uuid, date, channel);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Key)) {
return false;
}
Key other = (Key) obj;
return uuid.equals(other.uuid) && date.equals(other.date) && channel.equals(other.channel);
}
@Override
public String toString() {
return uuid + " " + date + " " + channel;
}
}
然后用另外 2 个方法扩展
ReportData
来创建密钥和初始聚合:
@AllArgsConstructor
public class ReportData {
//the fields
public String createKey() {
return uuid + " " + msgDate + " " + channel;
}
public Count createCount() {
return new Count(incomingCount, outgoingCount);
}
}
并收集数据:
public class SoMain {
public static void main(String[] args) {
List<ReportData> list = new ArrayList<>();
//populate the list
Map<Key, Count> result = list.stream()
.collect(Collectors.toMap(ReportData::createKey, ReportData::createCount, Count::merge, LinkedHashMap::new));
for (Map.Entry<Key, Count> entry : result.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
Collector 的论据如下:
ReportData::createKey
- 创建分组依据(地图的键)的键ReportData::createCount
- 从单个ReportData
(地图的值)创建初始聚合Count::merge
- 在按键碰撞时合并两个 Count
(请参阅合并方法)LinkedHashMap::new
- Map
插入结果的工厂。我想保留插入顺序,但如果不需要,可以省略该参数以使用默认工厂。打印:
c9c3a519 December 2023 digital 8 3
c9c3a519 November 2023 digital 4 4
c9c3a519 December 2023 manual 8 3
c9c3a519 November 2023 manual 4 4
3de4c44f December 2023 digital 5 3
3de4c44f November 2023 digital 4 4
3de4c44f December 2023 manual 5 3
3de4c44f November 2023 manual 4 4