Stream流

 

前言:

流式编程:流式编程是一种编程范式。

Stream流是依附Lambda表达式。Stream API 是与 Lambda 表达式紧密相关的。在 Java 8 中引入了 Lambda 表达式的同时,也引入了 Stream API。Lambda 表达式提供了一种简洁的方式来传递行为,而 Stream API 则提供了一种流畅的、函数式的方式来处理集合数据。

 

  在Java中,一元操作符是一种操作符,只涉及一个操作数。Java提供了多种一元操作符,用于执行各种操作。

  操作符及功能:

  1. 递增和递减操作符:

    • ++:递增操作符,用于将操作数的值增加1。
    • --:递减操作符,用于将操作数的值减少1。
  2. 正负号操作符:

    • +:正号操作符,用于表示正数。在实际应用中并不经常使用,因为正号没有实际改变数值。
    • -:负号操作符,用于表示负数。
  3. 逻辑非操作符:

    • !:逻辑非操作符,用于将布尔值取反。如果操作数为true,则结果为false;如果操作数为false,则结果为true。
  4. 按位取反操作符:

    • ~:按位取反操作符,用于按位取反操作数的二进制表示。例如,~x会对x的每个二进制位取反。
  5. 类型转换操作符:

    • (type):用于执行强制类型转换。例如,(int) x将变量x强制转换为int类型。

  这些一元操作符可以应用于各种数据类型,包括整数、浮点数和布尔值。在Java中,一元操作符可以用于变量、常量或表达式中。

 

1. 流式思想

  流式思想类似于工厂车间的”流水线“。当需要对元素进行操作(特别是多部操作)时,考虑到性能机便利性,应该先拼好一个”模型步骤方案“,然后再用方案执行。

  类比到编程,流式思想也是一种将处理过程划分为一系列步骤的方式。每个步骤都执行特定的操作,然后将结果传递给下个步骤进行进一步处理。

  这种方式使得代码更具可读性和可维护性。同时每个步骤都可重用,从而提高代码的灵活性和效率。

 

2. Stream(流)的概念

  Stream是JDK1.8中处理集合的关键抽象概念。Stream提供一种新的方式来处理集合数据,是代码更为简介,易懂,高效。

  使用Stream API 对集合数据进行操作,类似于使用SQL执行数据库查询。

  Stream处理元素可以看作是一种流式处理。元素流在管道中经过中间操作的处理,由终端操作得到前面处理的结果。

  特点:

  1. 连续的数据序列: Stream流代表了一系列元素的连续流,这些元素可以来自集合、数组、I/O通道等数据源。它们提供了一种更便捷和抽象的方式来处理数据集合。

  2. 惰性求值: Stream流的操作是惰性求值的,这意味着流的操作不会立即执行,直到需要结果时才会进行计算。这种延迟执行的特性使得流操作非常高效,尤其是在处理大数据集时。

  3. 函数式编程风格: Stream流提供了一套丰富的函数式编程风格的操作方法,例如map、filter、reduce、collect等。这些方法允许通过函数式接口来对流中的元素进行转换、过滤、聚合等操作,使得代码更加简洁和灵活。

  4. 流水线操作: Stream流的操作可以被连接成一个流水线,其中每个操作依次应用于流中的每个元素。这种流水线操作的设计使得代码更易读、易于理解,并且可以轻松地进行操作的组合和重用。

  5. 中间操作和终端操作: Stream操作可以分为中间操作和终端操作两种类型。中间操作(Intermediate Operations)用于对流中的元素进行转换、筛选等操作,而终端操作(Terminal Operations)则产生最终的结果。终端操作是流的触发点,执行终端操作后流将被消费,无法再进行其他操作。

3. Stream 创建

  1) 通过集合创建

  方法一:使用集合类提供的stream()方法获取一个串行流

  方法二:使用parallelStream()方法获取一个并行流,以提高处理效率。(用于多线程安全高效情况下)

        // 1. 通过集合创建
        List<String> listStream = Arrays.asList("张三","李四","王五");    // 创建包含字符串元素的列表

        Stream<String> sequentialStream = listStream.stream();      // 串行流

        Stream<String> parallelStream = listStream.parallelStream();    // 并行流

  串行流是 Stream API 中的一种流,它按照元素在集合中的顺序进行处理,即一个元素处理完成后才会处理下一个元素。

  并行流是 Stream API 中的一种流,它会将数据分成多个小块并行处理,以提高处理效率。

 

  2) 通过数组创建

  使用Arrays类的静态方法stream()获取数组的流。

        // 2. 通过数组创建
        String[] array = {"赵六","孙七","周八"};      // 创建包含字符串元素的数组

        Stream<String> stream = Arrays.stream(array);   // 调用 Arrays 类的 stream() 方法,将数组 array 转换为一个 Stream 流 stream;

 

  3) 通过Stream内的静态方法创建

  使用Stream.of()方法可直接创建一个包含指定元素的流。

        // 3. 通过Stream内的静态方法创建
        Stream<String> streamStatic = Stream.of("曹阿瞒","刘玄德","孙仲谋");

 

  4) 创建无限流

  无限流是指流中的元素数量是无限的,因此需要采取一些方式来限制流的大小,以便在实际处理中不会造成无限处理的情况。

  流中元素无限多,配合limit()使用。

  方式一:使用Stream.iterate()方法创建无限流

  Stream.iterate()方法接受一个初始值和一个一元操作符(UnaryOperator)作为参数。会从初始值开始,通过一元操作符生成后续的元素。

复制代码
        // 4. 创建无限流
        // 方式一:
        // 例:使用 Stream.iterate(0, n -> n + 2) 来创建一个从0开始,每次加2的无限流。
        /**
         *  调用Stream的iterate()方法创建创建流。
         *  两个参数:第一个参数为初始值:0;第二个参数用Lambda表达式表示对当前元素 n+2,生成下一个。
         */
        Stream<Integer> iterate = Stream.iterate(0,n -> n + 2);

        // 使用limit()方法来限制打印数量
        iterate.limit(10).forEach(System.out::println);     // 结果:0, 2, 4, 6, 8, ...

        // 例:使用 Stream.iterate() 方法从初始值 1 开始,每次将前一个元素乘以 2 生成后续的元素
        // 使用 limit(10) 方法限制了流的大小为 10 个元素,collect(Collectors.toList())是 Java 8 中 Stream API 中的一个方法调用,最终将这些元素收集到一个列表中。
        List<Integer> iterateCollection = Stream.iterate(1, n -> n*2).limit(10).collect(Collectors.toList());
        System.out.println("iterateCollection = " + iterateCollection);     // 结果:iterateCollection = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
复制代码

  一元操作符是一种操作符,只涉及一个操作数。Java提供了多种一元操作符,用于执行各种操作。

 

  方法二:使用Stream.generate()方法来创建无限流

  Stream.geberate()方法接受一个Supplier函数式接口作为参数,该接口生成流中的元素。因Supplier作为一个生产者,所以可产生无限的元素。

复制代码
        // 方式二:
        // 例:使用 Stream.generate(Math::random) 来创建一个包含随机数的无限流。
        Stream<Double> generate = Stream.generate(Math::random);    // 调用 Stream 类的静态方法 generate() 来创建流。接受一个参数: Stream<Double> 函数式接口。

        generate.limit(10).forEach(System.out::println); // 结果随机十条,范围在 0.0 到 1.0 之间。

        // 例:使用 Stream.generate() 创建一个无限流,元素是随机的整数
        // () -> new Random().nextInt(100) 作为 Supplier,每次调用它都会生成一个 0 到 99 之间的随机整数。
        List<Integer> generateCollection = Stream.generate(() -> new Random().nextInt(100)).limit(10).collect(Collectors.toList());
        System.out.println("generateCollection = " + generateCollection);   // 结果:generateCollection = [66, 48, 82, 43, 84, 67, 28, 72, 18, 21]
复制代码

 

  当创建流时,如果数组中包含基本数据类型的元素,而我们希望将其转换为对应的引用数据类型,需要使用 boxed() 方法进行装箱操作。

        // 当创建流时,如果数组中包含基本数据类型的元素,而我们希望将其转换为对应的引用数据类型,需要使用 boxed() 方法进行装箱操作。
        double[] arrays = {2024.0221, 25, 19, 9.12};
        DoubleStream doubleStream = Arrays.stream(arrays);  // Arrays.stream(arrays) 方法将基本数据类型的double数组转换为DoubleStream流;
        Stream<Double> boxed = doubleStream.boxed();    // 通过boxed 方法,将DoubleStream流内的元素装箱为Stream<Double>, 即可在流内操作引用数据类型。

 

4. Stream流的中间操作

  Stream流的中间操作指的是,在处理String类型的流时,可以对流内的数据进行一系列的中间操作,以实现特定的处理需求。

  这些中间操作不会立即触发流的需求,而是返回一个新的流,这样可以构成一条流水线,最终可以通过终端处理流。

  常见的Stream API流的中间操作:

  • filter(Predicate):filter()方法根据指定的条件过滤流中的元素,只保留满足条件的元素,丢弃不满足条件的元素。
  • map(Function):map()方法将流内的每个元素映射为另一个元素,通过给定的函数。可以对流中的元素进行转换或提取重要的元素。
  • flatMap(Function):flatMap()方法将流中的每个元素映射为一个流,将所有的流连接为一个流。常用于将多个流合并为一个流,将流中的元素扁平化处理。或将流中的每个元素拆分为多个元素。
  • distinct():distinct()方法用于去除流中的重复元素,返回一个取出后的新流。
  • sorted():sorted()方法用于对流中的方法进行排序,返回一个新的流。默认按自然顺序排序,也可传入自定义的Comparator来制定排序规则。
  • limit(long):limit()方法用于限制流中元素的数量,返回一个包含前n个元素的新流。
  • skip(long):skip()方法用于跳过流中的前n个元素,返回一个新流。

  1) filter需要的参数为Predicate--判断。Predicate中的test()方法的参数就是流中的数据类型。

复制代码
        // 1. filter
        // 例:使用 filter() 方法来过滤一个整数流,只保留偶数。
        // 创建一些包含整数的流。IntStream.rangeClosed() 是 Java 中的一个静态方法调用,用于创建一个包含指定范围内整数的 IntStream 流。
        IntStream intStream = IntStream.rangeClosed(0,10);  // 生成从1-10的整数流

        // 使用filter()方法过滤出整数
        IntStream evenStream = intStream.filter(n -> n % 2 == 0);

        // 打印过滤后的结果
        evenStream.forEach(System.out::println);    // 结果:0,2,
复制代码

 

  2) limit()

  • 用于限制流内元素的数量。指定的参数表示要保留的元素的数量;
  • 参数为0时,表示不获取任何元素,返回一个空流;
  • 参数为正整数时,表示从流中获取前几个元素;
  • 参数不可为负,否则会抛出异常;
  • limit()方法常和无限流配合使用,用于限制流的大小,使其变为有限流;
        // 2. limit
        // 例:使用 Stream.generate() 方法生成一个无限流,每次生成一个 0 到 99 之间的随机整数。
        // 使用 limit(8) 方法来限制流中元素的数量为8个,并通过 forEach(System.out::println) 方法将这8个元素打印出来。
        Stream.generate(() -> new Random().nextInt(100)).limit(8).forEach(System.out::println);

 

  3) skip()

  skip方法与limit()方法相反,用于跳过流内的前几个元素,然后返回一个新的流。

  skip()方法接受一个long类型的参数,表示要跳过的元素数量。

复制代码
        // 3. skip
        // 例:使用 skip() 方法跳过流中的前几个元素
        // 创建一个包含一些整数的流
        Stream<Integer> skipStream = Stream.iterate(1, n -> n + 1);   // 创建一个无限流,从1开始递增

        // 使用skip方法从第三个元素开始,跳过4个元素,然后继续获取4条
        skipStream.limit(3)   // 显示3条
                .forEach(System.out::println);

        // 注意:skipStream 在上面第一次使用后已经被消耗掉了,不能再使用。

        skipStream = Stream.iterate(1, n -> n +1);  // 重新创建一个新的流

        skipStream.skip(3 + 4)    // 跳过前3条,再跳过4条
                .limit(4)  // 获取四条
                .forEach(System.out::println);     // 打印结果:1,2,3 8,9,10,11
复制代码

  

  注意:流是一次性资源,一旦被消耗就无法再次使用。

 

  4) distinct()

  用于去除流内重复数据,返回一个去重后的新流。如果流内元素为引用数据类型,需重写equals()和hashCode();

  因为 distinct() 方法在去除重复元素时,会依赖对象的 equals()hashCode() 方法来确定元素是否相等。如果这两个方法没有正确重写,可能会导致 distinct() 方法无法准确识别和去除重复元素。

复制代码
        // 4. distinct
        // 例:创建了一个包含一些字符串的流 stringStream,其中包含了一些重复的元素。
        // 使用 distinct() 方法对流中的元素进行去重,返回一个新的流 distinctStream。
        // 通过 forEach(System.out::println) 方法将去重后的结果打印出来。
        Stream<String> nonReferenceDataTypeStream = Stream.of("张三","李四","王五","赵六","赵六","李四","赵六");

        // 使用distinct()方法去除重复元素
        Stream<String> distinctStream = nonReferenceDataTypeStream.distinct();

        distinctStream.forEach(System.out::println);
复制代码

  为引用数据类型时:

复制代码
// Person类,在流内去除重复的Person对象
// 重写了 equals() 和 hashCode() 方法,确保在比较两个 Person 对象时,它们的 name,age,sex 都相等时才被认为是相等的。
public class Person {

    private String name;
    private Integer age;
    private String sex;

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

    // 重写equals方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name) && Objects.equals(age, person.age) && Objects.equals(sex, person.sex);
    }

    // 重写hashCode方法
    @Override
    public int hashCode() {
        return Objects.hash(name, age, sex);
    }

    // 重写toString方法
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
复制代码
复制代码
        // 例:创建一个包含一些 Person 对象的流 personStream,其中有一些重复的对象。
        // 使用 distinct() 方法对流中的 Person 对象进行去重,并将去重后的结果打印出来。
        Stream<Person> personStream = Stream.of(
                new Person("张三",20,"男"),
                new Person("李四",23,"女"),
                new Person("王五",26,"男"),
                new Person("李四",23,"女"),
                new Person("赵六",24,"女")
        );
        // 使用 distinct()方法去除重复的Person对象
        Stream<Person> deduplicationPerson = personStream.distinct();
        deduplicationPerson.forEach(System.out::println);   // 打印结果:李四保留一条
复制代码

 

  5) sort()

  对流内的元素进行排序。

  可按照自然元素和指定的指定器来排序元素,并返回一个新的流。其中包含排序后的元素序列。

  自然排序(从小到大):

复制代码
        // 5. sort()
        // 创建了一个包含一些整数的列表 numbers,然后使用 numbers.stream() 方法将列表转换为流。
        // 使用 sorted() 方法对流中的元素进行排序,这里是按照自然顺序(从小到大)排序的。
        // 通过 collect(Collectors.toList()) 方法将排序后的结果收集到一个列表中,并打印出来。

        // 创建一个包含一些整数的列表
        List<Integer> numbers = Arrays.asList(7,88,32,22,25,18,45);

        // 使用sort()方法对列表内的元素进行排序
        List<Integer> sortNumbers = numbers.stream().sorted()   // 按默认自然顺序排
                .collect(Collectors.toList());

        System.out.println("倒序" + numbers.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList())); // 倒叙

        System.out.println("自然排序后的结果:" + sortNumbers);   // 打印结果:自然排序后的结果:[7, 18, 22, 25, 32, 45, 88]
复制代码

  自定义比较器:Comparator。

复制代码
        // 自定义比较器:Comparator。比较器将字符串按照长度从小到大进行排序
        // 创建了一个包含一些字符串的列表 names,然后使用 names.stream() 方法将列表转换为流。
        // 使用 sorted() 方法并传入一个自定义的比较器 Comparator.comparingInt(String::length),该比较器将字符串按照长度从小到大进行排序。
        // 通过 collect(Collectors.toList()) 方法将排序后的结果收集到一个列表中,并打印出来。

        // 创建包含一些字符串的列表
        List<String> names = Arrays.asList("jiaWenHe","sunQuan","caoMengDe","liuBei");

        // 使用sort()方法对列表内的字符串进行长度排序
        List<String> sortNames = names.stream()
                .sorted(Comparator.comparingInt(String::length)) // 比较器将字符串按照长度从小到大进行排序
                .collect(Collectors.toList());  // 将排序后的结果收集到列表内

        sortNames.forEach(System.out::println);
复制代码

 

  6) map()

  映射。接受一个函数作为参数,函数将流内每个元素(数据类型)转换(映射)为另一个元素(数据类型),并返回一个新的流,并包含映射后的元素序列。map()的映射参数为Function。

复制代码
        // 6. map()
        // 创建了一个包含一些整数的列表 numbersMap,然后使用 numbersMap.stream() 方法将列表转换为流。
        // 使用 map() 方法并传入一个 lambda 表达式 n -> n * n,该表达式将流中的每个元素转换为其平方。
        // 通过 collect(Collectors.toList()) 方法将转换后的结果收集到一个列表中,并打印出来。

        // 创建包含整数的列表
        List<Integer> numbersMap = Arrays.asList(1,2,3,4,5);

        // 使用map()方法将列表内的元素转化为平方
        List<Integer> squaredNumbers = numbersMap.stream()
                .map(n -> n * n)    // 将每个元素转换为其平方
                .collect(Collectors.toList());

        System.out.println("转换后的结果为:" + squaredNumbers);    // 结果打印:转换后的结果为:[1, 4, 9, 16, 25]
复制代码

 

  7) flatMap()

  flatMap()方法接受一个函数作为参数,这个函数将流中的每个元素映射为一个流,然后将这些流连接成一个单独的流。通常,这个函数的返回值是一个流。

复制代码
        // 7. flatMap()
        // 有一个嵌套的列表 numbersFlatMap,其中每个列表包含一些整数。
        // 使用 flatMap() 方法将嵌套列表中的每个列表转换为一个流,然后将这些流连接成一个单独的流。
        // 通过 collect(Collectors.toList()) 方法将连接后的结果收集到一个列表中,并打印出来。
        List<List<Integer>> numbersFlatMap = Arrays.asList(
                Arrays.asList(1,3,5),
                Arrays.asList(2,4,6),
                Arrays.asList(8,9,7)
        );
        // 使用flatMap() 将嵌套列表内所有元素连成一个表
        List<Integer> flattenedNumbers = numbersFlatMap.stream()
                .flatMap(List::stream)  // 将嵌套列表内的每个列表转为一个,并将他们连接成一个单独的流
                .collect(Collectors.toList());

        System.out.println("扁平化后的结果:" + flattenedNumbers);  // 打印结果:扁平化后的结果:[1, 3, 5, 2, 4, 6, 8, 9, 7]
复制代码

 

5. Stream流的终端操作

  Stream流的终端操作是在对流进行一系列中间操作后,触发对流的最终处理,从而产生最终结果的操作。

  终端操作会触发流的遍历,执行中间操作,并返回最终的结果。流的中间操作是流的“触发器”,没有终端操作,中间操作是不会执行的。

  常见的Stream流的终端操作:

  allMatch(Predicate):检查流中所有元素是否都满足给定的条件。

  anyMatch(Predicate):检查流中是否至少存在一个元素满足给定的条件。

  noneMatch(Predicate):检查流中是否没有元素满足给定的条件。

  findFirst:返回流内第一个元素。

  findAny:返回流内任意元素。

  count:返回流中的元素数量。

  max(Comparator):返回流内最大元素。

  min(Comparator):返回流内最小元素。

  forEach(Consumer):对流内的每个元素执行指定的操作。

复制代码
        int[] array = {8, 4, 1, 15, 42, 21, 33};
        Stream<Integer> integerStream = Arrays.stream(array).boxed();   // 原始类型数组转换为一个包含相同元素的Stream<Integer>对象

        // 1. 流的终端操作-->allMatch()
        // Predicate 判断 数组内的元素是否都是偶数
        System.out.println(integerStream.allMatch(num -> num % 2 == 0));    // 输出:false

        // 注意:流是一次性的,一旦终端操作触发,流就会被消耗掉,不可再次使用

        // 使用新流进行操作
        // 2. 流的终端操作-->anyMatch()
        integerStream = Arrays.stream(array).boxed();
        // Predicate 判断数组内是否有任意元素为偶数
        System.out.println(integerStream.anyMatch(num -> num % 2 == 0));    // 结果:true

        // 3. 流的终端操作-->noneMatch()
        // 使用新流
        integerStream = Arrays.stream(array).boxed();
        // Predicate 判断组内是否不存在元素为偶数
        System.out.println(integerStream.noneMatch(num -> num % 2 == 0));   // 结果:false

        // 4. 流的终端操作 --> findFirst()
        // 使用新流
        integerStream = Arrays.stream(array).boxed();
        // 获取组内大于7的元素,并取第一个元素
        Optional<Integer> first = integerStream.filter(num -> num > 7).findFirst();
        System.out.println(first.get());    // 结果:8

        // 5. 流的终端操作 --> findAny()
        // 使用新流
        integerStream = Arrays.stream(array).boxed();
        // 获取组内大于7的元素,并任意取一
        Optional<Integer> any = integerStream.filter(num -> num > 8).findAny();
        // System.out.println(any.get());  // 结果总会是第一个 >8 的元素;
        any.ifPresent(System.out::println); // 这是因为:默认串行流,在单线程的情况下,会找到流中的第一个符合条件的元素。

        // 6. 流的终端操作 --> count()
        integerStream = Arrays.stream(array).boxed();
        // 计算组内元素的数量
        long count = integerStream.count();
        System.out.println("count = " + count);

        // 7. 流的终端操作 --> max()
        integerStream = Arrays.stream(array).boxed();
        // 获取数组内最大的元素
        Optional<Integer> max = integerStream.max(Integer::compareTo);  // Integer::compareTo 是一个比较器,用于比较两个整数的大小
        System.out.println(max.get());

        // 8. 流的终端操作操作 --> min()
        integerStream = Arrays.stream(array).boxed();
        // 获取数组内最小的元素
        Optional<Integer> min = integerStream.min(Integer::compareTo);
        System.out.println(min.get());

        // 9. forEach()
        integerStream = Arrays.stream(array).boxed();
        // 遍历每个元素
        integerStream.forEach(System.out::println);
复制代码

 

  collect(Collector):将流内的元素搜集到一个结果容器。如:list-列表、set-集、map-映射。

复制代码
        // 10. collect()
        // 创建Stream流
        Stream<Integer> stream = Stream.of(2024, 2021, 2020, 2019);

        // collect方法收集流内元素
        List<Integer> list = stream.collect(Collectors.toList());   // 收集为:list
        System.out.println(list);   // 结果:[2024, 2021, 2020, 2019]

        stream = Stream.of(2024, 2021, 2020, 2019);
        Set<Integer> set = stream.collect(Collectors.toSet());  // 收集为Set
        System.out.println(set);    // 结果:[2019, 2020, 2021, 2024]

        stream = Stream.of(2024, 2021, 2020, 2019);
        Map<Integer,String> map = stream.collect(Collectors.toMap(x -> x, x -> "value:" + x));  // 收集为map   x -> x为生成键,x -> "value:" + x为生成对应值;
        System.out.println(map);    // 结果:{2019=value:2019, 2020=value:2020, 2021=value:2021, 2024=value:2024}
复制代码

 

  reduce(BinaryOperator):最流内的元素进行归约操作返回一个包含归约结果的Optional对象。元素累计,一般用于加法计算。

        // 11. reduce-归约
        // 使用reduce方法对流内元素进行求和
        Stream<Integer> reduceStream = Stream.of(2024, 2021, 2020, 2019);
        Integer reduce = reduceStream.reduce(0, (a, b) -> a + b); // 初始值为0,规约操作是将上一侧归约结果与流内下一个元素相加,得和
        System.out.println(reduce);     // 结果:8084

 

  groupingBy(分组):是一个收集器,用于将流内元素按照指定分类函数进行分组,并将分组结果存储与Map内。通常用于对于流内元素进行分组统计。

        // 12. groupingBy()
        // 根据元素的奇偶性对流中的元素进行分组统计
        integerStream = Arrays.stream(array).boxed();   // 将数组转换为流,然后进行封装,封装为Integer类型的对象
        Map<String, List<Integer>> group = integerStream.collect(Collectors.groupingBy(x -> x % 2 == 0 ? "偶数" : "奇数"));
        System.out.println("group = " + group); // 结果:group = {偶数=[8, 4, 42], 奇数=[1, 15, 21, 33]}

 

6. Optional

  Optional是Java8引入的一个类,为了解决空指针异常问题(NullPointerException),是的代码在处理可能为null得值时更为清晰。

  特性与用法:

  容器性质:Optional是一个容器类,包含某个个类型的值或仅保存null。可用来表示一个值可能为空或不为空的情况。

  Optional.of()Optional 类的一个静态方法,用于创建一个包含指定非空值的 Optional 对象。

  存在性检查:isPresent()可以检查Optional内包含值。有值则为true;为空则为false。

  安全取值:通过get()方法获取Optional内的值;在使用optional.get()前,先使用i是Present()方法先进行存在性检查,以免抛出异常。

  提供默认值:防止空指针异常。orElse()方法是isPerson()方法与get()方法得组合,接受一个参数,用于指定默认值。例:orElse(T other)方法是在Optional容器包含数据时返回容器的数据;若为空,则返回默认的数据。

复制代码
        // orElse()
        // 如果 optionalName 容器中包含数据(即不为空),则直接返回容器中的数据 "Hello,World!";
        Optional<String> optionalName = Optional.of("Hello,World!");
        String value = optionalName.orElse("Default Value");
        System.out.println("value = " + value); // 返回:value = Hello,World!
        
        // 如果 emptyOptional 容器为空,则返回指定的默认值 "Default Value"。
        Optional<Object> emptyOptional = Optional.empty();
        Object defaultValue = emptyOptional.orElse("Default Value");
        System.out.println("defaultValue = " + defaultValue);   // 返回:defaultValue = Default Value
复制代码

 

  Optional示例:有一个函数,用于从数据库中获取用户的年龄。由于数据库操作可能失败或返回空值,我们可以使来处理这种情况。

复制代码
// 定义一个 UserService 类,其中的 getUserAge() 方法模拟了从数据库中获取用户年龄的操作。
// 如果找到了用户年龄,则返回一个包含该年龄的 OptionalTest 对象;如果用户不存在,则返回一个空的 OptionalTest 对象。
public class UserService {
    // 模拟从数据库获取用户年龄的操作
    public Optional<Integer> getUserAge(String userId) {
        // 从数据查询用户年龄;若找到,返回Optional对象包含该年龄;反之返回一个空的Optional对象
        if(userId.equals("202410007")){
            return Optional.of(28);
        }else {
            return Optional.empty();
        }
    }
    public static void main(String[] args) {
        // OptionalTest:有一个函数,用于从数据库中获取用户的年龄。由于数据库操作可能失败或返回空值,我们可以使用 OptionalTest 来处理这种情况。
        UserService userService = new UserService();
        String userID = "202410007";
        // 获取用户年龄并处理结果
        Optional<Integer> userAge = userService.getUserAge(userID);
        // 若用户年龄存在,则打印用户年龄;否则打印默认消息
        if (userAge.isPresent()) {
            int age = userAge.get();
            System.out.println("年龄为:" + age);
        }else {
            System.out.println("用户年龄不存在.");
        }
    }
}
复制代码

 

 

    

 

posted @   学Java的`Bei  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示