使用 Java 8 Streams,是否可以以某种方式封装和重用中间流操作,而不会破坏流管道?
考虑一下 Java 教程中关于流的示例:
double average = roster
.stream()
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
.average()
.getAsDouble();
假设我需要在整个代码的不同位置使用过滤器和 mapToInt 操作。 我可能想尝试封装该逻辑,以便可以重用,例如:
IntStream maleAges(Stream<Person> stream) {
return stream
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge)
}
这很好,但要使用它,我可能不得不弄乱流管道。 例如,如果我想要名叫鲍勃的男性的平均年龄:
double averageBob =
maleAges(roster
.stream()
.filter(p -> "Bob".equals(p.getName()))
)
.average()
.getAsDouble();
有没有更好的方法可以做到这一点? 我正在考虑以下几点:
double averageBob = roster
.stream()
.filter(p -> "Bob".equals(p.getName()))
.apply(this::maleAges) // Doesn't compile
.average()
.getAsDouble();
一种方法是取一个流,取一个人并发出一个IntStream,例如:
final Predicate<Person> isMale = p -> p.getGender() == Person.Sex.MALE;
final Function<Person, IntStream> maleAges = person -> isMale.test(person)
? IntStream.of(person.age)
: IntStream.empty();
final double averageT = roster
.stream()
.flatMapToInt(maleAges)
.average()
.getAsDouble();
通过这种方式,您可以在任何地方重用您的男性年龄功能!
我的 StreamEx 库增强了标准 Stream API 以及其他功能,具有chain
方法,该方法与您提出的apply
完全相同:
double averageBob = StreamEx.of(roster)
.filter(p -> "Bob".equals(p.getName()))
.chain(this::maleAges) // Compiles!
.average()
.getAsDouble();
另一种可能的方法是从maleAges
返回一个函数:
Function<Stream<Person>, IntStream> maleAges() {
return stream -> stream
.filter(p -> p.getGender() == Person.Sex.MALE)
.mapToInt(Person::getAge);
}
并像这样使用它:
double averageBob = StreamEx.of(roster)
.filter(p -> "Bob".equals(p.getName()))
.chain(maleAges()) // Compiles!
.average()
.getAsDouble();
这样,您可以轻松地参数化封装的操作。例如:
Function<Stream<Person>, IntStream> agesForSex(Person.Sex sex) {
return stream -> stream
.filter(p -> p.getGender() == sex)
.mapToInt(Person::getAge);
}
double averageBob = StreamEx.of(roster)
.filter(p -> "Bob".equals(p.getName()))
.chain(agesForSex(Person.Sex.MALE)) // Compiles!
.average()
.getAsDouble();