我有以下对象:
class Event {
private LocalDateTime when;
private String what;
public Event(LocalDateTime when, String what) {
super();
this.when = when;
this.what = what;
}
public LocalDateTime getWhen() {
return when;
}
public void setWhen(LocalDateTime when) {
this.when = when;
}
public String getWhat() {
return what;
}
public void setWhat(String what) {
this.what = what;
}
}
我需要按年/月(yyyy-mm)和事件类型汇总,然后计算。例如以下列表
List<Event> events = Arrays.asList(
new Event(LocalDateTime.parse("2017-03-03T09:01:16.111"), "EVENT1"),
new Event(LocalDateTime.parse("2017-03-03T09:02:11.222"), "EVENT1"),
new Event(LocalDateTime.parse("2017-04-03T09:04:11.333"), "EVENT1"),
new Event(LocalDateTime.parse("2017-04-03T09:04:11.333"), "EVENT2"),
new Event(LocalDateTime.parse("2017-04-03T09:06:16.444"), "EVENT2"),
new Event(LocalDateTime.parse("2017-05-03T09:01:26.555"), "EVENT3")
);
应该产生以下结果:
Year/Month Type Count
2017-03 EVENT1 2
2017-04 EVENT1 1
2017-04 EVENT2 2
2017-04 EVENT3 1
我是否可以通过Streams API实现这一目标(如果是,如何)?
答案 0 :(得分:3)
如果您不想定义自己的密钥,可以groupBy
两次。结果是相同的,但格式略有不同:
System.out.println(events.stream()
.collect(Collectors.groupingBy(e -> YearMonth.from(e.getWhen()),
Collectors.groupingBy(Event::getWhat, Collectors.counting()))));
结果是:
{2017-05={EVENT3=1}, 2017-04={EVENT2=2, EVENT1=1}, 2017-03={EVENT1=2}}
答案 1 :(得分:3)
如果您不想按照assylias的建议创建新的密钥类,则可以执行双groupingBy
Map<YearMonth,Map<String,Long>> map =
events.stream()
.collect(Collectors.groupingBy(e -> YearMonth.from(e.getWhen()),
Collectors.groupingBy(x -> x.getWhat(), Collectors.counting()))
);
...后面是嵌套打印
map.forEach((k,v)-> v.forEach((a,b)-> System.out.println(k + " " + a + " " + b)));
打印
2017-05 EVENT3 1
2017-04 EVENT2 2
2017-04 EVENT1 1
2017-03 EVENT1 2
编辑:我注意到日期的顺序与OP的预期解决方案相反。使用groupingBy
的3参数版本,您可以指定已排序的地图实现
Map<YearMonth,Map<String,Long>> map =
events.stream()
.collect(Collectors.groupingBy(e -> YearMonth.from(e.getWhen()), TreeMap::new,
Collectors.groupingBy(x -> x.getWhat(), Collectors.counting()))
);
现在打印相同的map.forEach(...)
2017-03 EVENT1 2
2017-04 EVENT2 2
2017-04 EVENT1 1
2017-05 EVENT3 1
答案 2 :(得分:2)
你可以创建一个&#34;键&#34;包含年/月和事件类型的类:
class Group {
private YearMonth ym;
private String type;
public Group(Event e) {
this.ym = YearMonth.from(e.getWhen());
this.type = e.getWhat();
}
//equals, hashCode, toString etc.
}
然后,您可以使用该密钥对事件进行分组:
Map<Group, Long> result = events.stream()
.collect(Collectors.groupingBy(Group::new, Collectors.counting()));
result.forEach((k, v) -> System.out.println(k + "\t" + v));
输出:
2017-04 EVENT1 1
2017-03 EVENT1 2
2017-04 EVENT2 2
2017-05 EVENT3 1
答案 3 :(得分:0)
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
Stream.of(
new Event(LocalDateTime.parse("2017-03-03T09:01:16.111"), "EVENT1"),
new Event(LocalDateTime.parse("2017-03-03T09:02:11.222"), "EVENT1"),
new Event(LocalDateTime.parse("2017-04-03T09:04:11.333"), "EVENT1"),
new Event(LocalDateTime.parse("2017-04-03T09:04:11.333"), "EVENT2"),
new Event(LocalDateTime.parse("2017-04-03T09:06:16.444"), "EVENT2"),
new Event(LocalDateTime.parse("2017-05-03T09:01:26.555"), "EVENT3")
).collect(Collectors.groupingBy(event ->
dateTimeFormatter.format(event.getWhen()),
Collectors.groupingBy(Event::getWhat, counting())))
.forEach((whenDate,v) -> v.forEach((whatKey,counter) ->
System.out.println(whenDate+ " "+ whatKey+" "+counter)));
无需使用Arrays.asList()方法来获取流。直接使用Stream.of()方法获取流。
<强>输出强>:
2017-03 EVENT1 2
2017-04 EVENT2 2
2017-04 EVENT1 1
2017-05 EVENT3 1
答案 4 :(得分:0)
我们可以在POJO中创建方法,其中包含用于分组的字段列表,如下所示
public String getWhenAndWhat() {
return YearMonth.from(when) + ":" + what; //you can use delimiters like ':','-',','
}
和流代码
System.out.println(events.stream()
.collect(Collectors.groupingBy(Event::getWhenAndWhat, Collectors.counting())));
输出将是:
{2017-05:EVENT3 = 1,2017-04:EVENT1 = 1,2017-04:EVENT2 = 2,2017-03:EVENT1 = 2}