首页 > 其他分享 >02-JDK新特性-Stream流

02-JDK新特性-Stream流

时间:2024-04-03 21:33:13浏览次数:21  
标签:02 Stream JDK stream System 操作 public out

Stream流

什么是Stream流

Stream流是Java 8中的一个新特性,它提供了一种处理集合和数组的方式。Stream流可以让我们以一种更加简洁、高效、可读性更强的方式来处理数据。

Stream流可以用于过滤、映射、排序、聚合等操作,它可以让我们避免使用循环和条件语句来处理数据,从而让代码更加简洁易懂。

Stream流的操作可以分为中间操作和终止操作两种类型,中间操作返回的是一个新的Stream流,终止操作返回的是一个非Stream类型的结果。

Stream流的处理是惰性求值的,只有在执行终止操作时才会开始处理数据。

流(Stream)是一种基于支持一次性处理数据的数据源的元素序列,流只能使用一次。

流的设计初衷是为了支持函数式编程,它的目的是将数据处理和数据存储分离开来,使得数据处理更加灵活和高效。因此,流的元素只是在流中传递的临时数据,它们并不是永久存储在内存中的数据。

当流的元素被消费后,它们就会被释放,不能再次使用.。如果需要对同一个数据集进行多次不同的操作,可以使用流的中间操作方法来构建多个流管道,每个流管道都可以对流进行不同的操作,并返回一个新的流。这样就可以对同一个数据集进行多次操作,而不需要重新获取数据集。

流的分类

顺序流

顺序流是一种单线程的流,它按照数据流的顺序依次处理每个元素,每个元素的处理都必须等待上一个元素的处理完成才能开始

并行流

并行流是一种多线程的流,它可以将数据分成多个部分并行处理,每个部分都可以在不同的线程中处理,从而提高处理效率。

使用顺序流可以保证数据处理的顺序和一致性,适用于处理数据量较小的情况。而使用并行流可以提高数据处理的速度,适用于处理数据量较大、处理时间较长的情况。

但是并行流也有一些缺点,比如线程之间的通信和同步会带来额外的开销,而且并行流可能会影响数据的顺序和一致性。因此在使用并行流时需要注意线程安全和数据一致性等问题

区别

顺序流和并行流的区别在于它们的处理方式不同,顺序流是单线程的,而并行流是多线程的。使用的方法也有一些区别,例如:

  • 获取顺序流:可以使用集合类的stream()方法、Arrays类的stream()方法、Stream类的of()方法、Stream类的iterate()方法、Stream类的generate()方法、Files类的lines()方法等来获取顺序流。
  • 获取并行流:可以使用集合类的parallelStream()方法、Stream类的of()方法的parallel()方法、Stream类的iterate()方法的parallel()方法、Stream类的generate()方法的parallel()方法等来获取并行流。

除此之外,顺序流和并行流的使用方法基本相同,例如可以使用map()、filter()、reduce()等方法对流进行操作。但需要注意的是,在使用并行流时需要考虑线程安全和数据一致性等问题。

Stream流的使用

按照的处理过程可以分为

  1. 生成流

    通过数据源(集合、数组等)生成流,例如:list.stream()
  2. 中间操作

    一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流交给下一个操作使用.例如:filter()
  3. 终结操作

    一个流只能有一个终结操作,当这个操作执行后,流就被使用完了,无法在继续操作。所以着必定是流的最后一个操作。例如:forEach()

生成流的方式

  1. 通过集合获取流
package demo1;

import java.util.*;
import java.util.stream.Stream;

/**
 * 生成流的方式:
 * 通过集合获取流:可以使用集合类中的stream()方法或parallelStream()方法来获取流
 *
 * @author Anna.
 * @date 2024/4/3 16:18
 */
public class StreamDemo01 {
    public static void main(String[] args) {
        System.out.println("=======List========");
        System.out.println("=======顺序流========");
        List<String> list = Arrays.asList("林青霞", "张曼玉", "王祖贤", "柳岩", "张敏", "张无忌");
        // 通过集合中stream()获取顺序流
        Stream<String> stream = list.stream();
        // 循环输出
        stream.forEach(System.out::println);

        System.out.println("=======并行流========");
        // 通过集合中stream()获取并行流
        Stream<String> parallelStream = list.parallelStream();
        parallelStream.forEach(s -> System.out.printf("%s 输出了:%s%n", Thread.currentThread().getName(), s));

        System.out.println("=======Set========");
        Set<String> set = new HashSet<String>(list);
        // 通过stream()获取顺序流
        Stream<String> streamSet = set.stream();
        // 循环输出
        streamSet.forEach(System.out::println);

        // 并行流演示(后续单独说)

        System.out.println("=======Map========");
        // Map中没有提供直接转换成Stream流的相应的方法,但是可以通过间接的方式获取
        Map<String, String> map = new HashMap<String, String>();
        map.put("name", "林青霞");
        map.put("age", "30");
        // 获取Map中keySet对应的Stream流
        Stream<String> stream1 = map.keySet().stream();
        stream1.forEach(System.out::println);

        // 获取Map中values对应的Stream流
        Stream<String> stream2 = map.values().stream();
        stream2.forEach(System.out::println);

        // 获取Map中entrySet对应的Stream流
        Stream<Map.Entry<String, String>> stream3 = map.entrySet().stream();
        stream3.forEach(s -> {
            System.out.printf("key=%s,value=%s%n", s.getKey(), s.getValue());
        });
    }
}
  1. 通过Arrays数组工具类中stream()方法获取流
package demo1;

import java.util.Arrays;
import java.util.stream.Stream;

/**
 * 生成流的方式:
 * 通过Arrays数组工具类中stream()方法获取流
 *  Arrays  没有提供并行流的原因是因为数组在内存中是连续存储的,不能像集合那样方便地分割成多个部分进行并行处理。  如果要对数组进行并行处理,需要将其转换为流或使用并发编程技术手动分割和处理数组的不同部分。因此,并行流更多地是针对集合类数据结构而设计的。
 * @author Anna.
 * @date 2024/4/3 16:18
 */
public class StreamDemo02 {
    public static void main(String[] args) {
        String[] arr = {"林青霞", "张曼玉"};
        Stream<String> stream = Arrays.stream(arr);
        // 循环输出
        stream.forEach(System.out::println);
    }
}

注意:

Arrays 没有提供并行流的原因是因为数组在内存中是连续存储的,不能像集合那样方便地分割成多个部分进行并行处理。

如果要对数组进行并行处理,需要将其转换为流或使用并发编程技术手动分割和处理数组的不同部分。因此,并行流更多地是针对集合类数据结构而设计的。

  1. 通过 Stream.of()方法获取流
package demo1;

import java.util.stream.Stream;

/**
 * 生成流的方式:
 * 通过 Stream.of()方法获取流
 *
 * @author Anna.
 * @date 2024/4/3 16:18
 */
public class StreamDemo03 {
    public static void main(String[] args) {
        String[] arr = {"林青霞", "张曼玉"};
        Stream<String> stream = Stream.of(arr);
        // 循环输出
        stream.forEach(System.out::println);
    }
}
  1. 通过Stream.iterate()方法获取流
package demo1;

import java.util.stream.Stream;

/**
 * 生成流的方式:
 * 通过Stream.iterate()方法获取流:可以使用 Stream 类中的iterate()方法来获取流
 *
 * @author Anna.
 * @date 2024/4/3 16:18
 */
public class StreamDemo04 {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.iterate(1, (x) -> x + 100).limit(3);
        stream.forEach(System.out::println);
    }
}

执行结果

在这里插入图片描述

  1. 通过Stream.generate()方法获取流
package demo1;

import java.util.stream.Stream;

/**
 * 生成流的方式:
 * 通过Stream.generate()方法获取流:可以使用 Stream 类中的generate()方法来获取流
 *
 * @author Anna.
 * @date 2024/4/3 16:18
 */
public class StreamDemo05 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.generate(() -> new String("a123")).limit(3);
        stream.forEach(System.out::println);
    }
}

执行结果

在这里插入图片描述

  1. 通过Files.lines()方法获取流

GenStream.txt

张三,30
李四,20
王五,40
package demo1;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * 生成流的方式:
 * 通过Files.lines()方法获取流:可以使用Files类中的lines()方法来获取流。
 *
 * @author Anna.
 * @date 2024/4/3 16:18
 */
public class StreamDemo06 {
    public static void main(String[] args) throws IOException {
        new StreamDemo06().genStreamByFile();
    }

    public void genStreamByFile() throws IOException {
        Stream<String> lines = Files.lines(Paths.get("D:/WordkSpaces/JavaWorkSpaces/IdeaWorkSpaces/StudyWorkSpaces/JavaStudyWorkSpaces/02-new-feature-stream/src/main/resources/GenStream.txt"));
        lines.forEach(System.out::println);
    }
}

执行结果

在这里插入图片描述

中间操作

常用中间操作如下:

分类方法名作用
筛选与切片
-filter(Predicate<? super T> predicate)保留满足给定条件的元素
-distinct()返回由该流的不同元素(根据Object.equals(Object))组成的流
-limit(long maxSize)返回一个新流,其元素是该流的前n个元素
-skip(long n)跳过前n个元素
映射
-map(Function<? super T,? extends R> mapper)将每个元素应用到一个函数上,并返回结果的新流
-flatMap(Function<? super T,? extends Stream<? extends R>> mapper)将每个元素应用到一个函数上,该函数产生一个流,然后将所有产生的流连接成一个流
排序
-sorted()返回由该流的元素组成的流,根据元素的自然顺序排序(即按照字母顺序)
-sorted(Comparator<? super T> comparator)返回由该流的元素组成的流,根据提供的Comparator进行排序
有状态操作
-distinct()虽然不是典型的有状态操作,但在某些实现中,它可能需要跟踪遇到的元素以提供不同的结果
-sorted()这是一个有状态操作,因为它需要维护一个内部状态来跟踪排序顺序

注意:

不是所有的中间操作都是有状态的。有状态的操作是那些需要访问多个输入元素才能产生输出元素的操作,比如sorted(),它需要在整个流上操作以产生排序后的结果。

此外,还有一些组合器操作,如peek(Consumer<? super T> action),它主要用于调试目的,允许在流的处理过程中执行某些操作,但并不改变流的内容。

终结操作

常用终结操作如下:

方法名作用
forEach(Consumer<? super T> action)对流中的每个元素执行给定的操作。例如,可以用于打印流中的每个元素或执行其他副作用。
collect(Collector<? super T,A,R> collector)将流中的元素累积到一个汇总结果中,如List、Set或自定义类型。
reduce(T identity, BinaryOperator accumulator)将流中的元素使用提供的累积器函数组合起来,以产生单个输出值。
reduce(BinaryOperator accumulator)这是上一个reduce方法的简化版本,它假定流不为空,并且没有提供初始值。
anyMatch(Predicate<? super T> predicate)检查流中是否至少有一个元素满足给定的条件。
allMatch(Predicate<? super T> predicate)检查流中的所有元素是否都满足给定的条件。
noneMatch(Predicate<? super T> predicate)检查流中是否没有任何元素满足给定的条件。
findAny()返回流中的某个元素。此操作对于并行流可能返回流中的任何元素。
findFirst()返回流中的第一个元素。对于有序流,这将是第一个元素;对于无序流,这将是遇到的某个元素。
count()返回流中的元素数量。
max(Comparator<? super T> comparator)返回流中的最大元素,根据提供的比较器进行排序。
min(Comparator<? super T> comparator)返回流中的最小元素,根据提供的比较器进行排序。
toArray()将流中的元素收集到一个新数组中。

注意:

一旦执行了终结操作,流就会被消费掉,并且不能再次使用。如果你需要再次处理相同的数据,你需要重新创建流。同时,终结操作会触发流的中间操作链的执行,从而生成最终的结果。

案例

如图使用Stream对一个集合List,生成流后,首先筛选以张开头的姓名,然后筛选长度为3的姓名,最后输出结果

在这里插入图片描述

package demo2;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * 使用Stream对一个集合List,生成流后,首先筛选以张开头的姓名,然后筛选长度为3的姓名,最后输出结果
 *   List<String> list = Arrays.asList("林青霞", "张曼玉", "王祖贤", "柳岩", "张敏", "张无忌");
 *
 * @author Anna.
 * @date 2024/4/3 20:40
 */
public class StreamDemo {
    public static void main(String[] args) {
        // 创建集合
        List<String> list = Arrays.asList("林青霞", "张曼玉", "王祖贤", "柳岩", "张敏", "张无忌");
        // 获取流
        Stream<String> stream = list.stream();
        // 执行操作,输出结果
        stream.filter(s -> s.startsWith("张"))   // 筛选以张开头的姓名
                .filter(s -> s.length() == 3)   // 筛选长度为3的姓名
                .forEach(System.out::println);  // 输出结果
        ;
    }
}

执行结果
在这里插入图片描述

Stream的特点

  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果;
  2. stream对于值传递的集合不改变数据源,如果是对象则 引用传递则会改变数据源
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行
  4. stream不可复用,对一个已经进行过终端操作的流再次调用,会抛出异常。

案例

package demo3;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * 验证
 * 1 stream对于值传递的集合不改变数据源,如果是对象则 引用传递则会改变数据源
 * 2 只有调用终端操作时,中间操作才会执行
 * 3 stream不可复用
 *
 * @author Anna.
 * @date 2024/4/3 20:47
 */
public class StreamDemo {

    /**
     * 定义一个内部内
     */
    static class UserDo {
        private String name;
        private Integer age;

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

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

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

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

    public static void main(String[] args) {
        System.out.println("===============验证stream对于值传递的集合不改变数据源================");
        // 创建一个List集合
        List<Integer> list = Arrays.asList(1, 2, 3);
        // 获取流修改数据
        list.stream().map(s -> 20).forEach((s) -> System.out.printf("输出Straem处理后结果:%s%n", s));

        // 输出源数据
        list.forEach((s) -> {
            System.out.printf("输出源数据:%s%n", s);
        });

        System.out.println("===============验证stream对于对象引用传递则会改变数据源================");
        // 创建一个List集合
        List<UserDo> userDos = Arrays.asList(new UserDo("张三", 20), new UserDo("李四", 20));
        // 获取流
        Stream<UserDo> stream = userDos.stream();
        // 通过流将集合中所有所有UserDo属性age设置为30,并输出结果
        stream.map(s -> {    // 属性age设置为30
            s.setAge(30);
            return s;
        }).forEach((s) -> { // 输出结果
            System.out.printf("输出Straem处理后结果:%s%n", s);
        });

        // 输出源数据
        userDos.forEach((s) -> {
            System.out.printf("输出源数据:%s%n", s);
        });

        // 验证stream终结后是否可以复用
        // 报错 提示 Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
//        stream.map(s ->{    // 属性age设置为30
//            s.setAge(30);
//            return s;
//        }).forEach((s) ->{System.out.printf("输出Straem处理后结果:%s%n");});    // 输出结果

        System.out.println("===============验证只有调用终端操作时,中间操作才会执行================");
        // 验证只有调用终端操作时,中间操作才会执行
        // 重新获取流
        userDos.stream().peek(s -> System.out.println("===执行了===="));
    }
}

执行结果

在这里插入图片描述

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

标签:02,Stream,JDK,stream,System,操作,public,out
From: https://blog.csdn.net/u014331138/article/details/137358098

相关文章

  • #19 2024.4.3
    694.pjudge21633【PER#2】2048695.loj3483「USACO2021.2Platinum」CountingGraphs696.loj2468「2018集训队互测Day2」神秘货币史。697.cf1935fAndrey'sTree反思。考虑一个\(mx\rightarrowmx+1\)的构造。那么它挺赢的。考虑一些cornercase,即\(u=mx......
  • 2024.04 别急记录
    1.餐巾计划问题建图跑费用流即可:\((S,1,\inf,p)\);\(\foralli\in[1,N],(i,i+N,r_i,0)\);\(\foralli\in[1,N],(S,i+N,r_i,0)\);\(\foralli\in[1,N],(i,T,r_i,0)\);\(\foralli\in[1,N),(i,i+1,\inf,0)\);\(\foralli\in[1,N-n],(i+N,i+n,\inf,f)\);\......
  • 随堂练习2024.4.3
    建立规则,仪式,流程,模式:  定义代码编写和审查的标准,确保开发质量。  实施敏捷开发的仪式,如日常站会,迭代评审和回顾会议,以提高团队协作和项目透明度。 建立清晰的开发流程和里程碑,确保项目按时推进。给好行为正面的反馈:  对于按时完成任务且代码质量高的开发人员......
  • jdk8 StreamAPI 的使用
    本文将以UmsMenu对象为例来介绍StreamAPI的常用操作。UmsMenu是一个菜单对象,具有树形结构,对象定义如下Stream对象的创建Stream对象分为两种,一种串行的流对象,一种并行的流对象。1//menuList指所有菜单列表2//为集合创建串行流对象3Stream<UmsMenu>stream=menuLis......
  • Nginx 配置反向代理时出现 502 Bad Gateway (13_ Permission denied) while connectin
    我们在Nginx配置反向代理后,可能会出现如下报错:502BadGateway(13:Permissiondenied)whileconnectingtoupstreamSorry,thepageyouarelookingforiscurrentlyunavailable.<br/>Pleasetryagainlater.这些错误,一般是由SELinux引起的(https://stackoverf......
  • 吴恩达2022机器学习专项课程(一) 4.6 运行梯度下降&第一周课程实验:线性回归的梯度下降
    问题预览/关键词更新梯度下降对模型拟合,等高线图,3d空间图的变化。什么是批量梯度下降。实验目标计算梯度运行梯度下降梯度下降迭代次数和成本函数的关系可视化模型预测在等高线图上的梯度下降学习率过大报错问题笔记1.模型拟合,等高线图,3d空间图的变化3.5课节有一样的图,......
  • 2024.3.8力扣每日一题——找出美丽数组的最小和
    2024.3.8题目来源我的题解方法一数学题目来源力扣每日一题;题序:2834我的题解方法一数学经过分析,在target之前,取小于等于target/2的正整数才能使得和最小,并且满足条件3。时间复杂度:O(n)空间复杂度:O(n)publicintminimumPossibleSum(intn,inttarget)......
  • 2024年抖店还能做吗?市场是否趋于饱和?谈谈我的看法!
    我是电商珠珠现在已经2024年了,抖店距离现在已经发展了有4年多的时间了。目前红利还在,但是不如20年的风口那么大了。况且,现在已经上边有很多商家,除非你很优秀,否则不建议你做这个。抖店现如今严查无货源,有些新手可能还不知道什么是无货源,我来解释一下。无货源就是不需要货源,只......
  • 阿里云可观测 2024 年 3 月产品动态
    本月可观测热文回顾文章一览:全新架构!日志服务SLS自研免登录方案发布AIOps智能运维:有没有比专家经验更优雅的错/慢调用分析工具?一文看懂如何做好SQL质量监控使用SPL高效实现FlinkSLSConnector下推功能快报点击此处,了解更多产品详情。......
  • 2024-04-03 闲话
    我七八岁的时候非常盼望我能自由走出小区而不被爸爸妈妈担心安全;小学考试成绩很差的时候总盼着下次考试的到来能把现在的沮丧覆盖掉;疫情的时候我非常盼望能搂着人坐在沙发上享受午后的阳光,可能是因为我的臂弯从来没享受过长发的青睐;当联赛成绩很好的时候,我总是期盼省选那天的到......