带你走进Java8新特性Stream流的小世界

news2024/9/22 21:20:36

目录

一. 什么是流(Stream)

1.1 流的定义

1.2 流的特点

1.3 操作流

1.4 创建流

二. 流的中间操作

2.1 流的筛选与切片

2.1.1 filter

2.1.2 limit

2.1.3 skip

2.1.4 distinct

2.2 流的映射

2.2.1 map

2.2.2 flatMap

2.3 流的排序

2.3.1 sort

三. 流的终止操作

3.1 流的查找与匹配

3.1.1 allMatch

3.1.2 anyMatch

3.1.3 noneMatch

3.1.3 findFirst与findAny

3.1.4 count

3.1.5 max与min

3.2 归约与收集

3.2.1 归约(reduce)

3.2.2 收集(collect


🐼个人主页:爪哇斗罗

🐼博主介绍:一名打工人

🐼签名:圣人之道,为而不争。

🐼一起交流,一起进步,一起互动。

一. 什么是流(Stream)

1.1 流的定义

将原来的数组或者集合(数据源)通过一系列流水线的中间操作方式产生一个新的流,相当于一个数据渠道。  

1.2 流的特点

 流的特点主要有两条记住即可:

1. 集合是数据,流是计算;

2. 流不改变数据源,也不做任何存储。

1.3 操作流

操作流只需这三步:创建流中间操作流终止操作 。也就是对应上图中的解释。

具体来说就是一个数据源获取一个流,然后通过一个中间操作,对数据进行处理后,最后一个终止操作产生一个结果。

1.4 创建流

创建流主要三种方式:了解即可,具体如何操作在后面会说到。

@Test
public void t3()
{
    // 1.创建流 通过集合提供的stream()创建
    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream();   ·
    // 2.通过Arrays中的静态方法stream()创建流
    Integer[] integers = new Integer[10];
    Stream<Integer> stream1 = Arrays.stream(integers);
    // 3.Stream.of()创建
    Stream<Integer> stream2 = Stream.of(1, 2, 4);
}

二. 流的中间操作

开始前,请准备一个集合,集合可以自己随便创建。

// 案例数据 姓名 年龄 薪水
List<Employee> employees = Arrays.asList(
    new Employee("张三", 20, 9999.99),
    new Employee("李四", 21, 5555.55),
    new Employee("王五", 45, 4568.88),
    new Employee("赵六", 56, 6666.66),
    new Employee("田七",34 , 8888.88),
    new Employee("周一", 19, 9999.99),
    new Employee("吴用", 22, 6666.89),
    new Employee("孙十",22 , 7878.56));

2.1 流的筛选与切片

2.1.1 filter

通过接收一个lambda表达式,根据定义中间过滤条件过滤流中的一些数据。

比如,过滤年龄大于21的员工。

public  void filter()
{
    List<Employee> employees = emp();
    // 调用stream()方法创建一个流
    // 通过中间操作filter去过滤年龄大于21的员工
    // 并收集一个新的集合后打印。
    List<Employee> collect = employees.stream().filter(e -> e.age > 21).collect(Collectors.toList());
    collect.forEach(System.out::println);
}

练习(自己测试)

1. 过滤年龄大于21的并且薪水大于7000的员工。

2. 过滤年龄大于21的或者薪水大于7000的员工

2.1.2 limit

流的截断操作,使最后获取的数据不超过所指定数量。

比如,先过滤年龄大于21的并且薪水大于7000的员工,只取前两条数据。

public  void limit(){
    List<Employee> employees = emp();
    // 调用stream()方法创建一个流
    // 先过滤年龄大于21的并且薪水大于7000的员工,只取前两条数据。
    // 并收集一个新的集合后打印。
    List<Employee> collect = employees.stream()
            .filter(e -> e.age > 21 && e.salary>7000)
            .limit(2)
            .collect(Collectors.toList());
    collect.forEach(System.out::println);
}

只要取到对应数量的数据,就会发生短路,之后的数据便不会获取。

练习(自己测试)

1. 过滤年龄大于21的并且薪水大于7000的员工只获取前0条数据。

2. 过滤年龄大于21的并且薪水大于7000的员工只获取前10条数据。

2.1.3 skip

跳过前面n个数据,获取后面所有的数据,与limit刚好互补。

比如,过滤年龄大于21的员工。并跳过前2个数据获取后面的所有数据。

public  void skip(){
    List<Employee> employees = emp();
    // 调用stream()方法创建一个流
    // 过滤年龄大于21的员工。并跳过前2个数据获取后面的所有数据。
    // 并收集一个新的集合后打印。
    List<Employee> collect = employees.stream()
            .filter(e -> e.age > 21)
            .skip(2)
            .collect(Collectors.toList());
    collect.forEach(System.out::println);
}

练习(自己测试)

1. 过滤年龄大于21的并且薪水大于7000的员工跳过前0条数据。

2. 过滤年龄大于21的并且薪水大于7000的员工跳过前10条数据。

2.1.4 distinct

对重复的对象元素进行去重,它的原理是根据重写对象的hashCode方法与equals方法进行去重。

将之前的数据添加几个重复的数据:

// 案例数据
List<Employee> employees = Arrays.asList(
        new Employee("张三", 20, 9999.99),
        new Employee("李四", 21, 5555.55),
        new Employee("王五", 45, 4568.88),
        new Employee("赵六", 56, 6666.66),
        new Employee("田七",34 , 8888.88),
        new Employee("周一", 19, 9999.99),
        new Employee("吴用", 22, 6666.89),
        new Employee("吴用", 22, 6666.89),
        new Employee("吴用", 22, 6666.89),
        new Employee("吴用", 22, 6666.89),
        new Employee("孙十",22 , 7878.56));

然后重写员工的hashCode方法与equals方法。

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Employee employee = (Employee) o;
    return age == employee.age && Double.compare(employee.salary, salary) == 0 && Objects.equals(name, employee.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age, salary);
}

使用distinct方法进行元素去重:

public  void distinct(){
    List<Employee> employees = emp();
    // 调用stream()方法创建一个流
    // 过滤重复的元素。
    // 并收集一个新的集合后打印。
    List<Employee> collect = employees.stream()
            .distinct()
            .collect(Collectors.toList());
    collect.forEach(System.out::println);
}

这是进行对整个对象进行去重的,若对某个字段进行去重使用filter方法加上自定义方法

// 自定义方法
<T> Predicate<T>  distinctBykey(Function<? super T,?> key){
    Map<Object,Boolean> booleanMap = new ConcurrentHashMap<>();
    return t-> booleanMap.putIfAbsent(key.apply(t),Boolean.TRUE)==null;
}
public  void distinct(){
    List<Employee> employees = emp();
    // 调用stream()方法创建一个流
    // 过滤重复的薪水。
    // 并收集一个新的集合后打印。
    List<Employee> collect = employees.stream()
            .filter(distinctBykey(s->s.getSalary()))
            .collect(Collectors.toList());
    collect.forEach(System.out::println);
}

2.2 流的映射

2.2.1 map

接收一个lambda表达式,将对象中的元素转换成其它形式或者提取对象中的元素。

然后,接收一个函数作为参数,该函数会应用到每一个对象元素上,并映射成为新的对象元素。

比如,将对象中的所有英文元素转为大写。

@Test
public void test01(){
    // 构建一个集合
    List<String> strings = Arrays.asList("aaa","bbb","ccc","ddd");
    // 将集合中的元素进行大写
    List<String> collects = strings.stream().map(str -> str.toUpperCase()).collect(Collectors.toList());
    // 输出
    collects.forEach(System.out::println);
}

比如,将员工对象中的年龄抽取出来并去重。

@Test
public void test02(){
    List<Employee> employees = emp();
    // 将年龄提取出来,并去重。
    employees.stream().map(Employee::getAge)
            .distinct()
            .forEach(System.out::println);
}

2.2.2 flatMap

流的扁平化处理,接收一个函数作为参数,将流中的每一个值都换成另一个流,然后将所有的流连接成一个流。

首先我们看看之前的map操作

@Test
public void t4()
{
    // 构建集合
    List<String> list = Arrays.asList("hello","world");
    // map 将一个个流加入流
    List<String[]> collect = list.stream().map(s -> s.split("")).collect(Collectors.toList());
    collect.forEach(System.out::println);
}

看代码可以看出它返回的是一个集合,集合里面是一个数组,map操作是针对于单层式的操作,也就是说只能对外层进行处理。

所以,它输出的就是两个数组对象是这种格式的【[h,e,l,l,o],[w,o,r,l,d]】。

[h,e,l,l,o]作为一个流,[w,o,r,l,d]作为一个流,然后将两者按照流的方式逐个放入一个新流里。

如果要输出成这种格式的[h,e,l,l,o,w,o,r,l,d]就需要使用flatMap进行双层式的处理。

这种方式就是先将各个流拆成最小元素,然后将元素分别放入一个流中。

@Test
public void t4()
{
    List<String> list = Arrays.asList("hello","world");
    // 使用flapMap将流中的每一个值都换成另一个流,然后将所有的流连接成一个流。
    Stream<String> stringStream = list.stream().map(s -> s.split("")).flatMap(Arrays::stream);
    stringStream.forEach(System.out::println);
}

2.3 流的排序

2.3.1 sort

流的排序分为以下两种:

自然排序: 默认的字典方式的排序

@Test
public void t5()
{
    List<String> list = Arrays.asList("ccc","aaa","eee","ddd");
    // 默认字典排序
    list.stream().sorted().forEach(System.out::println);
}

自定义排序: 根据自己定义的规则排序

比如,若年龄一样,按照薪水排序,否则按照年龄排序。

@Test
public void t5()
{
    List<Employee> employees = emp();
    // 自定义排序
    employees.stream().sorted((e1,e2)->{
        // 若年龄一样,按照薪水排序
        if (e1.getAge()==e2.getAge()){
             return (int) (e1.getSalary()-(e2.getSalary()));
        // 否则按照年龄排序
        }else{
            return e1.getAge()-e2.getAge();
        }
    }).forEach(System.out::println);
}

三. 流的终止操作

3.1 流的查找与匹配

3.1.1 allMatch

用于检查是否匹配所有的元素,全部匹配成功返回true

现在将员工信息修改如下:

// 案例数据
List<Employee> employees = Arrays.asList(
    new Employee("张三", 22, 9999.99),
    new Employee("李四", 22, 5555.55),
    new Employee("王五", 22, 4568.88),
    new Employee("赵六", 22, 6666.66),
    new Employee("田七",22, 8888.88),
    new Employee("周一", 22, 9999.99),
    new Employee("吴用", 22, 6666.89),
    new Employee("吴用", 22, 6666.89),
    new Employee("吴用", 22, 6666.89),
    new Employee("吴用", 22, 6666.89),
    new Employee("孙十",22, 7878.56));
    return employees;

匹配年龄是否全部为22。

@Test
public void t7()
{
    List<Employee> employees = emp();
    // 匹配年龄是否全部为22
    boolean allMatch = employees.stream().allMatch(e -> e.age == 22);
    // 全部匹配到返回true
    System.out.println(allMatch);
}

3.1.2 anyMatch

用于检查元素是否匹配到流中的任意元素,匹配到一个就成功返回true

查找员工薪水是否有超过9000的。

@Test
public void t8()
{
    List<Employee> employees = emp();
    // 查找员工薪水是否有超过9000的
    boolean anyMatch = employees.stream().anyMatch(e -> e.salary > 9000);
    // 部分匹配到则返回true
    System.out.println(anyMatch);
}

3.1.3 noneMatch

全部没有匹配到元素返回true。

查找员工薪水是否有超过10000的。

@Test
public void t10()
{
    List<Employee> employees = emp();
    // 查找员工薪水是否有超过10000的
    boolean noneMatch = employees.stream().noneMatch(e -> e.salary > 10000);
    // 没有匹配到则返回true
    System.out.println(noneMatch);
}

3.1.3 findFirst与findAny

findFirst用于查找流中的第一个元素,对于元素处理的流是串行流。

findAny用于查找流中的任意一个元素,对于元素处理的流是并行流。

例如:查找员工薪水大于6000的并取第一个元素与查找员工薪水大于6000的并取任意一个元素

@Test
public void t11()
{
    List<Employee> employees = emp();
    // 查找员工薪水大于6000的并取第一个元素
    System.out.println(employees.stream().filter(e->e.getSalary()>6000).findFirst().get());
    // 查找员工薪水大于6000的并取任意一个元素
    System.out.println(employees.parallelStream().filter(e->e.getSalary()>6000).findAny().get());
}

3.1.4 count

统计流中的元素总个数。

例如,统计所有员工。

@Test
public void t12()
{
    List<Employee> employees = emp();
    // 统计流中的元素总个数
    long count = employees.stream().count();
    System.out.println(count);
}

3.1.5 max与min

max与min分别是获取流中的最大值与最小值。

@Test
public void t14()
{
    List<Employee> employees = emp();
    // 统计员工薪水最高的
    Optional<Employee> max = employees.stream().max(Comparator.comparingDouble(Employee::getSalary));
    System.out.println(max.get());
    // 统计员工薪水最低的
    Optional<Employee> min = employees.stream().min(Comparator.comparingDouble(Employee::getSalary));
    System.out.println(min.get());
}

3.2 归约与收集

3.2.1 归约(reduce)

可以将流中的元素反复结合起来,结成一个值。

例如,构建一个集合,求出它们的和。

@Test
public void t15()
{
    // 构建一个集合
    List<Integer> integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    // 进行求和
    Integer reduce = integers.stream().reduce(0, (x, y) -> x + y);
    System.out.println(reduce);
}

3.2.2 收集(collect)

集合收集

将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。

可以将最终的结果收集成一个List集合,Set集合等,也可以将得到的结果进行计算。

看下面这个例子:利用Collectors.toSet()进行去重。

@Test
public void test01(){
    // 构建一个集合
    List<String> strings = Arrays.asList("aaa","bbb","ccc","ddd","ddd");
    // 将集合中的元素进行大写
    Set<String> collects = strings.stream().map(str -> str.toUpperCase()).collect(Collectors.toSet());
    // 输出
    collects.forEach(System.out::println);
}

分组操作

接下来着重介绍一下 ,使用Collectors.groupingBy进行分组操作。

先准备一组数据:使用其分组操作将年龄进行分组。

    List<Employee> emp() {
// 案例数据
        List<Employee> employees = Arrays.asList(
                new Employee("张三", 22, 9999.99),
                new Employee("李四", 22, 5555.55),
                new Employee("王五", 23, 4568.88),
                new Employee("赵六", 23, 6666.66),
                new Employee("田七",24, 8888.88),
                new Employee("周一", 24, 9999.99),
                new Employee("吴用", 25, 6666.89),
                new Employee("吴用1", 25, 6666.89),
                new Employee("吴用2", 21, 6666.89),
                new Employee("吴用3", 21, 6666.89),
                new Employee("孙十",21, 7878.56));
        return employees;
    }
@Test
public void t17()
{
    List<Employee> employees = emp();
    // 返回一个map集合
    Map<Integer, List<Employee>> collect = employees.stream().collect(Collectors.groupingBy(Employee::getAge));
    System.out.println(collect);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智公网:2023年教师编必背30考点

1、制度化教育阶段开始于&#xff1a;近代。 2、各国的学校教育系统基本形成于&#xff1a;19世纪末。 3、现在世界上大多数国家的义务教育年限在&#xff1a;9年或9年以上。 4、“不愤不启&#xff0c;不悱不发”启发教学法的最早倡导者是&#xff1a;孔子。 5、“建国君民…

【Spring】Spring 6 新特性一一HTTP Interface

简介 Spring 6 的第一个 GA 版本发布了&#xff0c;其中带来了一个新的特性——HTTP Interface。 这个新特性&#xff0c;可以让开发者将 HTTP 服务&#xff0c;定义成一个包含特定注解标记的方法的 Java 接口&#xff0c;然后通过对接口方法的调用&#xff0c;完成 HTTP 请求…

硬盘损坏数据恢复怎么操作?恢复数据的常用方法

硬盘一般固定在电脑里面的存储装置&#xff0c;里面保存着我们大量的数据。随着电脑的使用越加广泛&#xff0c;有时不免出现一些问题&#xff0c;比如硬盘在使用过程中出现数据错误&#xff0c;或者是硬盘的内部零件出现故障。出现这些问题&#xff0c;硬盘损坏数据恢复怎么操…

Redis实现UV统计 | 黑马点评

一、HyperLogLog 1、为什么用HyperLogLog 先介绍两个概念&#xff1a; UV&#xff1a;全称 Unique Visitor&#xff0c;也叫独立访客量&#xff0c;是指通过互联网访问、浏览这个网页的自然人、1 天内同一个用户多次访问该网站&#xff0c;只记录 1 次。PV&#xff1a;全称 …

车载以太网 - SomeIP测试专栏 - 总纲

关于车载以太网中的SomeIP在网上也逐渐有越来越多的资料,讲的也是非常好;但是个人认为讲的泛,很难让初学者或者初入门者真正了解SomeIP到底是一个什么东西,以及它究竟在车载上有什么作用,本专栏会由浅入深的讲解SomeIP整个协议内容规范,并且对Tc8中SomeIP相关的协议测试用…

实习日记!

目录 http://localhost:5789实习第三天 接下来几天的target 实习第四天 Git的操作 实习第五天 12月5日-Mon 12月6日 12月9日 12月12日 12月15日 useState() hook 12月16日 useEffect() hook async 函数 异步编程 回调函数 12月17日 C#中的&#xff1f;&#x…

postgresql源码学习(54)—— HotStandby从库必须设置大于等于主库的参数

新的一篇本来计划研究lazy_scan_heap函数&#xff0c;但过于复杂还没研究出来… 下午做题遇到一个这样的问题&#xff0c;之前没太关注过&#xff0c;打算学习学习&#xff0c;避免主从配置踩坑。 题干搜一搜&#xff0c;没搜出啥有用的玩意…渣翻成英文搜一搜&#xff0c;搜出…

windows搭建go语言开发环境,IDEA安装go插件并运行Hello world代码

2023年1月27日1.Windows上安装Go语言开发包参考链接&#xff1a;http://c.biancheng.net/view/3992.html1.1.下载Go语言开发包可以在Go语言官网 &#xff08;https://golang.google.cn/dl/&#xff09; 下载Windows 系统下的Go语言开发包&#xff0c;如下图所示。这里我们下载的…

学习Docker就应该掌握的dockerfile语法与指令

在日常的工作中&#xff0c;常常需要制作自己的项目的镜像&#xff0c;一般通过以下两种方式制作镜像&#xff1a;Docker commit、Dockerfile。Docker commitDocker commit一般用做从一个运行状态的容器来创建一个新的镜像。定制镜像应该使用Dockerfile来完成。docker commit 容…

Git场景分析

当前的开发环境如下&#xff0c;我们每个人都对这个项目已经开发一段时间&#xff0c;接下来我们要切换成团队开发模式。 也就是我们由一个团队来完成这个项目实战的内容。团队有组长和若干组员组成(组长就是开发中的项目经理)。 练习场景如下&#xff1a; 1.由组长&#xff0…

LeetCode[1202]交换字符串中的元素

难度&#xff1a;中等题目&#xff1a;给你一个字符串 s&#xff0c;以及该字符串中的一些「索引对」数组 pairs&#xff0c;其中 pairs[i] [a, b]表示字符串中的两个索引&#xff08;编号从 0 开始&#xff09;。你可以 任意多次交换 在 pairs中任意一对索引处的字符。返回在…

java递归问题——汉诺塔

目录 &#x1f332;&#x1f332;什么是汉诺塔&#xff1f; &#x1f430; 当只有1个圆盘的时候&#xff1a; &#x1f430; 当只有2个圆盘的时候&#xff1a; &#x1f430; 当只有3个圆盘的时候&#xff1a; &#x1f332;&#x1f332;汉诺塔代码 &#x1f430;思路 &am…

电脑安全模式怎么进?3种方式教会你!

安全模式经常是电脑死机的时候&#xff0c;我们会选择的一种方式。因为安全模式可以帮助我们修复电脑系统里面的一些错误&#xff0c;电脑安全模式怎么进&#xff1f;其实很简单&#xff0c;主要有以下3种方式&#xff0c;你可以根据你的需要来选择其中一种&#xff01; 操作环…

剑指 Offer 第7天(中午睡起来都十二点了,今天摆了吧)

目录 剑指 Offer 26. 树的子结构 剑指 Offer 27. 二叉树的镜像 剑指 Offer 28. 对称的二叉树 剑指 Offer 26. 树的子结构 输入两棵二叉树A和B&#xff0c;判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构&#xff0c; 即 A中有出现和B相同的结构和节点…

极光厂商通道集成指南

小米集成指南 1、使用JCenter自动化集成步骤 确认AndroidStudio的Project根目录的主gradle中配置了jcenter支持。(新建project默认配置就支持&#xff09; buildscript { repositories { jcenter() } } allprojects {repositories { jcenter() } } 在应用module的gr…

ffmpeg为mkv封装格式的音视频文件添加内挂字幕

现在好莱坞的电影&#xff0c;都是全球看&#xff0c;一个地区的人看电影时&#xff0c;电影屏幕上应该展示对应的本地区语言字幕。故电影画面在不同的地区&#xff0c;需要配置不同的语言字幕。故视频画面里面的字幕应该可以拆出来&#xff0c;不能像老版三国演义&#xff0c;…

数据中心Spine/Leaf+VXLAN的结构

大家过年好&#xff0c;我是技福的小咖老师。今天我们继续聊聊网络架构。 随着业务系统对IT基础设备灵活度要求的不断提升&#xff0c;云计算、大数据以及虚拟化等技术在新型数据中心的建设中发挥着重要作用。如何更好地满足数据中心计算资源灵活调配以及服务扩展&#xff0c;…

C语言块级变量

所谓代码块&#xff0c;就是由{ }包围起来的代码。代码块在C语言中随处可见&#xff0c;例如函数体、选择结构、循环结构等。不包含代码块的C语言程序根本不能运行&#xff0c;即使最简单的C语言程序&#xff08;上节已经进行了展示&#xff09;也要包含代码块。C语言允许在代码…

不就是Java吗之 认识异常

认识异常一、异常的概念与体系结构1.1 异常的概念1.2 异常的体系结构1.3 异常的分类1.3.1 编译时异常(受查异常)1.3.2 运行时异常(非受查异常)二、异常的处理2.1 防御型编程2.1.1 LBYL2.1.2 EAFP2.2 异常的抛出2.3 异常的捕获2.3.1 异常的声明2.3.2 try-catch捕获并处理2.3.3 f…

linux gui版本控制工具对比

linux gui版本控制工具对比qgitungitgitgsublime mergegitKrakengitAhead tkdiff之前一直用windows开发, 最近想用linux开发, 选版本控制工具的时候考察了以下几款可以在linux平台上使用的版本控制工具, 还是觉得tortoise好用. 记录下它们的优缺点. 想起以前一位同事说过的话,…