十五、Stream 流操作

流的简单使用
        Integer[] nums = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(nums);

        // 过滤掉 不是 > 2 的,并统计元素数量
        long count = list.stream().filter(n -> n > 2).count();
        System.out.println(count);
        // 上面过程的并行版
        count = list.parallelStream().filter(n -> n > 2).count();
        System.out.println(count);

流的获取
        Integer[] nums = {1, 2, 3, 4, 5};
        List<Integer> list = Arrays.asList(nums);


        // 1、将 Collection 转为 Stream
        // Collection 接口下的 stream 和 parallelStream 方法 可以返回一个 Stream
        Stream<Integer> stream = list.stream();
        // 并行流,后续操作会以并行方式运行
        Stream<Integer> stream1 = list.parallelStream();


        // 2、Steam.of 方法直接传入对象
        // public static<T> Stream<T> of(T... values)
        // 这里如果传入 基本类型的数组的话, int[] ,泛型对不上,nums 会作为 int[] 类型的一个参数被传递
        Stream<Integer> stream2 = Stream.of(nums);
        stream2.forEach(System.out::println); // [I@11096447
        stream2 = Stream.of(1, 2, 3, 6);

        // 产生一个不包含任何元素的流
        Stream.empty();

        // 产生一个无限长度的流,每个结果由参数中的函数过程生成的返回值
        stream2 = Stream.generate(() -> 1);
        // 如产生一个随机数流
        Random random = new Random();
        stream2 = Stream.generate(random::nextInt);
        stream2 = Stream.generate(() -> random.nextInt(100));

        // 产生一个无限长度的流,设置一个初始结果,每次生成的结果会作为下一次生产过程的参数
        // 如产生一个自增流
        stream2 = Stream.iterate(0, n -> ++n);
        Stream<BigInteger> stream22 = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));


        // 3、将数组转换为 Stream
        // Arrays.stream 方法
        // 产生一个 从起始下标,到结束下标(不包括结束下标)的流
        Stream<Integer> stream3 = Arrays.stream(nums, 0, 4);


        // 还有其他很多 类都自带产生流的 方法 如 Pattern.splitAsStream 、 Files.lines
流的转换

将流做一些处理并返回一个流

        Integer[] nums = {1, 2, 3, 4, 5};
        Stream<Integer> stream = Arrays.stream(nums);


        // 流操作一次后不能再次操作,这里不用 stream 重新接收新的流的话,后面再次操作会报错错
        // stream.filter(n -> n > 2);

        // 过滤,将返回 true 的结果作为新的流返回
        stream=stream.filter(n -> n > 2);

        // 对每个元素进行操作,将传入的函数应用到每个元素上,函数的返回值作为每个元素的新值
        stream=stream.map(n->n*10);


        Stream<String> stringStream=Stream.of("what","is","your","name");
        // flatMap 将流里的每个元素都转换为流,再将这些流 合成为一个流返回
        stringStream.flatMap(w->{
            char[] chars = w.toCharArray();
            List<Character> list=new ArrayList<>();
            for(Character c:chars)list.add(c);
            return list.stream();
        }).forEach(System.out::print);
抽取子流和连接流
        Integer[] nums = {1, 2, 3, 4, 5 ,6 ,7, 8};
        Stream<Integer> stream = Arrays.stream(nums);


        // 截取流的 前 n 个元素,作为新的流返回
        stream=stream.limit(4);  // 1 2 3 4

        // 丢弃前 n 个元素,将剩余的元素作为新流返回
        stream=stream.skip(2);  // 3 4      1234跳过前两个剩下34

        // 生成一个 有 100 个随机数的流
        Stream.generate(Math::random).limit(1000);


        Stream<Integer> stream2 = Arrays.stream(nums);
        Stream<Integer> stream3 = Arrays.stream(nums);

        // 拼接两个流 ,第一个流不应该是无限的,否则第二个流始终用不到,没有意义
        stream=Stream.concat(stream2,stream3);  // 1.2.3···8.1.2.3···8


        // 去重,返回一个去掉重复元素的流
        stream.distinct();

        stream.forEach(System.out::println);
流的转换 2
        // 流排序
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 调用 sorted 方法获得一个经过排序的流
        stream=stream.sorted();

        // 传入 比较器  ,传入了一个逆序的比较器,做逆序
        stream=stream.sorted(Comparator.comparing(n->(Integer)n).reversed());



        // 返回一个流,并且当这个流取出元素的时候,会先执行这里的函数过程
        stream=stream.peek(e-> System.out.print("本次使用的元素是"+e+"---"));

        // 这里 调用 forEach 会取出元素,每次取出元素会先输出 本次使用的元素是X 在执行这里的过程
        // 如果没有调用forEach ,也就是没有取出元素,你会发现 peek 方法中的函数过程并不会执行
        stream.forEach(System.out::print); // 本次使用的元素是9---9本次使用的元素是8---8本次使用的元素是7---7 ···

简单约简 ,终结流的操作,将流转为非流值
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 获取流元素数
        long counts=stream.count();


        stream = Arrays.stream(nums);
        // 获取流元素中最大的 (min方法与之用法相同)
        Optional<Integer> max = stream.max(Integer::compare);
        // 获得实际值,参数是当获取不到的时候的默认值
        Integer iMax = max.orElse(0);



        stream = Arrays.stream(nums).filter(n->n>6);
        // 返回第一个匹配的元素
        Optional<Integer> first = stream.findFirst();


        stream = Arrays.stream(nums).filter(n->n>6);
        // 返回任何一个匹配元素,在并行处理流的时候比较有效
        Optional<Integer> any = stream.findAny();



        boolean b;
        // 是否全部匹配
        b=Arrays.stream(nums).allMatch(n->n>1); // false
        // 是否存在匹配
        b=Arrays.stream(nums).anyMatch(n->n>2); // true
        // 是否没有匹配
        b=Arrays.stream(nums).noneMatch(n->n>10);// true
        System.out.println(b);

Optional 类型
        Long[] nums = {6L,3L,1L,4L,5L,3L,9L,7L,6L,4L,2L,1L,8L};
        Stream<Long> stream = Arrays.stream(nums);

        // stream 的一些方法会返回一个包含结果的 Optional 对象
        Optional<Long> first = stream.filter(n -> n > 6L).findFirst();

        Long l;
        // 获得Optional中的值 ,如果没有则返回 传入的 替代物
        l=first.orElse(0L);

        // 当没有值的时候 还可以调用代码获取替代值,(只有在需要替代值的时候才会调用代码)
        l=first.orElseGet(()-> new Date().getTime());

        // 当没有值的时候 抛出异常
        //l= first.orElseThrow(Exception::new);

        // 只有当值存在的时候 调用
        first.ifPresent(System.out::println);

        // 将结果值处理并包装返回
        // 如果 first 有值,返回包装处理结果的 Optional ,
        // 如果 first 没有值,返回空的 Optional(是没有值的一个 Optional 对象,不是返回 null)
        Optional<Integer> aBoolean = first.map((n) -> n==9?1:2);

        // 返回 Optional 中的值,没有值的时候会抛异常,不推荐用
        first.get();
        // 返回一个 boolean 表示是否有值
        first.isPresent();


        /// 创建 Optional 的方法
        // 产生一个 Optional 对象,如果 传入的是 null 会抛异常
        Optional<Integer> integer = Optional.of(2);

        // 产生一个 Optional 对象,如果传入的是 null 会返回一个没有值的 Optional 对象
        Optional<Object> o = Optional.ofNullable(null);

        // 产生一个没有值的 Optional 对象
        Optional<Object> empty = Optional.empty();

        // 将值 处理,并包装成 Optional 并返回
        Optional<String> s = first.flatMap(m -> Optional.ofNullable(m.toString()));
收集结果-流转集合
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 获取 Iterator 来遍历流中的元素
        Iterator<Integer> iterator = stream.iterator();

        // 遍历每个元素并应用函数
        // 在并行流上是没有顺序的
        Arrays.stream(nums).parallel().forEach(System.out::print); // 9674421358316   9674331126458  9673216445138

        // 会按顺序遍历,但是会导致并行处理的性能优势基本丧失
        Arrays.stream(nums).parallel().forEachOrdered(System.out::print);

        // 获得由流元素组成的数组
        Object[] objects = Arrays.stream(nums).toArray();
        // 传入数组构造器,来获得正确的数组类型
        Integer[] integers = Arrays.stream(nums).toArray(Integer[]::new);

        // 获取流元素组成的 集合,可以传入 集合的构造器
        TreeSet<Integer> collect2 = Arrays.stream(nums).collect(Collectors.toCollection(TreeSet::new));
        // 获取流元素组成的 List
        List<Integer> collect = Arrays.stream(nums).collect(Collectors.toList());
        // 获取流元素组成的 Set
        Set<Integer> collect1 = Arrays.stream(nums).collect(Collectors.toSet());

        // 产生一个整型的对象收集器(收集过程中可以处理元素),利用收集器可以获得 平均、 最大、最小、总和、结果个数
        // 有对应的 long double 版本
        IntSummaryStatistics summaryStatistics = Arrays.stream(nums).collect(Collectors.summarizingInt(n -> n));
        summaryStatistics.getAverage();
        summaryStatistics.getCount();
        summaryStatistics.getMax();
        summaryStatistics.getMin();
        summaryStatistics.getSum();



        String[] words = {"zs","ls","ww"};
        Stream<String> wordsStream = Arrays.stream(words);

        // 可以将 流中的字符串拼接
        String collect3 = wordsStream.collect(Collectors.joining());
        // 设置分隔符
        wordsStream.collect(Collectors.joining(","));
        // 设置分隔符 ,前缀,后缀
        wordsStream.collect(Collectors.joining(",","^","$"));

收集到映射表中 Steam 转 Map
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 流转为 Map ,第一参数是 map 键的获取过程,第二个参数是 map 值的获取过程 ,如果键冲突会抛异常
        //Map<Integer, String> map = stream.collect(Collectors.toMap(n -> n, n -> n.toString()));

        // 第三个参数,指明当键冲突的时候,可以如何处理旧值(原来存在的值)和新值,作为此键对应的值
        Map<Integer, String> map2 = Arrays.stream(nums).collect(Collectors.toMap(n -> n, n -> n.toString(),(oldValue,newValue)->oldValue));

        // 转并发线程安全的 map
        // ConcurrentMap<Integer, String> concurrentMap = Arrays.stream(nums).collect(Collectors.toConcurrentMap(n -> n, n -> n.toString()));


        // 将元素分组,每组保存 到 list 中
        Map<String, List<Integer>> collect = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n>2?"大于2":"小于等于2"));
        System.out.println(collect); // {大于2=[6, 3, 4, 5, 3, 7, 9, 6, 4, 8], 小于等于2=[1, 2, 1]}
        // 取得并发线程安全 map 版
        Map<String, List<Integer>> collect2 = Arrays.stream(nums).collect(Collectors.groupingByConcurrent(n -> n>2?"大于2":"小于等于2"));
        System.out.println(collect2); // {大于2=[6, 3, 4, 5, 3, 7, 9, 6, 4, 8], 小于等于2=[1, 2, 1]}

        // 如果只分两组,可以使用以 boolean 为键的版本,其效率要 比 groupingBy 高
        Map<Boolean, List<Integer>> collect1 = Arrays.stream(nums).collect(Collectors.partitioningBy(n -> n > 2 ? true : false));
        System.out.println(collect1); // {false=[1, 2, 1], true=[6, 3, 4, 5, 3, 7, 9, 6, 4, 8]}
下游收集器, groupingBy 的后续

收集到 Map, groupingBy 的扩展,可对收集到的 list 做进一步转换


        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // toSet() :对 收集到的 map list 型 值,转换成 set
        Map<String, Set<Integer>> collect = stream.collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于等于2", Collectors.toSet()));
        System.out.println(collect); // {大于2=[3, 4, 5, 6, 7, 8, 9], 小于等于2=[1, 2]}

        // summingInt: 对 收集到的 map 各个list 型值 ,分别求各个list中的元素 和 , 这个例子 就是对 大于 2和 小于2 的分别求和 返回到一个 map
        // summingInt  还有对应 double long 版本
        // a->a  这里是将 list 中的数据转成 int 型的过程,因为本来list中的元素就是 int 型,所以不做任何处理返回, 也可以使用 Function.identity() 同样的效果
        Map<String, Integer> collect1 = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2", Collectors.summingInt(a -> a)));
        System.out.println(collect1); // {小于2=4, 大于2=55}

        // counting :对 收集到的 map 各个list 型值 ,分别求各个list中的元素 数目
        Map<String, Long> collect2 = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2", Collectors.counting()));
        System.out.println(collect2); // {小于2=3, 大于2=10}

        // 对 收集到的 map 各个list 型值 ,分别求各个list中的元素 最大元素 对应还有 minBy
        Map<String, Optional<Integer>> collect3 = Arrays.stream(nums).collect(Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2", Collectors.maxBy(Integer::compare)));
        System.out.println(collect3); // {小于2=Optional[2], 大于2=Optional[9]}


        // mapping
        // 对 收集到的 map 各个list 型值 ,分别对各个list中的元素做一层处理后,再交给后续的 Collectors
        Map<String, List<String>> collect4 = Arrays.stream(nums).collect(
                Collectors.groupingBy(n -> n > 2 ? "大于2" : "小于2",
                        Collectors.mapping(n -> "c"+n.toString(),
                                Collectors.mapping(n->n,Collectors.toList()))));
        System.out.println(collect4); // {小于2=[c1, c2, c1], 大于2=[c6, c3, c4, c5, c3, c7, c9, c6, c4, c8]}

约简操作 Stream.reduce
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // reduce :和 ConCurrentHashMap.reduce 类似
        // 将前一次的处理结果作为参数,参与下一次处理
        // 即  result = v1 op v2 op v3 op v4 ····  op 必须满足 ,x op y op z = x op ( y op z )
        // op 例 : 求和,乘积,字串拼接,max,min,并集,交集···
        Optional<Integer> reduce = stream.reduce((x, y) -> x + y);

        // 第一个参数为 幺元 e , 幺元满足  e op v = v
        // 当流为 null ,会返回 幺元
        Integer reduce1 = Arrays.stream(nums).reduce(0, (x, y) -> x + y);
        System.out.println(reduce1);

        // 第一个参数为 幺元
        // 第二个参数为 op
        // 第三个参数为,多线程将 不同分组的结果 合并的方式(内部使用了 fork-join框架)
        String reduce2 = Arrays.stream(nums).reduce("x", (x, y) -> x +"-"+ y.toString(), (x, y) -> x +"*" +y);
        System.out.println(reduce2); // x-6-3-1-4-5-3-7-9-6-4-2-1-8

        String reduce4 = Arrays.stream(nums).parallel().reduce("x", (x, y) -> x +"-"+ y.toString(), (x, y) -> x +"*" +y);
        System.out.println(reduce4); // x-6*x-3*x-1*x-4*x-5*x-3*x-7*x-9*x-6*x-4*x-2*x-1*x-8


        Integer reduce3 = Arrays.stream(new String[]{"zs", "ls", "zll"}).parallel().reduce(0, (a, b) -> a + b.length(), (a, b) -> +a + b);
        System.out.println(reduce3); // 7

基本类型流
        // 将基本类型包装成对象使用,效率会降低,可以使用基本类型专属的流,以int为例

        int[] nums={1,6,8,9,7,6};
        IntStream intStream = IntStream.of(1, 3, 6, 4, 9, 8);
        IntStream stream = Arrays.stream(nums);

        OptionalInt first = stream.findFirst();
        OptionalInt max = stream.max();

        IntSummaryStatistics intSummaryStatistics = stream.summaryStatistics();
        intSummaryStatistics.getSum();
        intSummaryStatistics.getMax();
        intSummaryStatistics.getAverage();
        intSummaryStatistics.getCount();

        // 产生步长为 1 的流 ,int 和 lang 专有 ,包括 10
        IntStream range = IntStream.range(1, 10);
        range.forEach(System.out::print);
        // 不包括 10
        IntStream range2 = IntStream.rangeClosed(1, 10);
        range2.forEach(System.out::print);

        int[] ints = stream.toArray();


        // 产生包装器对象流
        Stream<Integer> boxed = stream.boxed();
并行流的获取
        Integer[] nums = {6,3,1,4,5,3,7,9,6,4,2,1,8};
        Stream<Integer> stream = Arrays.stream(nums);

        // 产生一个 与当前 stream 元素相同的 并行流
        Stream<Integer> parallel = stream.parallel();
        // 产生一个 与当前 stream 元素相同 的无序流
        stream.unordered();

        Collection collection=new ArrayList();

        collection.stream();
        // 根据当前集合产生一个 并行流
        collection.parallelStream();
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341