Java 8 中的 Stream 是对集合(Collection)对象功能的加强,它专一于对集合对象进行各类很是便利、高效的聚合操做(aggregate operation),或者大批量数据操做 (bulk data operation)。html
Stream表明数据流,流中的数据元素的数量多是有限的,也多是无限的。java
Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的数据的访问和有效管理(增删改),而Stream并无提供访问和管理元素的方式,而是经过声明数据源的方式,利用可计算的操做在数据源上执行,固然BaseStream.iterator()
和 BaseStream.spliterator()
操做提供了遍历元素的方法。编程
Java Stream提供了提供了串行和并行两种类型的流,保持一致的接口,提供函数式编程方式,以管道方式提供中间操做和最终执行操做,为Java语言的集合提供了现代语言提供的相似的高阶函数操做,简化和提升了Java集合的功能。api
Stream接口还包含几个基本类型的子接口如IntStream, LongStream 和 DoubleStream。数组
关于流和其它集合具体的区别,能够参照下面的列表:多线程
filter
不会将数据源中的数据删除。limit(n)
或 findFirst()
,这些操做但是实现"短路"(Short-circuiting),访问到有限的元素后就能够返回。流的操做是以管道的方式串起来的。流管道包含一个数据源,接着包含零到N个中间操做,最后以一个终点操做结束。oracle
全部的流操做均可以串行执行或者并行执行。
除非显示地建立并行流,不然Java库中建立的都是串行流。 Collection.stream()
为集合建立串行流而Collection.parallelStream()
为集合建立并行流。IntStream.range(int, int)
建立的是串行流。经过parallel()
方法能够将串行流转换成并行流,sequential()
方法将流转换成串行流。app
除非方法的Javadoc中指明了方法在并行执行的时候结果是不肯定(好比findAny、forEach),不然串行和并行执行的结果应该是同样的。dom
能够经过多种方式建立流:函数式编程
一、经过集合的stream()
方法或者parallelStream()
,好比Arrays.asList(1,2,3).stream()
。
二、经过Arrays.stream(Object[])
方法, 好比Arrays.stream(new int[]{1,2,3})
。
三、使用流的静态方法,好比Stream.of(Object[])
, IntStream.range(int, int)
或者 Stream.iterate(Object, UnaryOperator)
,如Stream.iterate(0, n -> n * 2)
,或者generate(Supplier<T> s)
如Stream.generate(Math::random)
。
四、BufferedReader.lines()
从文件中得到行的流。
五、Files
类的操做路径的方法,如list
、find
、walk
等。
六、随机数流Random.ints()
。
七、其它一些类提供了建立流的方法,如BitSet.stream()
, Pattern.splitAsStream(java.lang.CharSequence)
, 和 JarFile.stream()
。
八、更底层的使用StreamSupport
,它提供了将Spliterator
转换成流的方法。
中间操做会返回一个新的流,而且操做是延迟执行的(lazy),它不会修改原始的数据源,并且是由在终点操做开始的时候才真正开始执行。
distinct
保证输出的流中包含惟一的元素,它是经过Object.equals(Object)
来检查是否包含相同的元素。
下面的例子则使用 distinct 来找出不重复的单词。
List<String> l = Stream.of("a","b","c","b").distinct().collect(Collectors.toList()); System.out.println(l); 输出结果:[a, b, c]
filter
返回的流中只包含知足断言(predicate)的数据。
下面的代码返回流中的偶数集合。
List<Integer> l = IntStream.range(1,10).filter( i -> i % 2 == 0).boxed().collect(Collectors.toList()); System.out.println(l); 输出结果:[2, 4, 6, 8]
map
方法将流中的元素映射成另外的值,新的值类型能够和原来的元素的类型不一样。
做用就是把 input Stream 的每个元素,映射成 output Stream 的另一个元素。
下面的代码中将字符元素映射成它的哈希码(ASCII值)。
List<Integer> l = Stream.of('a','b','c').map( c -> c.hashCode()).collect(Collectors.toList()); System.out.println(l); 输出结果:[97, 98, 99]
从上面例子能够看出,map 生成的是个 1:1 映射,每一个输入元素,都按照规则转换成为另一个元素。还有一些场景,是一对多映射关系的,这时须要 flatMap。
Stream<List<Integer>> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6) ); List<Integer> outputList = inputStream.flatMap((childList) -> childList.stream()) .collect(Collectors.toList()); 输出结果:[1,2,3,4,5,6]
将最底层元素抽出来放到一块儿,最终 output 的新 Stream 里面。
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素
List<Integer> l = IntStream.range(1,100).limit(5).skip(1) .boxed()//IntStream转换成Stream<Integer> .collect(Collectors.toList()); System.out.println(l); 输出结果:[2, 3, 4, 5]
peek 对每一个元素执行操做并返回一个新的 Stream
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList()); 输出结果: Filtered value: three Mapped value: THREE Filtered value: four Mapped value: FOUR
forEach 不能修改本身包含的本地变量值,也不能用 break/return 之类的关键字提早结束循环。
sorted()
将流中的元素按照天然排序方式进行排序,若是元素没有实现Comparable
,则终点操做执行时会抛出java.lang.ClassCastException
异常。
sorted(Comparator<? super T> comparator)
能够指定排序的方式。
对于有序流,排序是稳定的。对于非有序流,不保证排序稳定。
降序排列: List<String> li = Stream.of("c","d", "b", "a").sorted((s1,s2) ->s2.compareTo(s1)) .collect(Collectors.toList()); System.out.println(li); 输出结果:[d, c, b, a] 升序排序: List<String> li = Stream.of("c","d", "b", "a").sorted((s1,s2) ->s1.compareTo(s2)) .collect(Collectors.toList()); System.out.println(li); 输出结果:[a, b, c, d]
这一组方法用来检查流中的元素是否知足断言。
allMatch
只有在全部的元素都知足断言时才返回true,不然flase,流为空时老是返回true
anyMatch
只有在任意一个元素知足断言时就返回true,不然flase,
noneMatch
只有在全部的元素都不知足断言时才返回true,不然flase,
System.out.println(Stream.of(1,2,3,4,5).allMatch( i -> i > 0)); 输出结果:true System.out.println(Stream.of(1,2,3,4,5).anyMatch( i -> i > 0)); 输出结果:true System.out.println(Stream.of(1,2,3,4,5).noneMatch( i -> i > 0)); 输出结果:false System.out.println(Stream.<Integer>empty().allMatch( i -> i > 0)); 输出结果:true System.out.println(Stream.<Integer>empty().anyMatch( i -> i > 0)); 输出结果:false System.out.println(Stream.<Integer>empty().noneMatch( i -> i > 0)); 输出结果:true
count
方法返回流中的元素的数量
System.out.println(Stream.of("c","d", "b", "a").count()); 输出结果:4
使用一个collector执行mutable reduction
操做。辅助类Collectors
提供了不少的collector,java.util.stream.Collectors 类的主要做用就是辅助进行各种有用的 reduction 操做,例如转变输出为 Collection,把 Stream 元素进行归组。
System.out.println(Stream.of("c","d", "b", "a").collect(Collectors.toList())); 输出结果:[c, d, b, a]
findAny()
返回任意一个元素,若是流为空,返回空的Optional,对于并行流来讲,它只须要返回任意一个元素便可,因此性能可能要好于findFirst()
,可是有可能屡次执行的时候返回的结果不同。
findFirst()
返回第一个元素,若是流为空,返回空的Optional。
String l = Stream.of("cd","dc", "acb", "ade").filter(e -> e.contains("a")).findFirst().get(); System.out.println(l); 输出结果:acb
forEach
遍历流的每个元素,执行指定的action。它是一个终点操做,和peek
方法不一样。这个方法不担保按照流的encounter order
顺序执行,若是对于有序流按照它的encounter order
顺序执行,你可使用forEachOrdered
方法。
Stream.of(1,2,3).forEach(System.out::println); 输出结果: 1 2 3
max
返回流中的最大值,
min
返回流中的最小值。
System.out.println(IntStream.range(1,10).max().getAsInt()); System.out.println(IntStream.range(1,10).min().getAsInt()); 输出结果: 9 1
这个方法的主要做用是把 Stream 元素组合起来。它提供一个起始值(种子),而后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就至关于
Integer sum = integers.reduce(0, (a, b) -> a+b); 或
Integer sum = integers.reduce(0, Integer::sum);
也有没有起始值的状况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。
Integer total = Stream.of(2,1,3,4,5).reduce( (x, y) -> x +y).get(); System.out.println(total); Integer total2 = Stream.of(2,1,3,4,5).reduce(2, (x, y) -> x +y); System.out.println(total2); 输出结果: 15 17
将流中的元素放入到一个数组中。
像上面所说的,流操做能够是串行的,也能够是并行的。串行操做经过单线程执行,而并行操做则经过多线程执行。
下面的例子就演示了如何使用并行流进行操做来提升运行效率,代码很是简单。
首先咱们建立一个大的list,里面的元素都是惟一的:
int max = 1000000; List<String> values = new ArrayList<>(max); for (int i = 0; i < max; i++) { UUID uuid = UUID.randomUUID(); values.add(uuid.toString()); }
如今,咱们测量一下对这个集合进行排序所使用的时间。
long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis)); // sequential sort took: 899 ms
long t0 = System.nanoTime(); long count = values.parallelStream().sorted().count(); System.out.println(count); long t1 = System.nanoTime(); long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis)); // parallel sort took: 472 ms
如你所见,全部的代码段几乎都相同,惟一的不一样就是把stream()
改为了parallelStream()
, 结果并行排序快了50%。