本文的内容是对《计算机程序的构造和解释(SICP)》3.5 节描述的流的 Java 实现,希望能提供一些启发,仅用于学习,不可用于生产环境。
网络上已经有很多对 Java 8 Stream,RxJava 的流实现的分析,学习后可以对那些API有更深的理解。而本文侧重使用尽可能少的代码从零开始来实现流以及相关的操作(例如,filter、map、reduce),来加深对流的理解。
public class Stream<T> {
private static final Stream<?> EMPTY = new Stream<>(null, () -> null); //空的流
private T value; //每个流节点对应的值
private Supplier<Stream<T>> nextSupplier; //流的下一个节点的提供者(这里实现懒求值的效果)
private Stream<T> nextStream; //下一个流节点的缓存
public Stream(T value, Supplier<Stream<T>> nextSupplier) {
this.value = value;
this.nextSupplier = nextSupplier;
}
/* 获取下一个流节点,如果之前没求值,就对其 */
public Stream<T> next() {
if (nextStream == null) {
nextStream = nextSupplier.get();
}
return nextStream;
}
/* 获取当前流节点的值 */
public T value() {
return value;
}
}
//1. 获取流的从0开始的第index个元素。
public T ref(int index) {
Stream<T> stream = this;
for (int i = 0; i < index; i++) {
stream = stream.next();
if (stream == null || stream == EMPTY) {
throw new IndexOutOfBoundsException(String.format("index[%d] is out of bounds", index));
}
}
return stream.value;
}
//2. 对当前流进行迭代
public void forEach(Consumer<T> consumer) {
Stream<T> stream = this;
while (stream != null && stream != EMPTY) {
consumer.accept(stream.value);
stream = stream.next();
}
}
//3. 将当前流映射为 R 类型的流
public <R> Stream<R> map(Function<T, R> mapper) {
return new Stream<>(mapper.apply(value), () -> nextSupplier.get().map(mapper));
}
//4. 根据给定的断言得到根据该断言过滤的流对象
public Stream<T> filter(Predicate<T> predicate) {
Stream<T> stream = this;
while (stream != null && stream != EMPTY) {
if (predicate.test(stream.value)) {
Supplier<Stream<T>> supplier = stream.nextSupplier;
return new Stream<>(stream.value, () -> supplier.get().filter(predicate));
} else {
stream = stream.next();
}
}
return (Stream<T>) EMPTY;
}
//5. 获取前n个元素的流对象
public Stream<T> takeFirst(int n) {
return n > 0
? new Stream<>(value, () -> takeFirst(n - 1))
: (Stream<T>) EMPTY;
}
//6. 根据给定和归约方法来对流进行归约
public <R> R reduce(R seed, BiFunction<T, R, R> biFunction) {
Stream<T> stream = this;
while(stream != null && stream != EMPTY){
seed = biFunction.apply(stream.value, seed);
stream = stream.next();
}
return seed;
}
//产生从 start 开始的无限整数流
public static Stream<Integer> range(int start) {
return new Stream<>(start, () -> range(start + 1));
}
//产生从 start(包括) 到 end(不包括) 的有限整数流
public static Stream<Integer> range(int start, int end) {
return start < end
? new Stream<>(start, () -> range(start + 1, end))
: (Stream<Integer>) EMPTY;
}
//产生 obj 对象的无限流
public static <T> Stream<T> objects(T obj) {
return new Stream<>(obj, () -> objects(obj));
}
//从一个可迭代对象(例如List、Set)来产生流
public static <T> Stream<T> fromIterable(Iterable<T> iterable) {
return fromIterator(iterable.iterator());
}
//从一个迭代器对象来产生流
public static <T> Stream<T> fromIterator(Iterator<T> iterator) {
return iterator.hasNext()
? new Stream<>(iterator.next(), () -> fromIterator(iterator))
: (Stream<T>) EMPTY;
}
//1. 或许从1到100数字流,按偶数过滤,添加前缀后,收集到List中
LinkedList<String> numberStrList = Stream.range(1, 101)
.filter(i -> i % 2 == 0)
.map(i -> "number:" + i)
.reduce(new LinkedList<>(), (s, strings) -> {
strings.add(s);
return strings;
});
//2. 将一个集合转换为流,按偶数过滤,求和并返回
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Integer evenCount = Stream.fromIterable(integers)
.filter(i -> i % 2 == 0)
.reduce(0, Integer::sum);
System.out.println("even:" + evenCount);
//3. 斐波那契数列
//定义数列生成函数
private Stream<Integer> fibGen(int a, int b) {
return new Stream<>(a, () -> fibGen(b, a + b));
}
//得到斐波那契数列的无限流
Stream<Integer> fibStream = fibGen(1, 1);
//获取并打印第10个斐波那契数
System.out.println("fib 10:" + fibStream.ref(9));//输出 fib 10:55
//计算前10个斐波那契数的和
Integer sum = fibStream.takeFirst(10)
.reduce(0, Integer::sum);
System.out.println("fib sum:" + sum);//输出 fib sum:143
public class Stream<T> {
public static final Stream<?> EMPTY = new Stream<>(null, () -> null);
private T value;
private Supplier<Stream<T>> nextSupplier;
private Stream<T> nextStream;
public static Stream<Integer> range(int start) {
return new Stream<>(start, () -> range(start + 1));
}
public static Stream<Integer> range(int start, int end) {
return start < end
? new Stream<>(start, () -> range(start + 1, end))
: (Stream<Integer>) EMPTY;
}
public static <T> Stream<T> objects(T obj) {
return new Stream<>(obj, () -> objects(obj));
}
public static <T> Stream<T> fromIterable(Iterable<T> iterable) {
return fromIterator(iterable.iterator());
}
public static <T> Stream<T> fromIterator(Iterator<T> iterator) {
return iterator.hasNext()
? new Stream<>(iterator.next(), () -> fromIterator(iterator))
: (Stream<T>) EMPTY;
}
public Stream(T value, Supplier<Stream<T>> nextSupplier) {
this.value = value;
this.nextSupplier = Objects.requireNonNull(nextSupplier);
}
public Stream<T> next() {
if (nextStream == null && nextSupplier != null) {
nextStream = nextSupplier.get();
}
return nextStream;
}
public T value() {
return value;
}
public T ref(int index) {
Stream<T> stream = this;
for (int i = 0; i < index; i++) {
stream = stream.next();
if (stream == null || stream == EMPTY) {
throw new IndexOutOfBoundsException(String.format("index[%d] is out of bounds", index));
}
}
return stream.value;
}
public void forEach(Consumer<T> consumer) {
Stream<T> stream = this;
while (stream != null && stream != EMPTY) {
consumer.accept(stream.value);
stream = stream.next();
}
}
public <R> Stream<R> map(Function<T, R> mapper) {
return this == EMPTY ? (Stream<R>) EMPTY : new Stream<>(mapper.apply(value), () -> nextSupplier.get().map(mapper));
}
public Stream<T> filter(Predicate<T> predicate) {
Stream<T> stream = this;
while (stream != null && stream != EMPTY) {
if (predicate.test(stream.value)) {
Supplier<Stream<T>> supplier = stream.nextSupplier;
return new Stream<>(stream.value, () -> supplier.get().filter(predicate));
} else {
stream = stream.next();
}
}
return (Stream<T>) EMPTY;
}
public Stream<T> takeFirst(int n) {
return n > 0 && this != EMPTY
? new Stream<>(value, () -> nextSupplier.get().takeFirst(n - 1))
: (Stream<T>) EMPTY;
}
public <R> R reduce(R seed, BiFunction<T, R, R> biFunction) {
Stream<T> stream = this;
while (stream != null && stream != EMPTY) {
seed = biFunction.apply(stream.value, seed);
stream = stream.next();
}
return seed;
}
}