java - 转换 Java Stream 并返回减少的值
问题描述
假设我从不想实现的源中使用实体流,并且我想转换元素并返回一些全局减少的值,那么 java(8) 的惯用方式是什么?
这本质上是试图同时执行 areduce()
和 a collect()
。
例子:
class Person {
public String firstname,
public String lastname,
public int age;
}
class TeamSummary {
public List<String> fullnames, // firstname and lastname of all
public Person oldest
}
public TeamSummary getSummary(Stream<Person> personStream) {
final TeamSummary summary = new Summary();
summary.fullnames = personStream
.peek(p -> if (summary.getOldest() == null || summary.getOldest.age < p.age) {
summary.oldest = p;
})
.map(p -> p.firstname + ' ' + p.lastname)
.collect(toList());
return summary;
}
在 peek 方法内与流外部的变量进行交互看起来很难看,但是有什么好的替代方案,看来我需要结合collect()
and reduce()
。
如果我想从整个流中获得降低的值(如平均年龄)和过滤列表(如 18 岁以上的人),情况会变得更糟。如果 TeamSummary 是一个不可变类,并且需要额外的可变变量,情况也会变得更糟。
在这种情况下,在 stream.iterator() 上使用 while 循环来避免流方法和变量的耦合更习惯吗?或者使用 reduce 到像(最旧的,累积的)这样的元组是否自然。
我知道这个问题是一个见仁见智的问题,除非有一种明显的方法(比如特殊的收集器)可以优雅地解决这个问题。
解决方案
所以你想把你的收藏减少到一个单一的价值吗?这就是Collectors.reducing
发挥作用的地方(替代方案:您可以使用Stream.reduce
但进行其他修改)。此外,您希望以某种方式聚合您的值并拥有完美的累加器:TeamSummary
.
现在,在下面的代码中,我做了一些愚蠢的调整:
- Team Summary 具有reduce 所需的merge/identity 功能,因为它充当累加器
- 我使用空对象而不是
null
不存在的人,这使得代码在没有空检查的情况下更具可读性(转换器期间的 NPE 是问题之一)。如果流为空,您是否考虑过您的输出? Person
为了自己的方便,我添加了一个构造函数。但是考虑使用 getter 和 final 字段(即使您认为 getter 和整个假封装都是样板:您可以使用方法引用,例如传递给比较器,但不能使用字段引用)
这是代码:
static class Person {
public String firstname;
public String lastname;
public int age;
public Person(String firstname, String lastname, int age) {
this.firstname = firstname;
this.lastname = lastname;
this.age = age;
}
public static Person getNullObjectYoung() {
return new Person("", "", 0);
}
}
static class TeamSummary {
public List<String> fullnames;
public Person oldest;
public static TeamSummary merge(TeamSummary lhs, TeamSummary rhs) {
TeamSummary result = new TeamSummary();
result.fullnames = new ArrayList<>();
result.fullnames.addAll(lhs.fullnames);
result.fullnames.addAll(rhs.fullnames);
result.oldest = Comparator.<Person, Integer>comparing(p -> p.age).reversed()
.compare(lhs.oldest, rhs.oldest) < 0
? lhs.oldest
: rhs.oldest;
return result;
}
public static TeamSummary of(Person person) {
TeamSummary result = new TeamSummary();
result.fullnames = new ArrayList<>();
result.fullnames.add(person.firstname + " " + person.lastname);
result.oldest = person;
return result;
}
public static TeamSummary identity() {
TeamSummary result = new TeamSummary();
result.fullnames = new ArrayList<>();
result.oldest = Person.getNullObjectYoung();
return result;
}
}
public static void main(String[] args) {
Stream<Person> personStream = Arrays.asList(
new Person("Tom", "T", 32),
new Person("Bob", "B", 40))
.stream();
TeamSummary result = personStream.collect(
Collectors.reducing(
TeamSummary.identity(),
TeamSummary::of,
TeamSummary::merge
));
System.out.println(result.fullnames + " " + result.oldest.age);
}
注意:您要求提供 java 8 版本。也许在 java 12 中,您也可以使用Collectors.teeing
,因为您基本上想同时进行两种不同的归约(我们目前可以利用累加器)。
编辑:还添加了一个解决方案Stream.reduce
,它需要一个 BiFunction (summary, person) -> person:
static class TeamSummary {
...
public TeamSummary include(final Person person) {
final TeamSummary result = new TeamSummary();
result.fullnames = new ArrayList<>(fullnames);
result.fullnames.add(person.firstname + " " + person.lastname);
result.oldest = Comparator.<Person, Integer> comparing(p -> p.age).reversed()
.compare(oldest, person) < 0
? oldest
: person;
return result;
}
}
public static void main(final String[] args) {
...
final TeamSummary reduced = personStream.reduce(
TeamSummary.identity(),
TeamSummary::include,
TeamSummary::merge);
}
推荐阅读
- python-2.7 - 使用 Google Cloud Platform 的简单任务队列:Google PubSub 的问题
- ios - 作为 TableView 数据源的 Swift 通用枚举
- javascript - 从 url 中删除特定字符串并重定向
- javascript - Javascript Mobile Swipe 只能使用一次
- oracle - 分区修剪问题
- tfs - TFS 2018 XAML 构建迁移
- php - /tmp 目录未在实时服务器上清理
- android - 单击TextView(设置为可聚焦)后如何在android上隐藏软键盘?
- java - 将字符串数组添加到 MongoDB 中的 ProjectionOperation
- puppeteer - 如何构建 puppeteer 表单源