简介
流(Stream)提供了数据视图,让你可以在笔集合类更高的概念层上制定操作,使用流,只需要指定做什么,而不是则么做。你只需要将操作的调度执行留给实现。
从迭代到 Stream 操作
例子统计一个文件中的长单词(在Android Studio中实现):
读取 raw 文件中的 txt 文件:
public static String readTextFileFromResource(Context context, int ResId){ StringBuilder body = new StringBuilder(); InputStream inputStream = context.getResources().openRawResource(ResId); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String nextLine; try { while ((nextLine = bufferedReader.readLine()) != null){ body.append(nextLine); } } catch (IOException e) { e.printStackTrace(); } return body.toString(); }
普通迭代来统计:video_sequences.txt 文件有 76KB
String contents = TextResReader.readTextFileFromResource(this, R.raw.video_sequences); Log.d("contents", contents); //用正则将字符串拆分成单词 List
words = Arrays.asList(contents.split("[^a-zA-z0-9]")); int count = 0; long startTime = System.currentTimeMillis(); for (String w : words) { if (w.length() > 12) count++; } Log.d("Count", count + ""); long endTime = System.currentTimeMillis(); //获取结束时间 System.out.println("普通程序运行时间: " + (endTime - startTime) + "ms"); 使用流,实现相同的功能:
long startTime1 = System.currentTimeMillis(); long counts = Stream.of(words).filter(w -> w.length() > 12).count(); Log.d("StreamCount", counts + ""); long endTime1 = System.currentTimeMillis(); //获取结束时间 System.out.println("Stream程序运行时间: " + (endTime1 - startTime1) + "ms");
打印结果:
D/Count: 183
I/System.out: 普通程序运行时间: 64ms
D/StreamCount: 183
I/System.out: Stream程序运行时间: 30ms
只使用了一行代码就能搞定,不用为了过滤和扫描循环了,效率也提高了不少。 Stream 遵循做什么,而不是怎么去做的原则。在这个实例中,我们描述了需要做的事情,找到单词并对他们计数,我们不需要指定顺序,不需要指定在哪个线程上运行,执行顺序和执行线程都会自动由 Stream 实现。
流表面上看起来与集合类似,允许你转换和检索数据,但是他们是不同的:
- 流不存储元素。它们存储在底层的集合或者按需生成。
- 流操作不改变源数据。filter方法不会从一个新流中删除元素,而是生成一个不包含特定元素的新流。
- 如果可能的话Stream操作可能是延迟执行的。
使用流的流程
- 创建一个Stream
- 指定将初始流转换成其他流的中间操作,可能需要多步操作。
- 应用终止操纵产生结果,在这之后流就不会用到了。
创建 Stream
- 可以使用 Collection 接口的 stream 方法将任何集合转化为 Stream
- 如果面对的是数组,则使用静态方法 Stream.of()方法将它转化为一个 stream
一些创建方法:
//1.Stream.of()
//split 方法返回 String[] 数组
Stream wordsStream = Stream.of(contents.split("[^a-zA-z0-9]"));
//of方法可以接受可变长度的参数
Stream song = Stream.of("gently", "down", "the", "stream");
//2.Array.stream(array, from, to) 将数组的一部分转化成 Stream
//3.创建一个不含任何元素的 Stream
Stream silence = Stream.empty();
// Stream 接口有两个创建无限 Stream 的方法 generate()、iterate()
//4.generate()方法
//参数是一个 Supplier 的对象 下面创建一个常量值的 Stream
Stream echos = Stream.generate(() -> "echo");
//一个含有随机数的 Stream
Stream randoms = Stream.generate(Math::random);
//5.iterate()方法
//创建一个如 0 1 2 3 ... 的无穷数列,可以使用iterate()方法
//第一个参数种子值,第二个是一个函数
Stream integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
filter、map 和 flatMap 方法
流的转换产生了一个流,该流的元素来源于其他流。 filter 转换生成一个匹配条件的新流。如:
//长单词流
//filter 的参数是一个 Predicate<T> 对象
Stream<String> longWords = Stream.of(words).filter(w -> w.length() > 12);
将流中的值进行某种形式的转换(map方法)
//将所有单词转换成小写形式:
//使用方法引用
Stream<String> lowercaseWords = Stream.of(words).map(String::toLowerCase);
//使用 lambda 表达式,产生一个包含每个单词的第一个字符的流
Stream<String> firstLetters = Stream.of(words).map(s -> s.substring(0, 1));
当使用 map 方法时,函数将作用于每一个元素,这样就产生了一个包含最终结果的新流。假设有一个函数,返回的不是一个值,而是一个包含多个值的流,如:
public Stream letters(String s){
List result = new ArrayList<>();
for (int i = 0; i < s.length(); i++){
result.add(s.substring(i, i + 1));
}
return Stream.of(result);
}
letters(“boat”) 返回的是流 [“b”, “o”, “a”, “t”]。
将该 letters() 方法映射到一个字符串流:
Stream
结果是:
[…[“b”,”o”,”a”,”t”],[“y”,”o”,”u”],…], 要将其展开为一个只包含字符串的流 […”b”,”o”,”a”,”t”,”y”,”o”,”u”,…]使用 flatMap 方法而不是 map 方法:
Stream<String> flatResult = Stream.of(words).flatMap(w -> letters(w));
提取子流和组合流
stream.limit(n) 会返回一个包含 n 个元素的新流(如果原始流的长度小于n,则会返回原始流),这个方法特别适用于裁剪指定长度的流。例如:
//产生一个10个随机数的流 Stream<Double> randomsDouble = Stream.generate(Math::random).limit(10);
stream.skip(n) 正好相反,它会丢弃前 n 个元素。
Stream<String> randomsSkip = Stream.of("gently", "down", "the", "stream").skip(1);
concat 连接流,第一个流不应该是无限的
Stream<String> combined = Stream.concat(letters("hello"), letters("world")); //生成 combined: ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]
其他流转换
distinct方法会根据原始流中的元素返回一个具有相同顺序、抑制了重复元素的新流。
Stream<String> uniqueWords = Stream.of("gently", "down","down", "the", "stream").distinct();
Stream排序
简单归约
归约是流的终止操作,也就是从流中获得答案 。