Java8用了这么久了,Stream 流用法及语法你都知道吗?

1.简介

Stream流 最全的用法
Stream 能用来干什么?用来处理集合,通过 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询,Stream API 提供了一种高效且易于使用的处理数据的方式

为什么用Java 8 Stream ?因为 操作简单
为什么操作简单?因为 Lambda 表达式,它极大的提高了编程效率和程序可读性
怎么操作流? 首先你的有个数据源(数组、集合),操作会产生新的流对象,原来的流对象不会改变
流用法有结束操作,这种代码不是你写了一个方法就执行一个方法,而是最后触发结束操作的时候才统一执行的,collect、foreach 方法就是一种结束方法,详情看代码及结果参考 2.映射map、flatMap用法 部分

2.具体用法

2.1 创建流

 // 集合创建流
        List<String> list = new ArrayList<>();
        // 获取一个顺序流
        Stream<String> listStream = list.stream();
        // 获取一个并行流
        Stream<String> parallelListStream = list.parallelStream();

        // 数组创建流
        Integer[] nums = new Integer[] { 1, 2, 3, 4, 5 };
        Stream<Integer> arrStream = Arrays.stream(nums);
        arrStream.forEach(System.out::println);// 1 2 3 4 5

        // 静态方法of创建流
        Stream<Integer> ofStream = Stream.of(1, 2, 3, 4, 5);
        ofStream.forEach(System.out::println);// 1 2 3 4 5

        // 静态方法iterate 创建流
        Stream<Integer> iterateStream = Stream.iterate(1, (x) -> x + 10).limit(4);
        iterateStream.forEach(System.out::println); // 1 11 21 31

        // 静态方法generate 创建流
        Stream<Double> generateStream = Stream.generate(Math::random).limit(2);
        generateStream.forEach(System.out::println);

2.2 操作流

1.过滤

filter:过滤流中的某些元素(可以做一些基本的判空、替换、判断逻辑操作)
limit(n):获取n个元素,结果获取几个元素
skip(n):跳过n元素,配合limit(n)可实现分页
distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素

     //filter 判空
     Stream<Integer> notNullStreamObj = Stream.of(1, 2, null, 4, 5, 6, 7, null, 2);
     Stream<Integer> notNullStream = notNullStreamObj.filter(i -> (null != i));
     notNullStream.forEach(System.out::println);//1 2 4 5 6 7 2

     //filter 逻辑判断
     Stream<Integer> logicStreamObj = Stream.of(1, 2, null, 4, 5, 6, 7, null, 2);
     Stream<Integer> logicStream = logicStreamObj.filter(i -> (i != null && i > 5));
     logicStream.forEach(System.out::println); // 6 7

     //filter 替换
     Stream<String> strStreamObj = Stream.of("aa", "ab", null, "ac", "bd", "ee");
     Stream<String> strStream = strStreamObj.filter(str -> (null != str && str.contains("a")));
     strStream.forEach(System.out::println); // aa ab ac


     //skip 跳过
     Stream<String> skipStreamObj = Stream.of("aa", "ab", null, "ac", "bd", "ee");
     Stream<String> skipStream = skipStreamObj.skip(2);
     skipStream.forEach(System.out::println); // null ac bd ee

     //distinct 去重
     Stream<String> disStreamObj = Stream.of("aa", "ab", null, "ac", "aa", "ab", null, "ee");
     Stream<String> disStream = disStreamObj.distinct();
     disStream.forEach(System.out::println); // aa ab null ac ee

2.映射

map:接收一个函数作为参数,该函数会被应用到每个元素上,映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
peek:这个操作很骚,类似map只不过map 是Func函数,提供返回值,而peer是取出元素,Consumer表达式设值,我个人觉得没啥区别呢,官方文档提示:该方法主要用于调试,做一些消耗这个对象但不修改它的东西,没啥事不要用
很想问一下 这俩map、flatMap 区别 ,细品,你细品,你细细品
map是将每个元素 映射成一个新元素,除非你过滤了,否则不会改变元素个数
flatMap是将原流中的每个值都变成另一个流,然后把流合并串起来,必须有返回值,拼装成新的流

     //map 把包含a的元素,替换成| 注意,注意, 元素还是一个整体,对每个元素
     Stream<String> mapStreamObj = Stream.of("a,b,c", "a,e,f", "g,h,i");
     Stream<String> mapStream = mapStreamObj.map(str -> str.replaceAll(",", "|"));
     mapStream.forEach(System.out::println); // a|b|c a|e|f h|i|j

     //flatMap 可以把元素 切分后,再按照新元素组成新的字符串
     Stream<String> flatMapStreamObj = Stream.of("a,b,c", "a,e,f", "g,h,i");
     Stream<String> flatMapStream = flatMapStreamObj.flatMap(str -> {
         String[] arr = str.split(",");
         Stream<String> result = Arrays.stream(arr);
         return result;
     });
     flatMapStream.forEach(System.out::println); //a b c d e f g h i


     System.out.println("1===========");
     Stream<String> peekStreamObj = Stream.of("a,b,c", "a,e,f", "g,h,i");
     Stream<String> peekStream = peekStreamObj.peek(e -> System.out.println("Filtered value: " + e))
             .map(String::toUpperCase)
             .peek(e -> System.out.println("Mapped value: " + e));
     System.out.println("2=========== peek代码结束,但是日志没打印");
     Set<String> stringSet = peekStream.collect(Collectors.toSet());
     System.out.println("3=========== collect结束操作,代码日志打印");
     stringSet.forEach(System.out::println);

map执行结果

//看下执行结果,说明 collect才是结束操作,代码结束,但是并不是真正结束
1===========
2=========== peek代码结束,但是日志没打印
Filtered value: a,b,c
Mapped value: A,B,C
Filtered value: a,e,f
Mapped value: A,E,F
Filtered value: g,h,i
Mapped value: G,H,I
3=========== collect结束操作,代码日志打印
A,B,C
A,E,F
G,H,I

3.排序

sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):定制排序,自定义Comparator排序器
先构建一个User类

 public static class User {
       private String name;
       private Integer age;

       public User(String name, Integer age) {
           this.name = name;
           this.age = age;
       }

       public String getName() {
           return name;
       }

       public void setName(String name) {
           this.name = name;
       }

       public Integer getAge() {
           return age;
       }

       public void setAge(Integer age) {
           this.age = age;
       }

       @Override
       public String toString() {
           return "User{" +
                   "name='" + name + '\'' +
                   ", age=" + age +
                   '}';
       }
   }

然后 看下sort用法

//按字母排序
       Stream<String> sortStreamObj = Stream.of("a,e,f", "a,d,c", "a,b,i");
       Stream<String> sortStream = sortStreamObj.sorted();
       sortStream.forEach(System.out::println); //abi adc aef


       User u1 = new User("bb", 1);
       User u2 = new User("aa", 2);
       User u3 = new User("cc", 3);
       User u4 = new User("aa", 4);

       Set<User> userSet = Sets.newHashSet(u1, u2, u3, u4);
       Stream<User> userStream = userSet.stream().sorted(
               (obj1, obj2) -> {
                   if (obj1.getName().equals(obj2.getName())) {
                       //name相等 按age
                       return obj1.getAge() - obj2.getAge();
                   }
                   return obj1.getName().compareTo(obj2.getName());
               }
       );

       userStream.forEach(System.out::println);// u2 u4 u1 u3

sort 执行结果

a,b,i
a,d,c
a,e,f
User{name='aa', age=2}
User{name='aa', age=4}
User{name='bb', age=1}
User{name='cc', age=3}

4.流匹配

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值

        List<Integer> numLists = Arrays.asList(3, 4, 5, 6, 10);
        // 全部匹配 - true
        boolean allMatch1 = numLists.stream().allMatch(e -> e > 2); //true
        System.out.println("allMatch1:" + allMatch1);

        // 全部匹配 - true
        boolean allMatch2 = numLists.stream().allMatch(e -> e > 5); //false
        System.out.println("allMatch2:" + allMatch2);

        // 全部都不符合 - true
        boolean noneMatch = numLists.stream().noneMatch(e -> e > 20); //true
        System.out.println("noneMatch:" + noneMatch);

        // 任一元素符合 - true
        boolean anyMatch = numLists.stream().anyMatch(e -> e > 4);  //true
        System.out.println("anyMatch:" + anyMatch);

        //返回第一个
        Integer findFirst = numLists.stream().findFirst().get(); //3
        System.out.println("findFirst:" + findFirst);

        //返回任一个
        Integer findAny = numLists.stream().findAny().get(); 
        System.out.println("findAny:" + findAny);

        //返回 count
        long count = numLists.stream().count(); //5
        System.out.println("count:" + count);

        //返回max
        Integer max = numLists.stream().max(Integer::compareTo).get(); //10
        System.out.println("max:" + max);

        //返回min
        Integer min = numLists.stream().min(Integer::compareTo).get();//3
        System.out.println("min:" + min);

匹配执行结果

allMatch1:true
allMatch2:false
noneMatch:true
anyMatch:true
findFirst:3
findAny:3
count:5
max:10
min:3

5.组合操作

Reduce 就是组合操作
Reduce(BinaryOperator accumulator) 没有起始值,按照运算规则进行运算操作
解释:第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素,按照函数进行操作;
第二次执行时,第一个参数为第一次函数执行操作的结果,第二个参数为流中的第三个元素;往下依次类推,返回Optinal 通过get()方法获取结果
Reduce(T identity, BinaryOperator accumulator)含有初始值,第二个是第一个的变形,跟第一个方法对比,不同的是此次这个会接受一个identity参数,用来指定Stream循环的初始值。如果Stream为空,就直接返回该值,特殊:该方法不会返回 Optional

Optional sumResult = Stream.of(1, 2, 3, 4)
                .reduce((sum, item) -> {
                    System.out.println("sum : " + sum);
                    sum += item;
                    System.out.println("item: " + item);
                    System.out.println("sum+ : " + sum);
                    System.out.println("-----——---");
                    return sum;
                });
        System.out.println("========sumResult: " + sumResult.get());


        Integer sumDefineResult = Stream.of(1, 2, 3, 4)
                .reduce(100, (sum, item) -> {
                    System.out.println("sum : " + sum);
                    sum += item;
                    System.out.println("item: " + item);
                    System.out.println("sum+ : " + sum);
                    System.out.println("---——-----");
                    return sum;
                });
        System.out.println("========sumDefineResult: " + sumDefineResult);

reduce 执行结果

//下面是执行结果
//查看执行结果
sum : 1
item: 2
sum+ : 3
-----——---
sum : 3
item: 3
sum+ : 6
-----——---
sum : 6
item: 4
sum+ : 10
-----——---
========sumResult: 10
sum : 100
item: 1
sum+ : 101
---——-----
sum : 101
item: 2
sum+ : 103
---——-----
sum : 103
item: 3
sum+ : 106
---——-----
sum : 106
item: 4
sum+ : 110
---——-----
========sumDefineResult: 110

6. 收集转换操作

这是个最最最最最基本的操作,10个流操作 9个都会使用到当前操作

collect(Collectors.toList()) 转换List
collect(Collectors.toSet()) 转换Set
Collectors.toMap(key, value) 转换Map ,如果key重复,!!!报错
Collectors.joining() join进行拼接
Collectors.groupingBy(key) 以Key为map的 key分组
Collectors.partitioningBy(规则) 以规则分区 比如 >5 ,map key为true,false

        User s1 = new User("aa", 1);
        User s2 = new User("bb", 2);
        User s3 = new User("cc", 3);
        User s4 = new User("dd", 2);
        List<User> list = Arrays.asList(s1, s2, s3, s4);

        //转换list
        List<Integer> ageList = list.stream().map(User::getAge).collect(Collectors.toList()); // [1, 2, 3]
        System.out.println(ageList.toString());

        //转成set
        Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet()); // [1, 2, 3]
        System.out.println(ageSet);

        //转成map,注:key不能相同,否则报错
        Map<String, Integer> userMap = list.stream().collect(Collectors.toMap(User::getName, User::getAge)); // {cc=10, bb=20, aa=10}
        System.out.println(userMap);

        //字符串分隔符连接
        String joinName = list.stream().map(User::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
        System.out.println(joinName);

        //分组
        Map<Integer, List<User>> ageMap = list.stream().collect(Collectors.groupingBy(User::getAge));
        System.out.println(ageMap);

        //多重分组,先根据类型分再根据年龄分
        Map<Integer, Map<Integer, List<User>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getAge)));
        System.out.println(typeAgeMap);

        //分区
        //分成两部分,true 一部分age大于2岁, false 一部分age小于等于2岁
        Map<Boolean, List<User>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 2));
        System.out.println(partMap);

collect 执行结果

[1, 2, 3, 2]
[1, 2, 3]
{dd=2, cc=3, bb=2, aa=1}
(aa,bb,cc,dd)
{1=[User{name='aa', age=1}], 2=[User{name='bb', age=2}, User{name='dd', age=2}], 3=[User{name='cc', age=3}]}
{1={1=[User{name='aa', age=1}]}, 2={2=[User{name='bb', age=2}, User{name='dd', age=2}]}, 3={3=[User{name='cc', age=3}]}}
{false=[User{name='aa', age=1}, User{name='bb', age=2}, User{name='dd', age=2}], true=[User{name='cc', age=3}]}

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

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