• Java8新特性:stream流应用,丢掉for循环实现复杂遍历功能


    stream流

    stream流是支持数据处理操作的数据源生成的元素序列,这些数据源可以是集合、数组、文件I/O channel等。stream不是一种数据结构,也不会存储数据,并且它支持数据聚合操作,如过滤filter、映射map、去重distinct、匹配match等等。

    stream流操作可以分为,生成stream流、操作stream流(中间操作和终端操作)。

    生成stream

    有两种方式创建stream

    1. stream() − 为集合创建串行流。
    2. parallelStream() − 为集合创建并行流。

    通过集合生成

    集合实例后面直接.stream()就可以非常方便的创建一个stream。比如List、Set、Map集合都可以通过这种方式创建stream。

    List<String> list = Arrays.asList("欢迎阅读", "鳄鱼儿"); 
    list.stream();
    list.parallelStream()
    
    • 1
    • 2
    • 3

    通过数组生成

    使用Arrays.stream()方法生成流,生成流的类型是数值流IntStream。除了IntStreamLongStream DoubleStream

    int[] arr = new int[]{1,2,3};
    IntStream stream = Arrays.stream(arr);
    
    • 1
    • 2

    通过文件生成

    通过Files.lines(path, Charset)生成stream。

    注意:此时生成的流需要需要手动关闭,通过一个BaseStream.close()方法和实现AutoCloseable。一般来说几乎所有的流实例实际上不需要在使用后关闭,只有来源为I/O channel的流才需要如此。

    Stream<String> lines = Files.lines(Paths.get("./test.txt"), Charset.defaultCharset());
    
    • 1

    流的操作

    stream操作类型分为两种,中间操作和终端操作。

    中间操作

    中间操作即代表一个stream后返回的还是stream,其后面仍可以跟随中间操作,比如过滤filter后仍可以接distinct去重。

    filterdistinctsortedlimitskip

    通过以下代码展示filterdistinctsortedlimitskip的用法。

    1. 通过filter过滤掉小于2的元素
    2. 通过distinct去掉重复的元素
    3. 通过sorted对元素从小到大排序
    4. 通过skip忽略掉前2个元素
    5. 再通过limit截取前5个元素
    6. 最后通过forEach进行遍历输出。
    7. 输出结果为:4
      6
      8
      12
      55
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 12, 6, 8, 4, 55, 6, 77, 66, 12);
        list.stream()
                .filter(item -> item > 2)   // 过滤元素 1
                .distinct()                 // 去重 12
                .sorted()                   // 排序
                .skip(2)                    // 跳过流中元素 4 6
                .limit(5)                   // 保留前5个元素
                .forEach(System.out::println);  // 输出
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    mapmapToIntmapToLongmapToDoubleflatMap

    map流映射,即将元素映射成另外一个新的元素,这是一种一对一关系。
    flatMap流转换,即将一个流中的每个值都转换为另一个流,是一种一对多的关系.

    比如参考以下代码

    List stringList = Arrays.asList("欢迎 阅读", "鳄鱼儿 文章");
    List strLen = stringList.stream()
            .map(item -> item.length())
            .collect(Collectors.toList());
    System.out.println(strLen);
    
    List newStrList = stringList.stream()
            .flatMap(item -> Arrays.stream(item.split(" ")))
            .collect(Collectors.toList());
    System.out.println(newStrList);;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出结果分别是

    1. [5, 6],String 转变成了 Integer
    2. [欢迎, 阅读, 鳄鱼儿, 文章],一个流中的每个值都转换为另一个流,最好组合成一个新的stream。

    mapToIntmapToLongmapToDouble分别是返回一个IntStreamLongStreamDoubleStream
    这些类型的stream包括一些sum()max()
    min()方法等,可以用于计算,如下面代码求和示例:

    List<Integer> list = Arrays.asList(1, 12, 6, 8, 4, 55, 6, 77, 66, 12);
    // 转换IntStream
    list.stream().mapToInt(item -> item * 2).forEach(System.out::println); 
    // 对IntStream内元素进行计算,并求和 
    System.out.println(list.stream().mapToInt(item -> item * 2).sum());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    peekforeach

    peekforeach是对元素进行遍历处理的方法。

    区别在于peek是中间操作,foreach是终端操作,

    List list = Arrays.asList(1, 12, 6, 8, 4, 55, 6, 77, 66, 12);
    // 如果没有终端操作,则peek不会执行
    list.stream()
            .peek(System.out::println);
    
    // 有终端操作,先执行peek,再返回count,最后通过println输出
    System.out.println(
            list.stream()
                    .peek(System.out::println)
                    .count()
    );
    // foreach是终端操作
    list.stream()
            .forEach(System.out::println);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    输出结果:

    1. 1 12 66 12
    2. 4
    3. 1 12 66 12

    终端操作

    终端操作即一个stream的终止(关闭),一个stream中只能有一个终端操作。

    allMatchanyMatchnoneMatch

    1. allMatch:匹配所有元素
    2. anyMatch:匹配其中一个元素
    3. noneMatch:全部元素都不匹配,跟allMatch相反
    List<Integer> integerList = Arrays.asList(1, 3);
    
    if (integerList.stream().allMatch(i -> i > 5)) {
        System.out.println("所有元素值都大于5");
    } else {
        System.out.println("并非所有元素值都大于5");
    }
    
    if (integerList.stream().anyMatch(i -> i > 5)) {
        System.out.println("存在值大于5的元素");
    } else {
        System.out.println("不存在值大于5的元素");
    }
    
    if (integerList.stream().noneMatch(i -> i > 5)) {
        System.out.println("元素值都小于5");
    } else {
        System.out.println("元素值不都小于5");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    输出结果:

    1. 并非所有元素值都大于5
    2. 不存在值大于5的元素
    3. 元素值都小于5

    countmaxmin

    1. count: 统计流中元素个数
    2. max: 获取流中最大值
    3. min: 获取流中最小值
    List stringList = Arrays.asList("欢迎 阅读", "鳄鱼儿 文章");
    
    Optional min = stringList.stream()
            .map(String::length)
            .min(Integer::compareTo);
    
    stringList.stream()
            .mapToInt(String::length)
            .min();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    有两种写法,通过传参min(Comparatorcomparator)。或者转换成为IntStream再进行min操作。

    findAnyfindFirst

    1. findAny:从符合条件的元素中,随机查找到一个元素
    2. findFirst:根据条件查找到符合条件的第一个元素
    List<Integer> list = Arrays.asList(1, 12, 66, 12);
    
    System.out.println(list.stream().filter(i -> i > 3).findFirst().orElse(-1));
    System.out.println(list.stream().filter(i -> i > 3).findAny().orElse(-1));
    
    • 1
    • 2
    • 3
    • 4

    结果输出:

    1. 12
    2. 12

    总结

    到此,基础Stream用户就已经说完了,看到这里我们会发现,所以的stream其实都可以用for循环来实现,而我们全篇都没有使用for循环来实现,这些如果用for循环实现,你会发现实现的语句变得复杂了很多,不信的话,你可以试试。

    我们也可以看到stream带来的好处,包括代码更简洁解耦等。

  • 相关阅读:
    产业项目招商活动会议课程报名签到h5小程序pc开源版开发
    企业网络自动化配置
    Flask框架学习:蓝图的使用
    OpenCV+YOLO+IP摄像头实现目标检测
    Java池化技术
    Redis高级篇——Redis的优化
    【Python】先玩个魔术 ,再讲二进制 - 心灵感应魔法
    【论文基本功】【LaTeX】个人常用易忘LaTeX命令
    NVIDIA CUDA Win10安装步骤
    计算机网络入门基础篇——应用层
  • 原文地址:https://blog.csdn.net/Ber_Bai/article/details/128155228