在本章中,你将学习如何使用Java的流库,它是在Java SE 8中引入的,用来以“做什么而非怎么做”的方式处理集合。
流表明上看起来和集合很类似,都可以让我们转换和获取数据。但是,它们之间存在着显著的差异:
【API】java.util.stream.Stream
Stream filter(Predicate super T> p)
long count()
【API】java.util.Collection
default Stream stream()
default Stream parallelStream()
静态的stream.of方法
of方法具有可变长参数,因此我们可以构建具有任意数量引元的流
使用Array.stream(array,from,to)可以从数组中位于from(包括)和to(不包括)的元素中创建一个流。
为了创建不包含任何元素的流,可以使用静态的Stream.empty方法
Stream接口有两个用于创建无限流的静态方法。generate方法会接受一个不包含任何引元的函数(或者从技术上来讲,是一个Supplier
注意:Java API中有大量方法都可以产生流。例如,Pattern类有一个splitAsStream方法
【API】java.util.stream.Stream 8 :
static Stream of(T... values)
static Stream empty()
static Stream generate(Supplier s)
static Stream iterate(T seed, UnaryOperator f)
【API】java.util.Arrays 1.2 :
static Stream stream(T[] array, int startInclusive, int endExclusive) 8
【API】java.util.regex.Pattern 1.4 :
Stream splitAsStream(CharSequence input) 8
【API】java.nio.file.Files 7:
static Stream lines(Path path)
8static Stream lines(Path path, Charset cs)
8【API】java.util.function.Supplier
T get()
filter转换会产生一个流,它的元素与某种条件相匹配。
filter的引元是Predicate
通常,我们想要按照某种方式来转换流中的值,此时,可以使用map方法并传递执行该转换的函数。
我们得到一个包含流的流,为了将其摊平为字母流,可以使用flatMap方法而不是map方法。
【API】java.util.stream.Stream 8 :
Stream filter(Predicate super T> predicate)
Stream map(Function super T, ? extends R> mapper)
Stream flatMap(Function super T, ? extends Stream extends R>> mapper)
调用stream.limit(n)会返回一个新的流,它在n个元素之后结束(如果原来的流更短,那么就会在流结束时结束)。这个方法对于裁剪无限流的尺寸会显得特别有用。
调用stream.skip(n)正好相反:它会丢弃前n个元素。
我们可以用Steam类的静态的concat方法将两个流连接起来。
当然,第一个流不应该是无限的,否则第二个流永远都不会得到处理的机会。
【API】java.util.stream.Stream 8 :
Stream limit(long maxSize)
Stream skip(long n)
static Stream concat(Stream extends T> a, Stream extends T> b)
distinct方法会返回一个流,它的元素是从原有流中产生的,即原来的元素按照同样的顺序剔除重复元素后产生的。这个流显然能够记住它已经看到过的元素。
对于流的排序,有多种sorted方法的变体可用。其中一种用于操作Comparable元素的流,而另一种可以接受一个Comparator。
peek方法会产生另一个流,它的元素与原来的流中的元素相同,但是在每次获取一个元素时,都会调用一个函数。
对于调试,你可以让peek调用一个你设置了断点的方法。
【API】java.util.stream.Stream 8 :
Stream distinct()
Stream sorted()
Stream sorted(Comparator super T> comparator)
Stream peek(Consumer super T> action)
约简是一种终结操作(terminal operation),它们会将流约简为可以在程序中使用的非流值。
你已经看到过一种简单约简:count方法会返回流中元素的数量。
其他的简单约简还有max和min,它们会返回最大值和最小值。
findFirst返回的是非空集合中的第一个值。
如果不强调使用第一个匹配,而是使用任意的匹配都可以,那么就可以使用findAny方法。
如果只想知道是否存在匹配,那么可以使用anyMatch。这个方法接受一个断言引元,因此不需要使用filter。
还有allMatch和noneMatch方法,它们分别会在所有元素和没有任何元素匹配断言的情况下返回true。
【API】java.util.stream.Stream 8 :
Optional max(Comparator super T> comparator)
Optional min(Comparator super T> comparator)
Optional findFirst()
Optional findAny()
boolean anyMatch(Predicate super T> predicate)
boolean allMatch(Predicate super T> predicate)
boolean noneMatch(Predicate super T> predicate)
Optional
有效地使用Optional的关键是要使用这样的方法:它在值不存在的情况下会产生一个可代替物,而只有在值存在的情况下才会使用这个值。
ifPresent方法会接受一个函数。如果该可选值存在,那么它会被传递给该函数。否则,不会发生任何事情。
【API】java.util.Optional 8 :
T orElse(T other)
T orElseGet(Supplier extends T> other)
T orElseThrow(Supplier extends X> exceptionSupplier)
void ifPresent(Consumer super T> consumer)
Optional map(Function super T, ? extends U> mapper)
get方法会在Optional值存在的情况下获得其中包装的元素,或者在不存在的情形下抛出一个NoSuchElementException对象。
isPresent方法会报告某个Optional
【API】java.util.Optional 8 :
T get()
boolean isPresent()
ofNullable方法被用来作为可能出现的null值和可选值之间的桥梁。
【API】java.util.Optional 8 :
static Optional of(T value)
static Optional ofNullable(T value)
static Optional empty()
【API】java.util.Optional 8 :
Optional flatMap(Function super T, Optional> mapper)
调用iterator方法,它会产生可以用来访问元素的旧式风格的迭代器。
调用forEach方法,将某个函数应用于每个元素。
在并行流上,forEach方法会以任意顺序遍历各个元素。如果想要按照流中的顺序来处理它们,可以调用forEachOrdered方法。
调用toArray方法,获得由流的元素构成的数组。
因为无法在运行时创建泛型数组,所以表达式stream.toArray()会返回一个Object[]数组。如果想要让数组具有正确的类型,可以将其传递到数组构造器中。
collect方法,它会接受一个Collector接口的实例。Collectors类提供了大量应用于生成公共收集器的工厂方法。
如果想要将流的结果约简为总和、平均值、最大值或最小值,可以使用summarizing(Int| Long| Double)方法中的某一个。
【API】java.util.stream.BaseStream 8 :
Iterator iterator()
【API】java.util.stream.Stream 8 :
void forEach(Consumer super T> action)
Object[] toArray()
A[] toArray(IntFunction generator)
R collect(Collector super T,A,R> collector)
【API】java.util.stream.Collectors 8 :
static Collector> toList()
static Collector> toSet()
static > Collector toCollection(Supplier collectionFactory)
static Collector joining()
static Collector joining(CharSequence delimiter)
static Collector joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
static Collector summarizingInt(ToIntFunction super T> mapper)
static Collector summarizingLong(ToLongFunction super T> mapper)
static Collector summarizingDouble(ToDoubleFunction super T> mapper)
【API】IntSummaryStatistics 8
LongSummaryStatistics 8
DoubleSummaryStatistics 8:
long getCount()
(int|long|double) getSum()
double getAverage()
(int|long|double) getMax()
(int|long|double) getMin()
注意:对于每一个toMap方法,都有一个等价的可以产生并发映射表的toConcurrentMap方法。
【API】java.util.stream.Colletor 8 :
static Collector> toMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper)
static Collector> toMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction)
static Collector toMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)
static Collector> toConcurrentMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper)
static Collector> toConcurrentMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction)
static> Collector toConcurrentMap(Function super T, ? extends K> keyMapper, Function super T, ? extends U> valueMapper, BinaryOperator mergeFunction, Supplier mapSupplier)
【API】java.util.stream.Collector 8 :
static Colletor>> groupingBy(Function super T,? extends K> classifier)
static Colletor>> groupingByConcurrent(Function super T,? extends K> classifier)
static Colletor>> partitioningBy(Predicate super T> predicate)
groupingBy方法会产生一个映射表,它的每个值都是一个列表。如果想要以某种方式来处理这些列表,就需要提供一个“下游收集器”。例如,如果想要获得集而不是列表,那么可以使用Colletor.toSet收集器。
Java提供了多种可以将群组元素约简为数字的收集器:
mapping方法会产生将函数应用到下游结果上的收集器,并将函数值传递给另一个收集器。
【API】java.util.stream.Collection 8 :
static Collector counting()
static Collector summingInt(ToIntFunction super T> mapper)
static Collector summingLong(ToLongFunction super T> mapper)
static Collector summingDouble(ToIntFunction super T> mapper)
static Collector> maxBy(Comparator super T> comparator)
static Collector> minBy(Comparator super T> comparator)
static Collector mapping(Function super T, ? extends U> mapper, Colletor super U, A, R> downstream)
reduce方法是一种用于从流中计算某个值的通用机制,其最简单的形式将接受一个二元函数,并从前两个元素开始持续应用它。
【API】java.util.Stream 8 :
Optional reduce(BinaryOperator accumulator)
T reduce(T identity, BinaryOperator accumulator)
U reduce(U identity, BiFunction accumulator, BinaryOperator combiner)
Rcollect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)
流库中具有专门的类型IntStream、LongStream和DoubleStream,用来直接存储基本类型值,而无需使用包装器。如果想要存储short、char、byte和boolean,可以使用IntStream,而对于float,可以使用DoubleStream。
为了创建IntStream,需要调用IntStream.of 和 Arrays.stream方法
与对象流一样,我们还可以使用静态的generate 和 iterate 方法。此外,IntStream 和 LongStream有静态方法range和rangeClosed,可以生成步长为1的整数范围。
CharSequence接口拥有codePoints和chars方法,可以生成由字符的Unicode码或由UTF-16编码机制的码元构成的IntStream。
当你有一个对象流时,可以用mapToInt、mapToLong和mapToDouble将其转换为基本类型流。
为了将基本类型流转换为对象流,需要使用boxed方法。
通常,基本类型流上的方法与对象流上的方法类似。下面是最主要的差异:
注意:Random类具有ints、longs和doubles方法,它们会返回由随机数构成的基本类型流。
【API】java.util.stream.Stream 8 :
static IntStream range(int startInclusive, int endExclusive)
static IntStream rangeClosed(int startInclusive, int endExclusive)
static IntStream of(int... values)
int[] toArray()
int sum()
OptionalDouble average()
OptionalInt max()
OptionalInt min()
IntSummaryStatistics summaryStatistics()
Stream boxed
【API】java.util.stream.LongStream 8 :
static LongStream range(long startInclusive, long endExclusive)
static LongStream rangeClosed(long startInclusive, long endExclusive)
static LongStream of(long... values)
long[] toArray()
long sum()
OptionalDouble average()
OptionalLong max()
OptionalLong min()
LongSummaryStatistics summaryStatistics()
Stream boxed()
【API】java.util.stream.DoubleStream 8 :
static DoubleStream of(double... values)
double[] toArray()
double sum()
OptionalDouble average()
OptionalDouble max()
OptionalDouble min()
DoubleSummaryStatistics summaryStatistics()
Stream boxed()
【API】java.lang.CharSequence 1.0 :
IntStream codePoints()
8【API】java.util.Random 1.0 :
IntStream ints()
IntStream ints(int randomNumberOrigin, int randomNumberBound)
8IntStream ints(long streamSize)
8IntStream ints(long streamSize, int randomNumberOrigin, int randomNumberBound)
8LongStream longs()
8LongStream longs(long randomNumberOrigin, long randomNumberBound)
8LongStream longs(long streamSize)
8LongStream longs(long streamSize, long randomNumberOrigin, long randomNumberBound)
8DoubleStream doubles()
8DoubleStream doubles(double randomNumberOrigin, double randomNumberBound)
8DoubleStream doubles(long streamSize)
8DoubleStream doubles(long streamSize, double randomNumberOrigin, double randomNumberBound)
8【API】java.util.Optional(Int|Long|Double) 8 :
static Optional(Int|Long|Double) of((int|long|double) value)
(int|long|double) getAs(Int|Long|Double)()
(int|long|double) orElse((int|long|double) other)
(int|long|double) orElseGet((int|long|double) Supplier other)
void ifPresent((Int|Long|Double)Consumer consumer)
【API】java.util.(Int|Long|Double) SummaryStatistics 8 :
long getCount()
(int|long|double) getSum()
double getAverage()
(int|long|double) getMax()
(int|long|double) getMin()
流使得并行处理块操作变得很容易。这个过程几乎是自动的,但是需要遵守一些规则。首先,必须有一个并行流。可以用Collection.parallelStream()方法从任何集合中获取一个并行流。
为了让并行流正常工作,需要满足大量的条件:
换句话说,不要将所有的流都转换为并行流。只有在对已经位于内存中的数据执行大量计算操作时,才应该使用并行流。
【API】java.util.stream.BaseStream
S parallel()
S unordered()
【API】java.util.Collection
Stream parallelStream()
8