Stream 流

初识Stream流

简单认识一下Stream:Stream类中的官方介绍:

  1. /**
  2. * A sequence of elements supporting sequential and parallel aggregate
  3. * operations. The following example illustrates an aggregate operation using
  4. * {@link Stream} and {@link IntStream}:
  5. *
  6. * <pre>{@code
  7. * int sum = widgets.stream()
  8. * .filter(w -> w.getColor() == RED)
  9. * .mapToInt(w -> w.getWeight())
  10. * .sum();
  11. * }</pre>
  12. * In this example, {@code widgets} is a {@code Collection<Widget>}. We create
  13. * a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()},
  14. * filter it to produce a stream containing only the red widgets, and then
  15. * transform it into a stream of {@code int} values representing the weight of
  16. * each red widget. Then this stream is summed to produce a total weight.
  17. *
  18. */

看这么一个案例,类似于js中的链式操作。就明白了大概流是什么样子的。类似于 Linux的 pipeline

流包含三部分组成:

  1. 零个或多个中间操作
  2. 终止操作

流操作的分类:

  1. 惰性求值
  2. 及早求值
  1. stream.xxx().yyy().zzz().count();

中间操作:惰性求值。只有在count()被调用的时候,中间的操作才会进行求值。

及早求值,count()方法调用的时候立刻求值,这就叫做及早求值。

流中的及早求值只会有一个。

生成流的三种方式

  1. public class StreamTest {
  2. public static void main(String[] args) {
  3. //本章才正式的开始对流进行讲解。
  4. //第一种方式,通过of方法
  5. Stream stream1 = Stream.of("hello","world");
  6. //第二种方式,通过数组方式
  7. String[] strings = new String[]{"hello","world"};
  8. Stream stream2 = Arrays.stream(strings);
  9. Stream stream3 = Stream.of(strings); //of的底层就是 通过Arrays.stream()来实现的.
  10. //第三种方式,通过集合.stream
  11. List<String> list = Arrays.asList("hello", "world");
  12. list.stream();
  13. }
  14. }

流怎么用(入门使用)

用法一:

  1. public class streamTest2 {
  2. public static void main(String[] args) {
  3. //Intstream 怎么用
  4. IntStream.of(5, 6, 7).forEach(System.out::println);
  5. System.out.println("----");
  6. IntStream.range(3, 8).forEach(System.out::println);
  7. System.out.println("----");
  8. IntStream.rangeClosed(3, 8).forEach(System.out::println);
  9. System.out.println("----");
  10. }
  11. }
  1. public class streamTest3 {
  2. public static void main(String[] args) {
  3. //List类型,int的值, 对每一个元素*2,然后加起来,得到结果
  4. List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
  5. //以前的写法
  6. // int i=0;
  7. // for (Integer i : list) {
  8. // sum += 2;
  9. // }
  10. // sum...
  11. //stream的写法,一行
  12. System.out.println(list.stream().map(integer -> integer*2).reduce(0,Integer::sum));
  13. //reduce方法,map()方法的调用会在下面进行详解.
  14. //实现简单,语义更明确
  15. }
  16. }

reduce(),终止操作,及早求值.

深入stream流

函数式编程,最根本的一点:方法传递的是行为.以前传递的都是数据.

  1. Collection提供了新的stream()方法
  2. **流不存储值,通过管道的方式获取值
  3. 本质是函数式的,对流的操作会生成一个结果,不过并不会修改底层的数据源,集合可以作为流的底层数据源
  4. 延迟查找,很多流操作(过滤,映射,排序等)都可以延迟实现(lazy)

看这个Example:

  1. public class streamTest4 {
  2. public static void main(String[] args) {
  3. Stream<String> stream = Stream.of("hello", "world", "hello world");
  4. //lambda写法
  5. //stream.toArray(length -> new String[length]);
  6. //方法引用的写法 (构造方法引用)
  7. String[] stringArray = stream.toArray(String[]::new);
  8. Arrays.asList(stringArray).forEach(System.out::println);
  9. }
  10. }

已知流,转List

  1. //已知流,转List
  2. Stream<String> stream = Stream.of("hello", "world", "hello world");
  3. List<String> collect = stream.collect(Collectors.toList());
  4. collect.forEach(System.out::println);

collect()方法详解 - Collectors里面也是通过collect(三个参数)这个方法来实现的

  1. /**
  2. * 第一个参数介绍
  3. * Performs a <a href="package-summary.html#MutableReduction">mutable
  4. * reduction</a> operation on the elements of this stream. A mutable
  5. * reduction is one in which the reduced value is a mutable result container,
  6. * such as an {@code ArrayList}, and elements are incorporated by updating
  7. * the state of the result rather than by replacing the result. This
  8. * produces a result equivalent to: 第二个参数的介绍
  9. * <pre>{@code
  10. * R result = supplier.get();
  11. * for (T element : this stream)
  12. * accumulator.accept(result, element);
  13. * return result;
  14. * }</pre>
  15. * 被并行化. 流带来的好处.
  16. * <p>Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations
  17. * can be parallelized without requiring additional synchronization.
  18. * 这是一个终止操作.
  19. * <p>This is a <a href="package-summary.html#StreamOps">terminal
  20. * operation</a>.
  21. * 方法签名是非常适合于使用方法引用的方式.就是最下面举例的Example
  22. * @apiNote There are many existing classes in the JDK whose signatures are
  23. * well-suited for use with method references as arguments to {@code collect()}.
  24. * For example, the following will accumulate strings into an {@code ArrayList}:
  25. * <pre>{@code
  26. * List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add,
  27. * ArrayList::addAll);
  28. * }</pre>
  29. * 扩展功能:字符串实现拼接的操作
  30. * <p>The following will take a stream of strings and concatenates them into a
  31. * single string:
  32. * <pre>{@code
  33. * String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
  34. * StringBuilder::append)
  35. * .toString();
  36. * }</pre>
  37. *
  38. * @param <R> type of the result
  39. 第一个参数:结果容器,如LinkList
  40. * @param supplier a function that creates a new result container. For a
  41. * parallel execution, this function may be called
  42. * multiple times and must return a fresh value each time.
  43. 第二个参数:关联性的,不冲突的,无状态的,用于合并. item->list
  44. * @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
  45. * <a href="package-summary.html#NonInterference">non-interfering</a>,
  46. * <a href="package-summary.html#Statelessness">stateless</a>
  47. * function for incorporating an additional element into a result
  48. 第三个参数:用于融合,将上次遍历得到的集合融合到最终的结果集中.
  49. * @param combiner an <a href="package-summary.html#Associativity">associative</a>,
  50. * <a href="package-summary.html#NonInterference">non-interfering</a>,
  51. * <a href="package-summary.html#Statelessness">stateless</a>
  52. * function for combining two values, which must be
  53. * compatible with the accumulator function
  54. * @return the result of the reduction
  55. */
  56. <R> R collect(Supplier<R> supplier,
  57. BiConsumer<R, ? super T> accumulator,
  58. BiConsumer<R, R> combiner);

通过源码解释:我们得知实现流转List的底层就是通过这个三参数的collect方法来实现的,我们逐一来对这三个参数进行了解.

1.参数1:supplier,类型Supplier的函数式接口. 功能:用来提供一个初步的List容器

2.参数2: accumulator,类型BiConsumer的函数式接口. 功能:累加器,将流中的一个个元素累加进集合中.

3.参数3:combiner,类型为BiConsumer的函数式接口. 功能:组合器,将上一次遍历得到一个的集合进行融合到最终的List中.

自行阅读上面的Collector的文档.我说的这些内容都在里面有所体现.

通过上述的了解,我们可以通过三参数的collect()方法,来自己实现一个底层stream转换List的实现,如下:

  1. //功能描述:已知流,转List
  2. Stream<String> stream = Stream.of("hello", "world", "hello world");
  3. List<String> collect = stream.collect(Collectors.toList());
  4. collect.forEach(System.out::println);
  5. //使用collect(三个参数)的底层方法来实现这个操作. 因为这个三参的collect()方法就是这个操作的底层.
  6. List<String> list = stream.collect(() -> new ArrayList(),(theList,item)->theList.add(item),(theList1,theList2)->theList1.addAll(theList2));
  7. //通过方法引用优化后的代码如下:
  8. //优化后的代码:
  9. List<String> list1 = stream.collect(LinkedList::new,LinkedList::add,LinkedList::addAll);

上述源码注释中还提供了 字符串拼接的操作


  1. * 扩展功能:字符串实现拼接的操作
  2. * <p>The following will take a stream of strings and concatenates them into a
  3. * single string:
  4. * <pre>{@code
  5. * String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
  6. * StringBuilder::append)
  7. * .toString();
  8. * }</pre>
  9. *

流的其他使用

  1. //使用 Collectors.toCollection()方法来实现 流转List
  2. Stream<String> stream = Stream.of("hello", "world", "hello world");
  3. // ArrayList<String> list = stream.collect(Collectors.toCollection(ArrayList::new));
  4. // list.forEach(System.out::println);
  5. //使用 Collectors.toCollection()方法来实现 流转Set
  6. Set<String> list = stream.collect(Collectors.toCollection(TreeSet::new));
  7. list.forEach(System.out::println);
  8. //使用 方法来实现,流转String字符串
  9. stream.collect(Collectors.joining());

以后开发的时候,要多考虑,List,Set,这些转换是否可以使用JAVA8提供的这些stream来实现.用到实际开发中.

再思考

  1. public class StreamTest5 {
  2. public static void main(String[] args) {
  3. //集合,全部转换大写,然后输出.
  4. List<String> list = Arrays.asList("hello", "world", "hello world");
  5. //要考虑能不能用函数式接口,lambda表达式的技能?显然是可以呢
  6. //这是不是映射? 先要要用map. 给定一个参数,返回一个结果.
  7. //java8提供这些接口,就是为了方便开发者.合理的应用.
  8. list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);
  9. //求出每个数字的平方,然后打印出来
  10. List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5);
  11. list1.stream().map(item -> item * item).collect(Collectors.toList()).forEach(System.out::println);
  12. }
  13. }
  1. //要考虑能不能用函数式接口,lambda表达式的技能?显然是可以呢
  2. //这是不是映射? 先要要用map. 给定一个参数,返回一个结果.
  3. //java8提供这些接口,就是为了方便开发者.合理的应用.

flatMap()方法:扁平化映射


  1. /**
  2. * Returns a stream consisting of the results of replacing each element of
  3. * this stream with the contents of a mapped stream produced by applying
  4. * the provided mapping function to each element. Each mapped stream is
  5. * {@link java.util.stream.BaseStream#close() closed} after its contents
  6. * have been placed into this stream. (If a mapped stream is {@code null}
  7. * an empty stream is used, instead.)
  8. *
  9. * <p>This is an <a href="package-summary.html#StreamOps">intermediate
  10. * operation</a>.
  11. *
  12. * @apiNote
  13. * The {@code flatMap()} operation has the effect of applying a one-to-many
  14. * transformation to the elements of the stream, and then flattening the
  15. * resulting elements into a new stream.
  16. *
  17. * <p><b>Examples.</b>
  18. *
  19. * <p>If {@code orders} is a stream of purchase orders, and each purchase
  20. * order contains a collection of line items, then the following produces a
  21. * stream containing all the line items in all the orders:
  22. * <pre>{@code
  23. * orders.flatMap(order -> order.getLineItems().stream())...
  24. * }</pre>
  25. *
  26. * <p>If {@code path} is the path to a file, then the following produces a
  27. * stream of the {@code words} contained in that file:
  28. * <pre>{@code
  29. * Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
  30. * Stream<String> words = lines.flatMap(line -> Stream.of(line.split(" +")));
  31. * }</pre>
  32. * The {@code mapper} function passed to {@code flatMap} splits a line,
  33. * using a simple regular expression, into an array of words, and then
  34. * creates a stream of words from that array.
  35. *
  36. * @param <R> The element type of the new stream
  37. * @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
  38. * <a href="package-summary.html#Statelessness">stateless</a>
  39. * function to apply to each element which produces a stream
  40. * of new values
  41. * @return the new stream
  42. */
  43. <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

和map很像,但是完全不同.否则就不会存在这个方法了.

扁平化的map;

1.map中映射的时候, 一个集合有三个List,每个List又有不同的值.映射完之后,模块还在

2.flatMap中映射的时候,一个集合有三个List, 打平的去给融合的到一个list中.

实例Example:

  1. //每一个元素都乘方,然后将数据作为一个整体,输出. 当做一个集合. 就要用flatmap()
  2. Stream<List<Integer>> listStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
  3. listStream.flatMap(theList->theList.stream()).map(integer -> integer*integer).forEach(System.out::println);

Stream类中的其他方法介绍

  1. generate()
  1. /**
  2. * Returns an infinite sequential unordered stream where each element is
  3. * generated by the provided {@code Supplier}. This is suitable for
  4. * generating constant streams, streams of random elements, etc.
  5. *
  6. * @param <T> the type of stream elements
  7. * @param s the {@code Supplier} of generated elements
  8. * @return a new infinite sequential unordered {@code Stream}
  9. */
  10. public static<T> Stream<T> generate(Supplier<T> s) {
  11. Objects.requireNonNull(s);
  12. return StreamSupport.stream(
  13. new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
  14. }

如何使用?如下Example:

  1. public class StreamTest6 {
  2. public static void main(String[] args) {
  3. Stream<String> generate = Stream.generate(UUID.randomUUID()::toString);
  4. System.out.println(generate.findFirst());
  5. }
  6. }
  1. Optional<T> findFirst();

为什么这个findFirst()方法会返回一个Optional?

因为Optional,就是为了规避NPE的问题.

所以此处需要使用Optional.ifPresent(),这才是Optional类的正确使用方法.应该修改为:

  1. public class StreamTest6 {
  2. public static void main(String[] args) {
  3. Stream<String> generate = Stream.generate(UUID.randomUUID()::toString);
  4. generate.findFirst().ifPresent(System.out::println);
  5. }
  6. }
  1. iterate()
  1. public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
  2. Objects.requireNonNull(f);
  3. final Iterator<T> iterator = new Iterator<T>() {
  4. @SuppressWarnings("unchecked")
  5. T t = (T) Streams.NONE;
  6. @Override
  7. public boolean hasNext() {
  8. return true;
  9. }
  10. @Override
  11. public T next() {
  12. return t = (t == Streams.NONE) ? seed : f.apply(t);
  13. }
  14. };
  15. return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
  16. iterator,
  17. Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
  18. }

如何使用?

  1. package com.dawa.jdk8.stream;
  2. import java.util.UUID;
  3. import java.util.stream.Stream;
  4. public class StreamTest6 {
  5. public static void main(String[] args) {
  6. Stream<String> generate = Stream.generate(UUID.randomUUID()::toString);
  7. generate.findFirst().ifPresent(System.out::println);
  8. //如果不加限制,iterate 会变成一个无限流.
  9. //Stream.iterate(1, integer -> integer + 2).forEach(System.out::println);
  10. //所以在使用的时候一定不要单独使用.
  11. //要搭配limit()方法,一个中间操作,使用.
  12. Stream.iterate(1, integer -> integer + 2).limit(6).forEach(System.out::println);
  13. }
  14. }

注意: //如果不加限制,iterate 会变成一个无限流.

//所以在使用的时候一定不要单独使用.

//要搭配limit()方法,一个中间操作,使用.

找出(1,3,5,7,9)流中大于2的元素,然后将每个元素乘以2,然后忽略流中的前两个元素,然后再取出流的前两个元素,最后求出流中元素的总和.

  1. //找出(1,3,5,7,9)流中大于2的元素,然后将每个元素乘以2,然后忽略流中的前两个元素,然后再取出流的前两个元素,最后求出流中元素的总和.
  2. // Stream<Integer> stream = Stream.of(1, 3, 5, 7, 9);
  3. Stream<Integer> stream = Stream.iterate(1, integer -> integer + 2).limit(6);//通过iterate方法来获取值
  4. System.out.println(stream.filter(integer -> integer > 2).mapToInt(integer -> integer * 2).skip(2).limit(2).sum());
  5. //用到的方法. map,mapToint,skip,limit.
  1. ...skip() 跳过

  2. ...limit() 截取

  3. ...map().mapToInt(),mapToDouble().... 映射

    mapToInt... 避免自动装箱和自动拆箱.(避免性能损耗).

  4. ...sum(),min(),max(). 最大,最小,求和等等

    ​ sum()返回值类型是int.

    ​ min().max(),返回值类型是:OptionalInt.

    为什么呢?Optional类,因为使用与否,本质是取决于,这个值可不可能为空.

  5. summaryStatistics():小结,总结.流中的数据的简单统计.

    如:一个小结对象:IntSummaryStatistics{count=2, sum=32, min=14, average=16.000000, max=18}

    这个类提供了各样的方法.

    1. getCount
    2. getSum
    3. getMin
    4. getMax
    5. getAverage
    6. toString

上面的案例里面,已经使用了Stream类中的大量的方法.如有需要,自行查询官方源码.

注意:在对流进行中间操作的时候,会返回一个全新的流.直到进行一个终止操作的时候,才会得到最终的结果.

关于流被关闭的问题.

刚才无意之间,在操作的时候,抛出来一个这样的异常,提示流已经被关闭.

复盘一下代码:


  1. Stream<Integer> stream = Stream.iterate(1, integer -> integer + 2).limit(6);
  2. System.out.println(stream);
  3. System.out.println(stream.filter(integer -> integer > 2));
  4. System.out.println(stream.distinct());

流的特点

  1. 一旦被操作了.就会自动关闭流.
  2. 同一个流不能被重复操作.
  3. 流关闭了就不能继续操作了.
  4. 每一次中间操作都会返回一个新的操作流对象.

如何规避?

  1. 先生成一个流,操作完之后,再生成一个流.

  2. 紧接着去操作新生成的流.

    1. Stream<Integer> stream = Stream.iterate(1, integer -> integer + 2).limit(6);//通过iterate方法来获取值
    2. System.out.println(stream);
    3. Stream<Integer> stream1 = stream.filter(integer -> integer > 2);
    4. System.out.println(stream1);
    5. Stream<Integer> stream2 = stream1.distinct();
    6. System.out.println(stream2);

关于中间操作和终止操作之间的本质区别

Example:

  1. public class StreamTest7 {
  2. public static void main(String[] args) {
  3. List<String> list = Arrays.asList("hello", "world", "hello world");
  4. //首字母大写,其他的小写,然后打印输出.
  5. list.stream().map(item -> item.substring(0, 1).toUpperCase() + item.substring(1)).forEach(System.out::println);
  6. //另外的操作
  7. list.stream().map(item -> {
  8. String result = item.substring(0, 1).toUpperCase() + item.substring(1);
  9. System.out.println("test");
  10. return result;
  11. });//运行之后没有值
  12. //另外的操作
  13. list.stream().map(item -> {
  14. String result = item.substring(0, 1).toUpperCase() + item.substring(1);
  15. System.out.println("test");
  16. return result;
  17. }).forEach(System.out::println);//运行之后能够执行.
  18. //原因:中间操作,如果没有终止操作,是不会自己执行的,是lazy类型的.是惰性求值的.
  19. }
  20. }

原因:中间操作,如果没有终止操作,是不会自己执行的,是lazy类型的.是惰性求值的.

再考虑一下效率问题.

也许可能会认为,多次中间操作,会多次循环,会降低效率.

其实只执行了一次.并不会影响效率.

会有一个容器,把所有的中间操作放在一起.一次执行.并不会有冗余操作.

如何区分中间操作和终止操作

中间操作都会返回一个Stream对象,比如说返回Stream,...等

再看另外一个操作:关于中间操作和终止操作的影响

  1. public class StreamTest8 {
  2. public static void main(String[] args) {
  3. IntStream.iterate(0, i -> (i + 1) % 2).distinct().limit(6).forEach(System.out::println);
  4. }
  5. }

上述代码跑起来之后是不会自动终止的.

应该修改为:

  1. public class StreamTest8 {
  2. public static void main(String[] args) {
  3. IntStream.iterate(0, i -> (i + 1) % 2).limit(6).distinct().forEach(System.out::println);
  4. }
  5. }

这个原因就是因为中间操作和终止操作的影响.

  1. 如果先执行limit,就是一个终止操作.然后再消除重复一次,程序就会终止.

  2. 如果先执行消除重复操作,就是第一种情况,返回一个流,再截取6个,流并没有关闭.


Stream底层深入

  • 和迭代器又不同的是,Stream可以并行化操作,迭代器只能命令式地,串行化操作
  • 当使用串行方式去遍历时,每个item读完后再读下一个item.
  • 使用并行去遍历时,数据会被分成多个段,其中每个都在不同的线程中去处理,然后将结果一起输出
  • Stream流的并行操作依赖于JAVA7中引入的Fork/Join框架.

流的本质三个主要操作:源->中间操作->中间操作->...->终止操作

这里我们借助一个SQL来进行参照学习

  1. select name from student where age>20 and address='beijing' order by age desc;

描述性的语言

通过stream把这个SQL描述出来

  1. student.stream()
  2. .filter(student->student.getAge>20)
  3. .filter(student->student.getAddress()
  4. .equals("beijing")
  5. .sorted(...)
  6. .forEach(student->System.out.println(student.getName()));
  7. //这个描述和上面的SQL其实是等价的.

你只是给DB发送了一个指令,而没有所怎么去找.你只是给出了一个描述,然后根据降序之类的规则去进行筛选.对于整个过程,你完全没有告诉底层去怎么实现.

SQL是这样,Stream也是这样.只是描述性的语言.

这种方式就叫做内部迭代.

内部迭代

外部迭代(之前的方式)

  1. 不是描述性的处理方式,完全基于老版本的实现方式.和描述性语言相比,这个可读性太差.

  2. 都是串行化的操作,不能并行化

  1. for(int i=0;i<student.size();i++){
  2. Student student = students.get(i);
  3. if(student.getAge()>20&&student.getAddress().equals("beijing")){
  4. list.add(student);
  5. }
  6. }
  7. //然后进行排序
  8. Collection.sort(list,Comparator()...);
  9. //然后再去寻找需要的东西
  10. for(Student student:list){
  11. System.out.println(student.getName);
  12. }

内部迭代(描述性语言)

新人也是能看懂的吧.

  1. student.stream()
  2. .filter(student->student.getAge>20)
  3. .filter(student->student.getAddress()
  4. .equals("beijing")
  5. .sorted(...)
  6. .forEach(student->System.out.println(student.getName()));

Stream的出现和集合是密不可分的.

  1. 可以执行并行化的操作.
  2. 底层执行的时候,不是一个条件一个条件的去循环遍历的

内部迭代和外部迭代最本质的区别

  1. 耦合度
  2. 底层处理方式
  3. 并行和串行化的不同

总结

  1. 集合关注的是数据与数据存储本身;
  2. 流关注的则是对数据的计算;
  3. 流与迭代器类似的一点是:流是无法重复使用和消费的;
  4. 底层使用:fork/join 方法,分解大任务为小任务去执行.

最需要注意的

流的执行原理一定不是一个方法一个方法的执行循环遍历的.


并行流的使用(parallelStream)

串行流(stream())和并行流(parallelStream())的执行效率判断.

  • 串行流
  1. package com.dawa.jdk8.stream;
  2. import java.util.ArrayList;
  3. import java.util.UUID;
  4. import java.util.concurrent.TimeUnit;
  5. public class StreamTest9 {
  6. public static void main(String[] args) {
  7. ArrayList<Object> list = new ArrayList<>(5000000);
  8. for (int i = 0; i < 5000000; i++) {
  9. list.add(UUID.randomUUID().toString());
  10. }
  11. System.out.println("开始排序");
  12. long startTime = System.nanoTime();//纳秒 比毫秒的精度高
  13. list.stream().sorted().count();
  14. long endTime = System.nanoTime(); //纳秒, 结束时间
  15. long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
  16. System.out.println("耗时:" + millis + "毫秒");
  17. }
  18. }

运行结果: 串行耗时:4.0秒

  • 并行流
  1. public class StreamTest9 {
  2. public static void main(String[] args) {
  3. ArrayList<Object> list = new ArrayList<>(5000000);
  4. for (int i = 0; i < 5000000; i++) {
  5. list.add(UUID.randomUUID().toString());
  6. }
  7. System.out.println("开始排序");
  8. long startTime = System.nanoTime();//纳秒 比毫秒的精度高
  9. list.parallelStream().sorted().count();
  10. long endTime = System.nanoTime(); //纳秒, 结束时间
  11. long millis = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
  12. System.out.println("耗时:" + millis + "毫秒");
  13. }
  14. }

运行结果:并行耗时:1.1秒

并行和串行 - 时间成本相差:3-5倍.

短路

  1. public class StreamTest10 {
  2. public static void main(String[] args) {
  3. List<String> list = Arrays.asList("hello", "world", "hello world");
  4. //找出列表中,长度为5的第一个单词,同时将长度5打印出来.
  5. // list.stream().mapToInt( String::length).filter(length -> length == 5).findFirst().ifPresent(System.out::println);
  6. list.stream().mapToInt(item -> {
  7. int length = item.length();
  8. System.out.println(item);
  9. return length;
  10. }).filter(length -> length == 5).findFirst().ifPresent(System.out::println);
  11. }
  12. }

结果集:

为什么打印的时候只打印了1个?

原因:容器里面存放的是对每一个容器的操作.

当对流进行迭代,处理的时候,会拿着容器的操作,会逐个的运用到值上.这

如果不满足过滤规则,则还会发生短路运算操作.这是原因之二.只要找到符合条件的,后面就都不会运行.

如:没有满足的规则,则会进行全部执行完.所以就会出现如下结果:

案例:找出集合中所有的单词,并去重.(flatMap方法的应用)

  1. public class StreamTest11 {
  2. public static void main(String[] args) {
  3. //找出集合中所有的单词,并去重.
  4. List<String> list = Arrays.asList("hello world", "hello welcome", "hello", "hello world hello", "hello world welcome");
  5. //要输出: hello world welcome.
  6. // list.stream().map(item -> item.split(" ")).distinct().collect(Collectors.toList()); //不对
  7. List<String> collect = list.stream().map(item -> item.split(" ")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());
  8. collect.forEach(System.out::println);
  9. }
  10. }

案例:将两个集合组合起来, 打招呼-人名(flatMap的应用)

  1. public class StreamTest12 {
  2. public static void main(String[] args) {
  3. //将两个集合组合起来, 打招呼-人名
  4. List<String> list1 = Arrays.asList("Hi", "Hello", "你好");
  5. List<String> list2 = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
  6. // list1.stream().map(item->item.concat(list2.stream().map()))
  7. List<String> collect = list1.stream().flatMap(item -> list2.stream().map(item2 ->item+ " " + item2)).collect(Collectors.toList());
  8. collect.forEach(System.out::println);
  9. }
  10. }

分组和分区

如SQL中的group by.

  1. select * from studeny group by name;

Result:Map<String,List>

传统的实现思路:

  1. 循环列表
  2. 取出学生的名字
  3. 检查map中是否存在该名字,不存在则直接添加到该map;存在则将map中的List对象取出来,然后将该Student对象添加到List中.
  4. 返回Map对象

通过流的方式来实现分组(groupingby()方法)


  1. public class StreamTest13 {
  2. public static void main(String[] args) {
  3. Student student1 = new Student("zhangsan", 100, 20);
  4. Student student2 = new Student("lisi", 90, 20);
  5. Student student3 = new Student("wangwu", 90, 30);
  6. Student student4 = new Student("zhaoliu", 80, 40);
  7. List<Student> students = Arrays.asList(student1, student2, student3, student4);
  8. //Map<String, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getName));
  9. // System.out.println(collect);
  10. Map<Integer, List<Student>> collect = students.stream().collect(Collectors.groupingBy(Student::getScore));
  11. System.out.println(collect);
  12. }
  13. }

这种SQL如何用流来实现?

  1. select name,count(*) from student group by name;

很容易:

  1. Map<String, Long> collect = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.counting()));
  2. System.out.println(collect);

先实现名字的分组,然后再取组内的平均值如何用流实现?

  1. Map<String, Double> collect = students.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore)));
  2. System.out.println(collect);

以上所写的都是关于分组的概念.

分组:group by

分区:partition by

分区

分区是分组的特例,比如Boolean,只有true和false. 上述案例,比如以90分为分界点,分区

  1. Map<Boolean, List<Student>> collect = students.stream().collect(Collectors.partitioningBy(student -> student.getScore() > 90));
  2. System.out.println(collect);
  1. collect.get(true);//获取ture对应的值
  2. collect.get(false);//获取false对应的值

JAVA8学习——从使用角度深入Stream流(学习过程)的更多相关文章

  1. JAVA8学习——从源码角度深入Stream流(学习过程)

    从源代码深入Stream / 学习的时候,官方文档是最重要的. 及其重要的内容我们不仅要知道stream用,要知道为什么这么用,还要知道底层是怎么去实现的. --个人注释:从此看出,虽然新的jdk版本 ...

  2. Java8学习笔记(五)--Stream API详解[转]

    为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 ...

  3. 深度分析:java8的新特性lambda和stream流,看完你学会了吗?

    1. lambda表达式 1.1 什么是lambda 以java为例,可以对一个java变量赋一个值,比如int a = 1,而对于一个方法,一块代码也是赋予给一个变量的,对于这块代码,或者说被赋给变 ...

  4. Java8新特性之方法引用&Stream流

    Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...

  5. java8学习之Lambda表达式深入与流初步

    Lambda表达式深入: 在上一次[http://www.cnblogs.com/webor2006/p/8135873.html]中介绍Lambda表达式的作用时,其中说到这点: 如标红处所说,既然 ...

  6. Java8学习笔记目录

    Java8学习笔记(一)--Lambda表达式 Java8学习笔记(二)--三个预定义函数接口 Java8学习笔记(三)--方法引入 Java8学习笔记(四)--接口增强 Java8学习笔记(五)-- ...

  7. Java8学习(4)-Stream流

    Stream和Collection的区别是什么 流和集合的区别是什么? 粗略地说, 集合和流之间的差异就在于什么时候进行计算.集合是一个内存中的数据结构,它包含数据结构中目前所有的值--集合中的每个元 ...

  8. java8 Stream的实现原理 (从零开始实现一个stream流)

    1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2  ...

  9. Java8 Stream流

    第三章 Stream流 <Java8 Stream编码实战>的代码全部在https://github.com/yu-linfeng/BlogRepositories/tree/master ...

随机推荐

  1. css中两种居中方式text-align:center和margin:0 auto 的使用场景

    关于使用text-align:center和margin:0 auto 两种居中方式的比较 前言:最近由于要学习后端,需要提前学习一部分前端知识,补了补css知识,发现狂神在讲这一部分讲的不是特别清楚 ...

  2. 基于Appium的APP自动化测试基础--美团APP的实例

    转:https://blog.csdn.net/Tigerdong1/article/details/80159156 前段时间用一种流行语言,一个主流工具,一个实用框架,写了一个美团app自动化测试 ...

  3. Hybrid App(混合开发) 移动端开发调试

    1.下载项目,npm install安装依赖 本地运行 npm run dev(根据具体packjson配 置而定) 2.局域网访问:http://172.20.9.35:8080/ 3.手机端访问: ...

  4. Java 异步 I/O

    Java 中的异步 I/O 简称 AIO, A 即 Asynchronous.AIO 在 JDK1.7 时引入,基于操作系统提供的异步 I/O 通信模型,封装了一些进行异步 I/O 操作的 API. ...

  5. django_url配置

    前言 我们在浏览器访问一个网页是通过url地址去访问的,django管理url配置是在urls.py文件.当一个页面数据很多时候,通过会有翻页的情况,那么页数是不固定的,如:page=1.也就是url ...

  6. pytest文档5-参数化parametrize

    pytest.mark.parametrize装饰器可以实现测试用例参数化. parametrizing 1.这里是一个实现检查一定的输入和期望输出测试功能的典型例子 # content of tes ...

  7. 集合框架-Map集合练习-记录字母次数思路及代码

    1 package cn.itcast.p10.map.test; 2 3 import java.util.Iterator; 4 import java.util.Map; 5 import ja ...

  8. Let's Encrypt 证书 wget 报错

    最近发现一个奇怪的问题,网站使用 let's encrypt 的免费证书,浏览器访问时正常,但是wget的时候报错.报错信息如下: wget https://www.example.com --202 ...

  9. Asp-Net-Core学习笔记:身份认证入门

    前言 过年前我又来更新了~ 我就说了最近不是在偷懒吧,其实这段时间还是有积累一些东西的,不过还没去整理-- 所以只能发以前没写完的一些笔记出来 就当做是温习一下啦 PS:之前说的红包封面我还没搞,得抓 ...

  10. Hello,find!

    Hello,find! 前言 余幼时,每至除夕,爆竹声声,如雷贯耳,醒于梦中,便知春节将至.与兄长二三人,执摔炮六七只, 玩耍于廷中.出门罢,廷中白雪覆于黄土之上.二三尺之外,见犬窝,余之爱犬趴于其中 ...