java8学习:使用流(1)

简介:

内容来自《 java8实战 》,本篇文章内容均为非盈利,旨为方便自己查询、总结备份、开源分享。如有侵权请告知,马上删除。
书籍购买地址:java8实战

  • 筛选和切片

    • filter方法
    //取出是素菜的菜单
    menu.stream()
          .filter(Dish::isVegetarian)   //代表是否是素食,这里条件并不一定是方法调用,只要的你条件能够返回boolean就可以
          .collect(Collectors.toList());
    • limit方法
    //取出菜单的前三个
    menu.stream().limit(3)              //跟sql中limit一样,不过这个是短路limit,也就是说,它取出了三个后就不再遍历后面的元素了,直接返回
                 .collect(Collectors.toList());
    • distinct方法:去重
    menu.stream()
        .distinct()  //去重操作,根据元素的hashCode和equals方法判断是否相等
        .collect(Collectors.toList());
    • skip方法:跳过指定数量的元素
    //取菜单中第三个到第五个
    menu.stream()
            .skip(2)   //跳过开头两个元素
            .limit(3)  //然后截取第三个到第五个
            .forEach(System.out::println);
    //如上可以看出skip和limit是互补的,因为limit没有这样的形式:limit(start,end)
  • 映射

    • 提到这个概念,就是把你stream里的每个元素都应用到自己指定的表达式之上然后返回,就比如
    List<String> list = Arrays.asList("1","2","3");
    list.stream()
            .映射方法(str -> str + "dada")
            .forEach(System.out::println);
    //以上为伪代码
    //说明的意思就是list中的每个元素:1,2,3都会应用到表达式:str + "dada"
    //并返回表达式的值
    //所以结果就是1dada,2dada,3dada
    • map方法
    //这里提到的map方法,完全可以替换上面的伪代码:映射方法,上面代码是传入A类型返回A类型的演示
    //也就是传入字符串1,2,3返回的也是字符串
    //map当然可以传入A类型返回B类型,比如
    List<String> list = Arrays.asList("1","2","3");
    list.stream()
            .map(Integer::parseInt)
            .forEach(System.out::println);
    //上面就是传入String,返回的是int
    //当然也可以是返回字符串的长度
    • 考虑一个场景,把n个单词的里面的字母去重然后输出

      • 就比如["abc","bce"],里面的字母就是["a","b","c","b","c","e"],去重后是:["a","b","c","e"],现在来实现它
      List<String> list = Arrays.asList("abc","bce");
      List<String[]> collect = list.stream()
              .map(str -> str.split(""))
              .distinct()
              .collect(Collectors.toList());
      collect.forEach(System.out::println);
      • 上面的感觉是对的,拆分,然后去重,但是一点需要注意,当lambda把第一个元素"abc"进行映射操作的时候,split方法返回的是一个String数组对象,这也就能解释为什么List的泛型是String数组了,数组跟数组distinct那肯定是去重不成功的。
      • 流的过程是这样的["abc","bce"] -> [{"a","b","c"},{"b","c","e"}],然后拿{"a","b","c"}和{"b","c","e"}去重肯定行不通
      • 现在遇到的问题就是:我们希望把abc和bce切分然后合并成一个流,然后进行去重,我们接着实验
      • Array::stream

        • Array::stream可以接受一个数组并产生一个流,如下
        String[] str = {"abc","bce"};
        Stream<String> stream = Arrays.stream(str);
        stream.forEach(System.out::println);
        //abc
        //bce
        • 我们在上面的split方法返回的就是数组,我们尝试将split返回的数组映射到此方法上,看看能不能合并成一条流
        List<String> list = Arrays.asList("abc","bce");
        List<Stream<String>> collect = list.stream()
                .map(str -> str.split(""))
                .map(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
        //java.util.stream.ReferencePipeline$Head@2aae9190
        //java.util.stream.ReferencePipeline$Head@2f333739
        • 但是并不尽如人意,因为他返回的泛型是Stream,也就是说,Array::stream方法只是把一个数组转换为一个流,流中的元素是String所以这个流就是这样的Stream,这种情况类似上面遇到的返回的是两个数组,现在返回的List中装入的并不是String,而是两条流,流跟流做去重也是行不通的
      • 解决方案

        • 现在遇到的问题就是:他们都是将一个list元素单独转化为一条流或者一个数组,我们需要的是转换为流或者数组之后,再将这些返回的数组或者流接为一个流或者数组,就像接水管一样,让他们连起来
        • 尝试方法:flatmap
        List<String> list = Arrays.asList("abc","bce");
        List<String> collect = list.stream()
                .map(str -> str.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
        collect.forEach(System.out::println);
        //a b c e
        • 如上的代码终于是返回了一个LIst,所以结果也是我们期待的达到了去重的效果,那么它的流的处理过程是怎么样的呢?如下

          • 传入"abc"->切分为["a","b","c"]
          • --->flatMap合并["a","b","c","b","c","e"]->去重->ok
          • 传入"bce"->切分为["b","c","e"]
          • 上面能看懂吗...懒得画图了,第一行和第三行是flatMap前面的split过程,然后是第二行存入flatMap进行合并流
        • flatMap也就是把n个流合并为一个流
      • 小实战

        • 要求两个list:["123"],["456"],然后组成元组:(1,4),(1,5),(1,6),(2,4),(2,5)...
        List<String> first = Arrays.asList("123");
        List<String> second = Arrays.asList("456");
        first.stream()
                .map(str -> str.split(""))
                .forEach(strs -> {
                    Arrays.stream(strs).forEach(f -> {
                        second.stream()
                                .map(sec -> sec.split(""))
                                .forEach(secs -> Arrays.stream(secs).forEach(s -> System.out.println("("+f+","+s+")")));
                    });
                });
        //上面代码是直接输出的,那么下面的就是将元组组合成一个LIst返回了
        List<String> first = Arrays.asList("123");
        List<String> second = Arrays.asList("456");
        List<String> collect = first.stream()
                .map(str -> str.split(""))
                .flatMap(strs ->              //将strs后的表达式返回的stream都合并为一个
                        Arrays.stream(strs).flatMap(f ->
                                second.stream()
                                        .map(sec -> sec.split(""))
                                        .flatMap(secs -> Arrays.stream(secs).map(s -> "(" + f + "," + s + ")")))
                ).collect(Collectors.toList());
        collect.forEach(System.out::println);
        //耐心看.....

查找和匹配

  • anyMatch:至少匹配一个
List<String> list = Arrays.asList("1","2","3","4");
boolean b = list.stream().anyMatch(s -> s.equals("1")); //true
  • allMatch:全部匹配
List<String> list = Arrays.asList("1","2","3","4");
boolean b = list.stream().allMatch(s -> Integer.parseInt(s) < 5); //true
  • noneMatch:全部不匹配
List<String> list = Arrays.asList("1","2","3","4");
boolean b = list.stream().noneMatch(s -> Integer.parseInt(s) > 5); //true
  • 如上的方法都具有短路效果,意思就是主要遇到一个不匹配条件的元素就立刻返回true或者false
  • findAny:找任意一个
  List<String> list = Arrays.asList("1","2","3","4");
  Optional<String> any = list.stream().findAny();
  System.out.println(any.get()); //虽然书上说是任意一个,但是我一直返回的是1
  //自己测试的将元素添加到十个,stream依旧是1,但是使用并行流parallelStream,将随机返回,但是依然是区间比较小
  • Option以后的帖子会说到的,你可以看一下我相关的帖子,只要记住Option是一个值的容器,因为findAny找的可能是个空列表,所以他可能会返回null,然后将返回的这个值包装在Option中,然后调用get方法就会出现返回的值,如果Option中没有值还get那么就会报错,现在只要知道这点就可以了
  • findFirst:找到第一个
List<String> list = Arrays.asList("1","2","3","4");
Optional<String> any = list.stream().findFirst();
System.out.println(any.get()); //1

归约reduce

  • 上面的映射和这词提到的归约可以一起使用,类似hadoop中的mr模型
  • 元素求和
int[] is = {1,2,3,4,5,6,7,8,9};
Arrays.stream(is).reduce(0,Integer::sum);//45  
//以0为起始值,所以如果数组内没有元素,也不至于null,然后依次相加
//0+1=1
//1+2=3
//3+3=6
//6+4=10 ....
int[] is = {1,2,3,4,5,6,7,8,9};
OptionalInt reduce = Arrays.stream(is).reduce(Integer::sum);
System.out.println("reduce = " + reduce.getAsInt());//45
//OptionalInt的出现就是因为它没有初始值进行累加,所以如果数组为空,那么将返回null
//OptionalInt使用方法和Optional一样,只是方法名变了一下,目前只知道这些就好了
  • 元素最大值和最小值
//max
int[] is = {1,2,3,4,5,6,7,8,9};
OptionalInt reduce = Arrays.stream(is).reduce(Integer::max);
System.out.println("reduce = " + reduce.getAsInt()); //9
//求最小改为min方法即可,也可以自己实现
OptionalInt reduce = Arrays.stream(is).reduce((a,b) -> a > b ? a : b );
  • 归约方法的优势和并行化

    • 求和方法:定义一个int变量,然后迭代去加。相比之下reduce将其转换为了内部迭代。而且迭代要去求和并且更新我们的一个int共享变量,这对于并行化来说并不容易实现,如果加入了同步,可能线程切换的开销就已经抵消了并行带来的性能提升。(可变的变量累计器对于并行来说并不好),如上的代码为了实现并行只需要把stream方法改为parallelStream()即可
    int[] is = {1,2,3,4,5,6,7,8,9};
    int sum = 0;    //int共享变量
    //这只是单线程的,如果是多线程的话,为了保证sum的正确肯定要sync。
    //所以这就是说的共享变量并不适合于并行化
    for (int i : is) {
        sum += i;     //更新共享变量
    }
  • 流操作的有状态和无状态

    • 如果你购买了书可以先去看看书的定义,下面是我自己的理解
    int[] is = {1,2,3,4,5,6,7,8,9};
    Arrays.stream(is)
            .filter(i -> i > 3)  //无状态
            .map(i -> i + 1)     //无状态
            .distinct()          //有状态
            .max();              //有状态
    • 对于上面来说,filter和map只是接收一个元素然后过滤映射一个元素,这个元素处理完就交给下面的方法处理了,自己并不保留这个元素,这样的叫做无状态
    • 那distinct和max来说,他不能接收一个处理一个然后再交给下面的方法处理,因为它需要拿到整个元素来去重和去最大值,如果拿到部分他肯定是不能做这些操作的,元素就暂时的保留在了方法中,所以这样的叫做有状态
接下来的东西下一篇讲
目录
相关文章
|
14天前
|
消息中间件 前端开发 Java
java学习路径
【4月更文挑战第9天】java学习路径
17 1
|
1月前
|
安全 Java 程序员
学习Java类加载机制
在Java的世界里,每一个类或者接口,在经历编译器后,都会生成一个个.class文件。
18 0
|
1月前
|
Java 关系型数据库 MySQL
37、一篇文章学习 Java 中的日期相关类(Date 和 Calendar),非常常用
37、一篇文章学习 Java 中的日期相关类(Date 和 Calendar),非常常用
27 0
|
2月前
|
Java 数据库连接 开发工具
正式开始JAVA学习之旅
正式开始JAVA学习之旅
39 0
|
1月前
|
存储 安全 Java
24、使用 Java 官方教程学习:① 类变量和类方法详解;② 深入介绍 main() 方法
24、使用 Java 官方教程学习:① 类变量和类方法详解;② 深入介绍 main() 方法
37 1
|
14天前
|
设计模式 前端开发 安全
Java是一种广泛使用的编程语言,其学习路径可以大致分为以下几个阶段
【4月更文挑战第9天】Java是一种广泛使用的编程语言,其学习路径可以大致分为以下几个阶段
15 1
|
1月前
|
Java 索引
Java中String方法学习总结_kaic
Java中String方法学习总结_kaic
|
4天前
|
JavaScript Java 测试技术
基于Java的驾考自主学习预约平台的设计与实现(源码+lw+部署文档+讲解等)
基于Java的驾考自主学习预约平台的设计与实现(源码+lw+部署文档+讲解等)
15 0
|
5天前
|
JavaScript Java 测试技术
基于Java的精品课程在线学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的精品课程在线学习系统的设计与实现(源码+lw+部署文档+讲解等)
25 1
|
5天前
|
JavaScript Java 测试技术
基于Java的中文学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的中文学习系统的设计与实现(源码+lw+部署文档+讲解等)
20 0