当处理集合时,通常会迭代所有元素并对其中的每一个进行处理,这样的处理使它很难被并行运算。在java8中的StreamAPI可以实现相同功能同时支持并行运算。一个Stream表面上看与一个集合很类似,允许你改变和获取数据。但是实际上它与集合有很大区别:
- Stream自己不会存储元素。元素可以被存储在底层的集合中,或者根据需要产生出来。
- Stream操作符不会改变源对象。相反,它们会返回一个持有结果的新Stream。
- Stream操作符可能是延迟执行的。这意味着它们会等到需要结果的时候才执行。
Stream遵循“做什么,而不是怎么去做”的原则。使用步骤:
- 创建一个Stream。
- 在一个或多个步骤中,指定将初始Stream转换为另一个Stream的中间操作。
- 使用一个终止操作来产生一个结果。该操作会强制它之前的延迟操作立即执行。在这之后,该Stream就不会再被使用了。
创建Stream
- 通过java8在Collection接口中新添加的stream方法,可以将任何集合转化为一个Stream。
- Stream<String> word = Stream.of("early","more","day");
- 使用Arrays.stream(arrray, from, to)方法将数组的一部分转化为Stream。
- Stream.empty(); 创建一个不含任何元素的Stream的静态方法。
- Stream接口有两个用来创建无限Stream的静态方法。generate方法接受一个无参数的函数,因此可以创建一个含有常量值的Stream: Stream<String> echos = Stream.generate(() -> "Echo"); 创建一个含有随机数字的Stream: Stream<Double> randoms = Stream.generate(Math::random); 创建一个形如0、1、2、3...的无限序列,可以使用iterate方法。它接受一个“种子(seed)”值和一个函数(从技术上讲,是一个UnaryOperator<T>接口的对象)作为参数,并且会对之前的值重复应用该函数。如:Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE)); 序列中的第一个元素是种子BigInteger.ZERO; 第二个值是f(seed),或者1(一个大整数); 下一个元素是f(f(seed)), 或者2;以此类推。
filter、map和flatMap方法
流转换是指从一个流中读取数据,并将转换后的数据写入到另一个流中。将一个字符串流转换到另一个只包含长单词的流:
List<String> wordList = ...;
Stream<String> words = wordList.stream();
Stream<String> longWords = words.filter(w -> w.length() > 12);
我们经常需要对一个流中的值进行某种形式的转换。这时可以考虑使用map方法,并传递给它一个执行转换的函数。 可以通过:
Stream<String> lowercaseWords = words.map(String::toLoweCase);
将所有单词转换为小写形式,这里使用了带有一个方法引用的map方法。通常,用lambda表达式来代替方法表达式;
Stream<Character> firstChars = words.map(s -> s.charAt(0));
该方法产生的流会包含每个单词的第一个字符。
使用map方法时,会对每个元素应用一个函数,并将返回的值收集到一个新的流中。但当函数返回值不是一个值而是一个包含多个值的流,此时要将多个流展开成成一个流就需要使用flatMap方法,而不是map方法。
提取子流和组合流
Stream.limit(n)会返回一个包含n个元素的新流(如果原始流的长度小于n,则会返回原始的流)。这个方法特别适用于剪裁指定长度的流。例如:
Stream<Double> randoms = Stream.generate(Math::random).limit(100);
Stream.skip(n)正好相反,它会丢弃前面的n个元素。同时还可以使用Stream类的静态方法concat将两个流连在一起。