深入了解Java 8 新特性:Stream流的实践应用(一)

news/2024/5/19 15:06:27 标签: java, java8新特性, Stream

阅读建议

嗨,伙计!刷到这篇文章咱们就是有缘人,在阅读这篇文章前我有一些建议:

  1. 本篇文章大概一万多字,预计阅读时间长需要10分钟(不要害怕字数过多,其中有一大部分是示例代码,读起来是比较轻松的)。
  2. 本篇文章兼具实战性和理论性,是一篇质量分数较高的技术干货文章,建议收藏起来,方便时常学习与回顾,温故而知新。
  3. 创作不易,免费的点赞、关注,请走上一走,算是对博主一些鼓励,让我更有动力输出更多的干货内容。

前言

        早就打算写一篇文章来梳理一下Java8的新特性Stream流的相关应用,终于得空来完成这件事了。原先以为这块内容应该不会太多,当我梳理完成以后,发现居然有两万多字。当然,在这两万字中,有一部分是示例代码。考虑到一口读完这两字,对于我的读者小伙伴来说,太不友好了,于是乎我打算,把这些内容拆成两部分:

        第一部分,主要是梳理Stream的核心方法;

        第二部分,主要是梳理Collectors类的核心方法;Collectors类是Stream实践应用中非常重要的一个工具类,读完这两篇文章相信肯定能意识到这一点。

Stream是什么

        Java的Stream是Java 8 引入的一个新特性,它提供了一种简洁、优雅的方式来处理集合数据。Stream允许你将集合中的元素进行过滤、映射、排序等操作,并将结果转换为一个新的集合。并且使用Stream,你可以将一个集合转换为一个流,然后对这个流进行各种操作,最后将结果收集到一个新的集合中。这种处理方式非常适合处理大量数据,因为它允许你在内存中一次只处理一个或一小批数据,而不是一次性加载整个数据集到内存中。因此Streamjava8新增特性中一个非常有用且强大的。它的核心特性:

  1. 声明性:Stream的操作以声明的方式进行,这使得代码更易读、易懂。声明性编程强调的是“做什么”,而不是“如何做”,这使得代码更具可维护性和可扩展性。
  2. 可复合:Stream的操作可以链式地进行,即可以将多个操作链接起来运行。这种可复合的特性使得对数据的处理更简洁、更易于理解。
  3. 可并行:Stream可以并行处理数据,这是为了适应多核机器的时代,提高系统CPU、内存的利用率。并行流是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

这些特性的能够给你带来的好处是:

  1. 提高代码的可读性和可维护性:通过使用声明性和链式操作,可以让代码更加简洁、易于理解和维护。
  2. 提高系统的性能:通过并行流的处理方式,可以充分利用多核CPU的性能,提高系统的处理速度和性能。
  3. 适应大数据时代的需求:随着大数据时代的到来,对大量数据的处理成为一项重要的任务。Stream的并行流处理方式可以满足这种需求,提高数据处理的速度和效率。

Stream的核心方法

Stream#filter

        Stream#filter 方法用于根据指定的条件筛选出 Stream 中的元素。filter 方法接收一个 Predicate 参数,Predicate接口在Java中是一个函数式接口,它只有一个抽象方法,即test(T t),用于接受一个参数并返回一个布尔值。这个接口通常用于定义一个断言(即条件),在编程中可以用于对集合进行过滤或者在函数式编程中作为参数使用

        示例:

        使用Stream#filter()过滤出年龄等于18的学生信息

java">@Test
public void test() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    List<Student> targetList = list.stream().filter(item -> item.getAge() == 18).collect(Collectors.toList());
    for (Student student : targetList) {
        Assert.isTrue(student.getAge() == 18, "单元测试结果与预期不匹配");
    }
}

Stream#map

        Stream#map方法,它用于将 Stream 中的每个元素映射到另一个元素,Stream#map()方法接受一个Function类型的参数,而Function接口是Java 8引入的函数式接口,主要方法是apply(T t),它接受一个参数并返回一个结果。这个方法可以被看作是一个操作或者函数,它对输入进行某种处理并产生输出。

        示例:

        先使用Stream#filter()过滤出年龄大于18的学生信息,再使用Stream#map()方法把过滤到的结果信息映射成为另外一个类型的

java">@Test
public void test2() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    List<Person> targetList = list.stream()
            .filter(item -> item.getAge() > 18
            ).map(item -> new Person(item.getName(), item.getAge())).collect(Collectors.toList());
    Assert.notNull(targetList, "单元测试结果与预期不匹配");
}

Stream#mapToInt

        Stream.mapToInt()是Java8中Stream API 的一部分,mapToInt() 方法接受一个函数接口作为参数,可以用于将 Stream 中的元素映射为 int 类型,当调用 mapToInt() 方法时,Java 会遍历原始 Stream 中的每个元素,并将每个元素传递给指定的函数。然后,Java 会收集这些结果,最后返回返回一个 IntStream,在IntStream 上可以执行一些特殊的操作,如求和、统计、排序、最大值、最小值等

                示例:

  • 把学生信息中的年龄数据过滤到一个数组中
  • 统计所有学生信息的总年龄
  • 统计所有学生信息中的最大年龄
java">@Test
public void test3() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    //把学生信息中的年龄数据过滤到一个数组中
    int[] ageArr = list.stream().mapToInt(item -> item.getAge()).toArray();
    for (int age : ageArr) {
        System.out.println(age);
    }
    //统计所有学生信息的总年龄
    int sum = list.stream().mapToInt(item -> item.getAge()).sum();
    System.out.println(sum);
    //统计所有学生信息中的最大年龄
    Integer maxAge = list.stream().map(item -> item.getAge()).max((v1, v2) -> {
        if (v1 > v2) {
            return 1;
        } else if (v1 < v2) {
            return -1;
        }
        return 0;
    }).get();
    System.out.println(maxAge);
}

Stream#distinct

        Stream#distinct 方法用于去除 Stream 中的重复元素,它会返回由不同元素组成的新 Stream。这个方法实际上调用了 Object.equals(Object o) 方法,默认的行为是比较两个对象的引用是否相等。如果两个对象的引用相等,则认为它们相等;反之则认为它们不相等。因此,在使用 distinct 方法时,如果需要比较的元素是自定义对象,需要在自定义类中重写 equals() 和 hashCode() 方法。

        示例:

        给存在重复学生信息的集合进行去重操作并回收结果

java">@Test
public void test4() {
    //wangwu的信息存在重复
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17), new Student("wangwu", 17));
    List<Student> targetList = list.stream().distinct().collect(Collectors.toList());
    Assert.isTrue(list.size() > targetList.size(), "单元测试结果与预期不匹配");
}

Stream#sorted

        Stream#sorted 方法用于对 Stream 中的元素进行排序。它返回一个包含按指定排序规则排序后元素的新的 Stream,默认情况下,使用自然顺序排序(对于实现了 Comparable 的元素类型)。

        示例

  • 自然排序,默认是升序,这里是首字母升序
  • 自定义排序,lambda表达式定义排序逻辑,这里是降序
java">@Test
public void test5() {
    //自然排序,默认是升序,这里是首字母升序
    List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    List<String> sortedCopy = names.stream().sorted().collect(Collectors.toList());
    String str = sortedCopy.stream().collect(Collectors.joining(","));
    System.out.println(str); 
    //自定义排序,lambda表达式定义排序逻辑,这里是降序
    List<Integer> ages = Arrays.asList(18, 17, 19, 20);
    List<Integer> ages2 = ages.stream().sorted((v1, v2) -> {
        if (v1 > v2) {
            return -1;
        } else if (v1 < v2) {
            return 1;
        } else {
            return 0;
        }
    }).collect(Collectors.toList());
    System.out.println(ages2.toString());
}

Stream#peek

        Stream.peek() ,它为每个元素提供了一个消费函数。该方法返回由该流的元素组成的流,并对每个元素执行所提供的 Consumer 操作方法,Consumer接口是java8的一个函数式接口,它定义了一个名为 accept 的抽象方法,该方法接受一个参数并且不返回任何结果。peek() 方法主要用于调试,以便在元素流过管道中的某个点时查看它们。peek() 是一个中间操作方法,将在调用终端方法时执行。

        示例

        打印输出以z开头的姓名;

java">@Test
public void test6() {
    List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    Stream<String> stream = names.stream()
            .peek(item -> {
                if (item.startsWith("z")) {
                    System.out.println(item);
                }
            });
    //在执行collect方法前,peek方法内的lambda表达式不会被执行;只有在终端方法collect()被调用时才会执行
    List<String> names2 = stream.collect(Collectors.toList());
    System.out.println(names2);
}

Stream#limit

        Stream#limit 方法用于限制 Stream 中元素的数量。它返回一个包含指定数量元素的新的 Stream,而不会改变原始 Stream 中的元素顺序。

        示例

        获取集合内的前两个元素

java">@Test
public void test7() {
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    List<String> result = list.stream().limit(2).collect(Collectors.toList());
    System.out.println(result.toString());
}

Stream#skip

        Stream#skip 方法,用于跳过 Stream 中的前 n 个元素。它返回一个新的 Stream,该 Stream 不包含原始 Stream 中的前 n 个元素。

        示例:

        跳过集合内的前3个元素,而获取剩余其他元素

java">@Test
public void test8() {
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    List<String> result = list.stream().skip(3).collect(Collectors.toList());
    System.out.println(result.toString());
}

Stream#forEach

        Stream#forEach 方法,用于对 Stream 中的每个元素执行遍历操作。该方法没有返回值,它只会对 Stream 中的每个元素进行遍历操作,而不会改变 Stream 中的元素或产生新的结果。

示例

java">@Test
public void test9() {
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    list.stream().forEach(item-> System.out.println(item));
}

Stream#forEachOrdered

        Stream#forEachOrdered方法 ,用于对 Stream 中的每个元素按照它们在 Stream 中的顺序执行一个提供的操作。与 Stream#forEach 方法不同的是,forEachOrdered方法 会保证操作的顺序与 Stream 中元素的顺序一致,而Stream#forEach方法则不保证操作的顺序。

        示例

java">@Test
public void test10() {
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    list.stream().forEachOrdered(item-> System.out.println(item));
}

Stream#reduce

        Stream#reduce 方法,用于将 Stream 中的元素进行某种聚合操作,并返回一个单一的结果。它接受一个 BinaryOperator 作为参数,该操作接受两个参数并返回一个结果。在聚合过程中,每个元素都会与前一个元素进行操作,并将结果传递给下一个元素,直到处理完所有元素并返回最终结果。使用 Stream#reduce 方法可以对数据进行求和、求积、求最大值、求最小值等各种聚合操作

        示例:

        求集合内所有学生信息中的学生年龄之和

java">@Test
public void test11() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    Integer totalAge = list.stream().reduce((val1, val2) -> new Student(val1.getAge() + val2.getAge())).get().getAge();
    System.out.println(totalAge);
}

Stream#collect

        Stream#collect 方法,用于将 Stream 中的元素收集到集合或其他对象中,如 List、Set、Map 等。它接受一个 Collector 作为参数,该 Collector 是一个函数式接口,用于定义将元素收集到目标集合或其他对象中的操作。

        关于Collector,可以参考Collectors类,该类内置很多静态方法,用于获取常见的Collector;

Stream#min

        Stream#min方法,用于找到 Stream 中元素的最小值。它返回 Stream 中元素的最小值,如果 Stream 为空,则返回 Optional.empty。Stream#min方法接受一个比较器作为参数,可以根据特定的业务逻辑或数据类型自定义自己的比较逻辑。

        示例

        获取集合内年龄最大的学生信息

java">    @Test
    public void test12() {
        List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
        Student student = list.stream().min((v1, v2) -> {
            if (v1.getAge() > v2.getAge()) {
                return 1;
            } else if (v1.getAge() < v2.getAge()) {
                return -1;
            } else {
                return 0;
            }
        }).get();
        System.out.println(student.toString());
    }
}

Stream#max

        与Stream#min方法类似,Stream#max用于找到 Stream 中元素的最大值,Stream#max方法接受一个比较器作为参数,可以根据特定的业务逻辑或数据类型自定义自己的比较逻辑。

java">@Test
public void test13() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    Student student = list.stream().max((v1, v2) -> {
        if (v1.getAge() > v2.getAge()) {
            return -1;
        } else if (v1.getAge() < v2.getAge()) {
            return 1;
        } else {
            return 0;
        }
    }).get();
    System.out.println(student.toString());
}

Stream#count

        Stream#count方法的功能是计算Stream中元素的数量,并返回一个long类型的数值。这个方法不会将Stream中的所有元素收集到一个集合中,而是直接在流上执行计数操作,因此可以高效地计算出Stream中元素的数量。单纯使用count(),与size()效果是一样的,实际使用过程中,一般会结合其他中间方法使用,如filter();

        示例

        统计姓名集合中以z开头的元素有几个

java">@Test
public void test14() {
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    long num = list.stream().filter(item -> item.startsWith("z")).count();
    System.out.println(num);
}

Stream#anyMatch

        Stream#anyMatch 方法,用于判断 Stream 中的元素是否满足指定的匹配条件。如果 Stream 中至少有一个元素满足匹配条件,则返回 true;否则返回 false。

        示例

        统计学生信息集合中,是否存在有年龄小于18的学生信息

java">@Test
public void test15() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    boolean flag = list.stream().anyMatch(item -> item.getAge() < 18);
    System.out.println(flag);//结果是true
}

Stream#allMatch

        Stream#allMatch 方法用于检查 Stream 中的所有元素是否满足给定的条件函数。如果 Stream 中的所有元素都满足条件函数,则返回 true,否则返回 false,可以用在需要检查 Stream 中所有元素是否满足某个条件的场景中。

        示例

        检查学生集合集合中的学生年龄是否全部大于18

java">@Test
public void test16() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    boolean flag = list.stream().allMatch(item -> item.getAge() < 18);
    System.out.println(flag);//结果是false
}

Stream#noneMatch

        Stream#noneMatch方法,它用于检查Stream中的元素是否都不满足给定的断言条件。Stream#noneMatch方法接受一个Predicate(断言)作为参数,该断言用于测试每个元素是否满足某个条件。如果Stream中没有任何一个元素满足断言条件,则noneMatch方法返回true;否则返回false。

        示例

        检查学生信息集合中的学生姓名首字母是否都不是以a开头

java">@Test
public void test17() {
    List<Student> list = Arrays.asList(new Student("zhangsan", 18), new Student("lisi", 19), new Student("wangwu", 17));
    boolean flag = list.stream().noneMatch(item -> item.getName().startsWith("a"));
    System.out.println(flag);//结果是
}

Stream#findFirst

        Stream#findFirst 方法,用于查找并返回 Stream 中的第一个元素。使用 findFirst 方法可以方便地查找符合特定条件的第一个元素,而无需遍历整个 Stream。findFirst 方法不会在 Stream 为空时抛出异常,而是返回一个空的 Optional 对象。可以结合着filter方法一起使用。

        示例

        检索出学生姓名集合中学生姓名以z开头的第一个姓名

java">@Test
public void test18() {
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    String name = list.stream().filter(item->item.startsWith("w")).findFirst().get();
    System.out.println(name);//结果是wangwu
}

Stream#findAny

        在 Java 11 中,Stream.findAny() 方法用于在流中查找满足特定条件的第一个元素。这个方法返回一个 Optional 对象,表示可能的元素。如果流为空,则返回一个空的 Optional。

        示例

java">@Test
public void test19(){
    List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
    String name = list.stream().findAny().get();
    System.out.println(name); //结果为zhangsan
    List<String> list2=new ArrayList<>();
    boolean flag = list2.stream().findAny().isEmpty();
    System.out.println(flag);//结果为true
}

Stream#of

        Stream#of() 方法,用于创建一个包含指定元素的 Stream。这个方法接收一个可迭代的数据源,如数组或集合,并返回一个包含该数据源中所有元素的 Stream

        示例

java">@Test
public void test20(){
    long count = Stream.of("zhangsan", "lisi", "wangwu", "zhaoliu").count();
    System.out.println(count);
}

Stream#iterator

        Stream#iterator 方法,用于获取一个迭代器,用于遍历 Stream 中的元素。这个方法返回一个 Iterator 对象,可以使用 next() 方法依次获取 Stream 中的每个元素。

java">@Test
public void test21(){
    Iterator<String> iterator = Stream.of("zhangsan", "lisi", "wangwu", "zhaoliu").iterator();
    while (iterator.hasNext()){
        String next = iterator.next();
        System.out.println(next);
    }
}

下一篇:深入了解Java 8 新特性:Stream流的实践应用(二) 


http://www.niftyadmin.cn/n/5200764.html

相关文章

AcWing102. 最佳牛围栏

题目 农夫约翰的农场由 N N N 块田地组成&#xff0c;每块地里都有一定数量的牛&#xff0c;其数量不会少于 1 头&#xff0c;也不会超过 2000 头。 约翰希望用围栏将一部分连续的田地围起来&#xff0c;并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。 围起区…

Java查询多条数据放入word模板 多个word文件处理成zip压缩包并在前端下载.zip文件

需求&#xff1a;Java查询多条数据放入word模板 多个word文件处理成zip压缩包并在前端下载.zip文件 解决方法&#xff1a;在模板的位置定义参数如 {{name}} {{age}}等等&#xff0c;使用 poi 处理 伪代码&#xff1a; PostMapping("/exportPracticeAppr")public S…

阿里云服务器带宽可以修改吗?不够用怎么办?

阿里云服务器公网带宽不够用有哪些解决方法&#xff1f;可以更改带宽或带宽临时升级&#xff0c;更改带宽是永久公网带宽&#xff0c;带宽临时升级可以选择升级时间段&#xff0c;也可以绑定弹性公网EIP来修改公网带宽&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云服务…

JavaScript AJAX操作

AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在Web开发中常用的技术&#xff0c;它允许在不刷新整个页面的情况下&#xff0c;通过与服务器进行异步通信来更新部分页面内容。本文将介绍如何使用JavaScript中的AJAX技术&#xff0c;包括发送异步请求、处…

操作系统基础操作

操作系统的启动 体系结构概念 CPU、I/O、内存-通过总线连接 操作系统一开始存放时没有放在内存里&#xff0c;而是当在DISK中&#xff0c;由BIOS提供相应支持 DISK&#xff1a;存放OSBIOS&#xff1a;基本I/O处理系统&#xff08;计算机开机时可以让系统检测各种外设&#…

探寻欧洲市场的机遇:深度剖析欧洲跨境电商

随着全球化的不断推进&#xff0c;欧洲作为一个经济发达、多元文化共存的大陆&#xff0c;成为跨境电商发展的重要目标。本文将深入剖析欧洲跨境电商的机遇&#xff0c;分析欧洲市场的特点、挑战与前景&#xff0c;为企业提供在这个充满潜力的市场中蓬勃发展的指导。 欧洲市场的…

第四天 map函数使用说明

map(function, iterable) 参数解释如下&#xff1a; function&#xff1a;一个函数或方法iterable&#xff1a;一个或多个序列&#xff08;可迭代对象&#xff09;函数的作用是&#xff1a;对序列iterable中每一个元素调用function函数&#xff0c;返回一个map对象实例。这个…

民宿酒店景区污水处理生产厂家整套设备

民宿酒店景区污水处理生产厂家整套设备 1.设备埋于地表下&#xff0c;上面可以进行绿化&#xff0c;环境美观。 2.整个设备一般不需要专人管理。 3.可以减少占地面积&#xff0c;设备上方可修建停车场等&#xff0c;无需建厂房等设施。 4.对周围环境无影响、污泥产生量少、噪音…