核心技术Ⅱ:流
跳到导航
跳到搜索
关于 Java SE 8 的流库
Stream它并不是一个容器,它只是对容器的功能进行了增强,添加了很多便利的操作,例如查找、过滤、分组、排序等一系列的操作。 并且有串行、并行两种执行模式,并行模式充分的利用了多核处理器的优势,使用fork/join框架进行了任务拆分,同时提高了执行速度。 简而言之,Stream就是提供了一种高效且易于使用的处理数据的方式。
流提供了一种在比集合更高的概念级别上指定计算的数据视图,以“做什么而非怎么做”的方式处理集合。
从迭代到流的操作
处理集合时,通常会迭代遍历它的元素,并在每个元素上执行某项操作:
String contents = new String(Files.readAllBytes(Paths.get("alice.txt")), Standard(harsets,UTF_8); // Read file into string
List<String> words= Arrays.aslist(contents.split("\\PL+")); // Split into words; nonletters are delimiters
long count = O;
for (String w : words)
{
if (w. length() > 12)
count++;
}
使用流时,相同的操作看起来像下面这样:
1ong count = words.stream().filter(w -> w.length() > 12).count();
- 仅将stream 修改为parallelStream 就可以让流库以并行方式来执行过滤和计数:
1ong count = words.parallelStream().filter(w -> w.length() > 12).count();
以上:
- stream 和parallel Stream 方法会产生一个用于words 列表的stream。
- filter 方法会返回另一个流,其中只包含长度大于12 的单词。
- count 方法会将这个流化简为一个结果。
流与集合
流表面上活起来和集合很类似,都可以让我们转换和获取数据。但是,它们之间存在着显著的差异:
- 流并不存储其元素。
- 这些元素可能存储在底层的栠合中,或者是按需生成的。
- 流的操作不会修改其数据源。
- 例如,filter 方法不会从新的流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素。
- 流的操作是尽可能惰性执行的。这意味若直至需要其结果时,操作才会执行。
- 例如,如果我们只想查找前5 个长单词而不是所有长单词,那么filter 方法就会在匹配到第5 个单词后停止过滤。因此,我们甚至可以操作无限流。
相关方法
java.util.stream.Stream<T> 8
- Stream<T> filter(Predicate<? super T> p)
- 产生一个流,其中包含当前流中满足P 的所有元索。
- 1ong count()
- 产生当前流中元素的数批。这是一个终止操作。
java.util.Collection<E> 1.2
- default Stream<E> stream()
- default Stream<E> parallel Stream()
- 产生当前集合中所有元素的顺序流或并行流。
流的创建
- 集合转换为流:
- 用“Collection.stream()”方法将任何集合转换为一个流。
- 数组转换为流:
- 使用“Array.stream(array, from, to)”可以从数组中位于from (包括)和to (不包括)的元索中创建一个流。
- 使用Stream静态的“of”方法,将数组转换为流:
Stream<String> words= Stream.of(contents.split("\\PL+")); // split returns a String[] array
- of 方法具有可变长参数,因此我们可以构建具有任意数趾引元的流:
Stream<String> song = Stream.of("gently", "down", "the", "stream");
- 空流:
- 使用静态的Stream.empty 方法,创建不包含任何元素的流;
Stream<String> silence = Stream.empty(); // Generic type <String> is inferred; same as St ream. <St ri ng>empty()
- 无限流:
- generate 方法:接受一个不包含任何引元的函数(或者从技术上讲,是一个Supplier<T> 接口的对象)。
Stream<String> echos = Stream.generate(() -> "Echo"); // 获得一个常批值的流: Stream<Double> randoms = Stream.generate(Math:: random); // 获得一个随机数的流:
- iterate 方法:接受一个“种子”值,以及一个函数(从技术上讲,是一个UnaryOperation<T>), 并且会反复地将该函数应用到之前的结果上。
Strea价<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE)); // 第一个元素是种子Biglnteger.ZERO, 第二个元素是f(seed), 即l (作为大整数),下一个元素是f(f(seed)), 即2, 后续以此类推。
- generate 方法:接受一个不包含任何引元的函数(或者从技术上讲,是一个Supplier<T> 接口的对象)。
Java API中的其他流方法:
- Pattern 类有一个“splitAsStream”方法,它会按照某个正则表达式来分割一个“CharSequence”对象:
Stream<String> words = Pattern,compile("\\PL+").splitAsStream(contents);
- 静态的“Files.lines”方法会返回一个包含了文件中所有行的Stream:
try (Stream<String> lines = Files.lines (path)) { ...Process lines... }
相关方法
java.util.stream.Stream 8
- static <T> Stream<T> of(T .. . values)
- 产生一个元素为给定值的流。
- static <T> Stream<T> empty()
- 产生一个不包含任何元素的流。
- static <T> Stream<T> generate(Supplier<T> s)
- 产生一个无限流,它的值是通过反复调用函数s 而构建的。
- static <T> Stream<T> iterate(T seed , UnaryOperator<T> f )
- 产生一个无限流,它的元素包含种子、在种子上调用f 产生的值、在前一个元素上调用f 产生的值,等等。
java.util.Arrays 1.2
- static <T> Stream<T> stream(T[] array, int startinclusive , int endExclusive) 8
- 产生一个流,它的元素是由数组中指定范围内的元素构成的。
java.util.regex.Pattem 1.4
- Stream<Stri ng> spl i tAsStream(CharSequence input) 8
- 产生一个流,它的元素是输入中由该模式界定的部分。
java.nio.file.Files 7
- static Stream<String> lines(Path path) 8
- static Stream<Stri ng> lines(Path path, Charset cs) 8
- 产生一个流, 它的元素是指定文件中的行,该文件的字符集为UTF-8 , 或者为指定的字符集。
java.util.function.Supplier<T> 8
- T get()
- 提供一个值。