本篇对应《Java 8 函数式编程》的第三章。
PS:如果你还没有了解过 Iterator 设计模式,请先去了解一下 Iterator 设计模式。
Stream
Stream 是用函数式编程方式在集合类上进行复杂操作的工具。
int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();
比如这个官方文档上的代码示例:计算所有红色 Widget 的权重的总和。
- 使用 Collection.stream() 方法,创建 widgets 集合的流。
- 使用 filter 操作,产生一个只包含红色 widgets 的流。
- 使用 mapToInt 操作,转换成红色 widgets 的权重的流。
- 使用 sum 操作,计算红色 widgets 的权重之和。
Stream operations and pipelines
流操作(Stream operations)分为中间操作(intermediate operations)和终点操作(terminal operations),合在一起就形成了流管道(stream pipelines)。
一个流管道(stream pipelines)包含一个数据源(source),零到多个中间操作(intermediate operations)和一个终点操作(terminal operations)。
数据源(source)可以是一个数组,一个集合,一个生成函数,一个 I/O 通道,~。
中间操作(intermediate operations)就是把一个流转换成另外一个流,比如 filter 和 map。
终点操作(terminal operations)会产生一个结果或者副作用,比如 count 和 forEach。
流是惰性求值的,只有终点操作(terminal operations)开始的时候,所有数据源(source)上的计算才会真正被执行,并且数据源元素只在需要时才会被使用。
常用的流操作
filter | intermediate operation | stateless |
map | ||
mapToInt | ||
mapToLong | ||
mapToDouble | ||
flatMap | ||
flatMapToInt | ||
flatMapToLong | ||
flatMapToDouble | ||
distinct | stateful | |
sorted | ||
peek | ||
limit | ||
skip | ||
takeWhile | ||
dropWhile | ||
forEach | terminal operation | |
forEachOrdered | ||
toArray | ||
reduce | ||
collect | ||
max | ||
count | ||
anyMatch | ||
allMatch | ||
noneMatch | ||
findFirst | ||
findAny |
练习
Question 1:
public static int addUp(Stream<Integer> numbers) {
return numbers.reduce(0, (acc, x) -> acc + x);
}
public static List<String> getNamesAndOrigins(List<Artist> artists) {
return artists.stream()
.flatMap(artist -> Stream.of(artist.getName(), artist.getNationality()))
.collect(toList());
}
public static List<Album> getAlbumsWithAtMostThreeTracks(List<Album> input) {
return input.stream()
.filter(album -> album.getTrackList().size() <= 3)
.collect(toList());
}
Question 2:
int totalMembers = (int) artists.stream().flatMap(artist -> artist.getMembers()).count();
Question 3:
a. 及早求值(Eager)- 返回 Stream
b. 惰性求值(Lazy)
Question 4:
a. 是 - 参数是函数
b. 否
Question 5:
a. 没有副作用
Question 6:
public static int countLowercaseLetters(String string) {
return (int) string.chars()
.filter(Character::isLowerCase)
.count();
}
Question 7:
public static Optional<String> mostLowercaseString(List<String> strings) {
return strings.stream()
.max(Comparator.comparing(StringExercises::countLowercaseLetters));
}
进阶练习
Question 1:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* Advanced Exercises Question 1
*/
public class MapUsingReduce {
public static <I, O> List<O> map(Stream<I> stream, Function<I, O> mapper) {
return stream.reduce(new ArrayList<O>(), (acc, x) -> {
// We are copying data from acc to new list instance. It is very inefficient,
// but contract of Stream.reduce method requires that accumulator function does
// not mutate its arguments.
// Stream.collect method could be used to implement more efficient mutable reduction,
// but this exercise asks to use reduce method.
List<O> newAcc = new ArrayList<>(acc);
newAcc.add(mapper.apply(x));
return newAcc;
}, (List<O> left, List<O> right) -> {
// We are copying left to new list to avoid mutating it.
List<O> newLeft = new ArrayList<>(left);
newLeft.addAll(right);
return newLeft;
});
}
}
Question 2:
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* Advanced Exercises Question 2
*/
public class FilterUsingReduce {
public static <I> List<I> filter(Stream<I> stream, Predicate<I> predicate) {
List<I> initial = new ArrayList<>();
return stream.reduce(initial,
(List<I> acc, I x) -> {
if (predicate.test(x)) {
// We are copying data from acc to new list instance. It is very inefficient,
// but contract of Stream.reduce method requires that accumulator function does
// not mutate its arguments.
// Stream.collect method could be used to implement more efficient mutable reduction,
// but this exercise asks to use reduce method explicitly.
List<I> newAcc = new ArrayList<>(acc);
newAcc.add(x);
return newAcc;
} else {
return acc;
}
},
FilterUsingReduce::combineLists);
}
private static <I> List<I> combineLists(List<I> left, List<I> right) {
// We are copying left to new list to avoid mutating it.
List<I> newLeft = new ArrayList<>(left);
newLeft.addAll(right);
return newLeft;
}
}
参考:
https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html
508 total views, 3 views today