JDK1.8 新特性(二)【Stream 流】

news2024/11/17 11:50:40

前言

        上节我们学了 lambda 表达式,很快我就在 Flink 的学习中用到了,我学的是 Java 版本的 Flink,一开始会以为代码会很复杂,但事实上 Flink 中很多地方都用到了 函数接口,这也让我们在编写 Flink 程序的时候可以使用 lambda 表达式非常地简洁地实现匿名函数。

        今天再来学习一个新的特性,Stream 流,光是看名字就觉得和大数据能扯上关系,我们的 Spark、Flink 当中不就都是这种流的概念嘛。

1、什么是 Strem 流

        Stream 是 JDK1.8 中处理集合的关键抽象概念, Lambda 表达式 和 Stream 是JDK1.8 新增的函数式编程中最有亮点的特性了,它可以指定你希望对集合进行操作,可以执行非常复杂的查询过滤和映射等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 来执行对 Java 集合运算和表达的高阶抽象。

        Stream API 可以极大地提高 Java 程序员的生产力,让程序员写出更加高效、干净、简洁的代码。那对我在大数据开发中更是如此。

        这种风格将要处理的元素集合看做一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如过滤、排序、聚合等。

2、Stream 创建方式

1、创建串行 Stream

Stream<User> userStream = list.stream();

2、创建并行 Stream

Stream<User> userStream = list.parallelStream();

3、关闭

在Java中,Stream只能被操作一次,一旦你对其进行了一次操作(比如forEach, collect等),它就会被关闭,再次操作就会报错:stream has already been operated upon or closed。

3、Stream 将 List 转换为 Set

1、创建 List 集合

Stream 是通过集合创建出来的,所以我们先创建一个集合,而集合内我们需要存放实体,所以先创建一个实体类 User:

public class User {
    public String name;
    public int age;

    public User(){}

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }


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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

创建集合

List<User> list = new ArrayList<>();
        list.add(new User("燕双鹰",28));
        list.add(new User("李大喜",20));
        list.add(new User("李元芳", 30));
        list.add(new User("李元芳", 30));

重写 equals

注意:这里我们对实体类的 equals 和 hashcode 方法进行了重写,这在之前我是不会去重写的。重写和不重写的区别就是:
        重写后,当两个实体对象的属性相同时,equals 方法返回 true,如果没有重写,则 equals 返回 false。

== 和 equals

        == 用于比较基本数据类型的值是否相等或者对象的引用地址是否相同。

int a = 10;
int b = 10;
System.out.println(a==b);    //true

String str1 = "hello";
String str2 = "hello";
System.out.println(str1==str2);    //false

equals 用于比较两个对象的内容是否相等。在Object类中,默认的“equals()”实现使用“==”操作符比较对象的引用。但是,许多类(如String、Integer等)重写了“equals()”方法,以便根据类的特定属性比较对象的内容。

set 去重底层原理

set 去重底层依赖于 map 集合实现放重复的 key,map 集合底层基于 equals ,它先比较 key 的hashcode 是否相同,相同情况下再调用 equals 方法判断是否真的相等。

所以一个实体类是否重写 equals 方法区别很大。

User u1 = new User("s",1);
        User u2 = new User("s",1);
        System.out.println(u1.equals(u2));

上面的代码,如果我们以 User 对象作为 key,如果我们的 User 没有重写 equals 方法,那么返回的就是 false,因为默认使用 == ,引用地址不同;如果重写了 equals 方法,那么返回的就是 true,因为使用重写后的 equals ,两个对象属性相同返回 true。

注意:对象的比较不会去比较 hashcode。 

HashMap<User, String> map = new HashMap<>();
        map.put(u1,"a");
        map.put(u2,"b");
        System.out.println(map.get(u1).equals(map.get(u2)));

上面的代码,如果我们没有重写 hashcode 的情况下,那么返回的就是 true,因为 map 的底层是通过 hashcode 来比较两个 key 是否相同;如果重写了 hashcode ,那么返回的就是 true。

2、List 转为 Set

public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        list.add(new User("燕双鹰",28));
        list.add(new User("李大喜",20));
        // 下面是两个属性相同的两个对象(我们已经重写了 equals 和 hashcode 方法)
        list.add(new User("李元芳", 30));
        list.add(new User("李元芳", 30));

        // todo 创建 Stream 的两种方式
        // 1. 串行流 stream() 单线程
        Stream<User> stream = list.stream();
        Set<User> set = stream.collect(Collectors.toSet());
        set.forEach(user->{
            System.out.println(user.toString());
        });
}

运行结果:

User{name='李元芳', age=30}
User{name='燕双鹰', age=28}
User{name='李大喜', age=20}

可以看到,重写 equals 和 hashcode 方法后,虽然相同属性的两个对象的内存地址不同,但也被去除重复了。

4、Stream 将 List 转为 Map

1、创建 List

注意:List 转为 Map 的时候,由于 Map 集合不允许存在重复的 key,所以我们必须保证 list 集合中作为 key 字段的属性值唯一。

List<User> list = new ArrayList<>();
        list.add(new User("燕双鹰",28));
        list.add(new User("李大喜",20));
        list.add(new User("李元芳", 30));

 2、List 转为 Map

 Stream<User> stream = list.stream();
        // list 集合是没有 key 的,所以不能直接转为 map 集合,需要指定 key(指定对象的某个字段作为key)
        Map<String, User> collect = stream.collect(Collectors.toMap(new Function<User, String>() {  // 第一个参数 list中的类型,第二个参数是key类型: String
            @Override
            public String apply(User user) {
                return user.getName();
            }
        }, new Function<User, User>() { // 第一个参数 list中的类型,第二个参数是value类型: User
            @Override
            public User apply(User user) {
                return user;
            }
        }));

        collect.forEach(new BiConsumer<String, User>() {
            @Override
            public void accept(String key, User user) {
                System.out.println(key+","+user.toString());
            }
        }); 

使用 lambda 表达式简化一下代码:

        // 用lambda表达式
        Map<String, User> collect = stream.collect(Collectors.toMap(User::getName, user -> user));
        collect.forEach((key,user)-> System.out.println(key+","+user.toString()));

运行结果:

李元芳,User{name='李元芳', age=30}
李大喜,User{name='李大喜', age=20}
燕双鹰,User{name='燕双鹰', age=28}

5、Strem 通过 reduce 方法求和

1、简单求和

这里我们通过 Stream.of() 方法来进行数据的构造(这让我想到了最近 Flink)。

Stream<Integer> stream = Stream.of(10, 50, 30, 10);
        Optional<Integer> res = stream.reduce(new BinaryOperator<Integer>() {
            @Override
            public Integer apply(Integer integer, Integer integer2) {
                return integer + integer2;
            }
        });

使用 lamda 表达式 

 Optional<Integer> res = stream.reduce(Integer::sum);

关于结果的打印,我们后面讲到 Optional 类的时候再详细说,一般直接:

System.out.println(res.get());

2、对象属性和

我们构造一个 List 集合,然后转为 Stream 调用 reduce 方法进行求和。

注意:reduce 方法的返回结果类型必须和 Stream 的类型一致(就像我们 Hadoop 中的 WordCount)。

List<User> list = new ArrayList<>();
        list.add(new User("燕双鹰",28));
        list.add(new User("李大喜",20));
        list.add(new User("李元芳", 30));

        Stream<User> stream = list.stream();
        Optional<User> sum = stream.reduce(new BinaryOperator<User>() {
            @Override
            public User apply(User user1, User user2) {
                return new User("sum",user1.getAge()+ user2.getAge());
            }
        });

        System.out.println(sum.get());

lambda 表达式简化:

        Stream<User> stream = list.stream();
        Optional<User> sum = stream.reduce(((user1, user2) -> new User("sum", user1.getAge() + user2.getAge())));
        System.out.println(sum.get());//78

6、Strem 查找集合最大值和最小值

1、创建集合

List<User> list = new ArrayList<>();
list.add(new User("燕双鹰",28));
list.add(new User("李大喜",20));
list.add(new User("李元芳", 30));

2、查找最大 age 属性对象

Optional<User> max = stream.max(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        System.out.println(max.get());

lambda表达式简化:

Optional<User> max = stream.max((user1, user2) -> user1.getAge() - user2.getAge());
        System.out.println(max.get());    //30

3、查找最小 age 属性对象

Optional<User> min = stream.min((user1, user2) -> user1.getAge() - user2.getAge());
        System.out.println(min.get());    //20

7、Stream 中 Match 用法

anyMatch 表示,任意一个元素满足条件返回 true。

allMatch 表示,所有元素满足条件才会返回 true。

noMatch 表示,所有条件都不满足这个条件才会返回 true。

1、创建集合

List<User> list = new ArrayList<>();
        list.add(new User("燕双鹰",28));
        list.add(new User("李大喜",20));
        list.add(new User("李元芳", 30));
        Stream<User> stream = list.stream();

2、anyMatch

判断集合中是否存在 age 属性大于 25 的对象。

 boolean res = stream.anyMatch(new Predicate<User>() {
            @Override
            public boolean test(User user) {
                return user.getAge() > 25;
            }
        });
        System.out.println(res);

 lambda 表达式:

boolean res = stream.anyMatch(user -> user.getAge() > 25);
        System.out.println(res);    //true

3、allMatch

判断是否所有对象的 age属性都大于 30

boolean res = stream.allMatch(user -> user.getAge() > 30);
        System.out.println(res);    //false

4、noMatch

判断是否用户都不满足 name 为 “光头强 ”

boolean res = stream.noneMatch(user -> user.getName().equals("光头强"));
        System.out.println(res); //true

8、Stream 过滤器

和我们 Flink 的 DataStream API 中的转换算子 filter 很像,它们都是把 判断条件结果为 true 的数据留下,false 则丢掉。

1、创建集合

List<User> list = new ArrayList<>();
        list.add(new User("燕双鹰",28));
        list.add(new User("李大喜",20));
        list.add(new User("李元芳", 30));
        Stream<User> stream = list.stream();

2、过滤

留下 年龄>25 岁的 User 对象。
Stream<User> filterStream = stream.filter(new Predicate<User>() {
            @Override
            public boolean test(User user) {    //为 true 则留下
                return user.getAge()>25;
            }
        });

        filterStream.forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
            }
        });

运行结果: 

User{name='燕双鹰', age=28}
User{name='李元芳', age=30}

lambda表达式:

Stream<User> filterStream = stream.filter(user -> user.getAge() > 25);
        filterStream.forEach(System.out::println);

9、Stream Limit 和 Skip

同样,Stream 需要通过集合来创建。

List<User> list = new ArrayList<>();
list.add(new User("燕双鹰",28));
list.add(new User("李大喜",20));
list.add(new User("李元芳", 30));
list.add(new User("熊大",15));
list.add(new User("熊二",14));
list.add(new User("光头强",20));
Stream<User> stream = list.stream();

1、取出前2条数据

// 在mysql中limit(start,end)需要传两个参数,但在这里只允许传入一个long类型的 maxSize
        // 取前2条数据
        stream.limit(2).forEach(System.out::println);

运行结果:

User{name='燕双鹰', age=28}
User{name='李大喜', age=20}

 

2、取出第 [3,6) 条数据

注意,这里的索引是从 0 开始的。

// 取 [3,6)条数据 想要分页从先 skip 再 limit
stream.skip(2).limit(3).forEach(System.out::println);

运行结果:
 

User{name='李元芳', age=30}
User{name='熊大', age=15}
User{name='熊二', age=14}

10、Stream 排序 sorted

下面用到的数据。

List<User> list = new ArrayList<>();
list.add(new User("燕双鹰",28));
list.add(new User("李大喜",20));
list.add(new User("李元芳", 30));
list.add(new User("熊大",15));
list.add(new User("熊二",14));
list.add(new User("光头强",20));
Stream<User> stream = list.stream();

1、直接排序

对于数值型的数据可以直接进行排序

Stream<Integer> integerStream = Stream.of(1, 5, 8, 3, 7);
integerStream.sorted().forEach(System.out::println);    //1 3 5 7 8

2、根据对象字段进行升序

stream.sorted(new Comparator<User>() {
    @Override
    public int compare(User o1, User o2) {
        return o1.getAge()-o2.getAge();
    }
}).forEach(System.out::println);

lambda 表达式:

stream.sorted((o1, o2) -> o1.getAge()-o2.getAge()).forEach(System.out::println);

运行结果: 

User{name='熊二', age=14}
User{name='熊大', age=15}
User{name='李大喜', age=20}
User{name='光头强', age=20}
User{name='燕双鹰', age=28}
User{name='李元芳', age=30}

JDK1.8 提供的函数接口

都在包 java.util.function 包下。

并行流

案例1 - 500亿次求和

1、使用单线程

Instant start = Instant.now();
        long sum = 0;
        for (long i = 0; i <= 50000000000L; i++) {
            sum+=i;
        }
        Instant end = Instant.now();
        System.out.println(sum);
        System.out.println("500亿次求和花费时间: "+ Duration.between(start,end).toMillis()+"ms");   // 单线程 11s左右  多线程 6s左右

2、使用并行流

Instant start = Instant.now();
        LongStream longStream = LongStream.rangeClosed(0,50000000000L);
        OptionalLong result = longStream.parallel().reduce(new LongBinaryOperator() {
            @Override
            public long applyAsLong(long left, long right) {
                return left + right;
            }
        });
        Instant end = Instant.now();
        System.out.println(result.getAsLong());
        System.out.println("500亿次求和花费时间: "+ Duration.between(start,end).toMillis()+"ms");   // 单线程 11s左右  多线程 6s左右

可以发现,多线程明显要快很多。

总结

        本次学习收获非常大,函数接口的思想在 Flink 中随处可见,的确,这样一种能够使得代码简洁高效的技术在大数据开发中是非常重要的。

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

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

相关文章

upload-labs关卡9(基于win特性data流绕过)通关思路

文章目录 前言一、靶场需要了解的知识1::$data是什么 二、靶场第九关通关思路1、看源码2、bp抓包修改后缀名3、检查是否成功上传 总结 前言 此文章只用于学习和反思巩固文件上传漏洞知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;不能随意去尚未授…

大模型之十二十-中英双语开源大语言模型选型

从ChatGPT火爆出圈到现在纷纷开源的大语言模型&#xff0c;众多出入门的学习者以及跃跃欲试的公司不得不面临的是开源大语言模型的选型问题。 基于开源商业许可的开源大语言模型可以极大的节省成本和加速业务迭代。 当前&#xff08;2023年11月17日)开源的大语言模型如下&#…

Devart dotConnect ADO.NET Data Providers Crack

开发数据相关 .NET 应用程序的终极解决方案&#xff1a;快速、灵活、全面、功能丰富、支持 ORM 的 ADO.NET 提供程序 概述 实体框架 连接字符串 博客 高性能 ADO.NET 数据提供程序 dotConnect 是基于 ADO.NET 架构和采用多项创新技术的开发框架构建的增强型数据连接解决方​​…

Pandas 求平均值

Pandas是Python中最流行的数据分析库之一&#xff0c;它提供了许多强大的工具来处理和分析数据集。其中&#xff0c;求平均值是数据分析中最常见的操作之一。在本文中&#xff0c;我们将从多个角度分析Pandas中如何求平均值。 一、基础操作 Pandas中求平均值的基础操作是使用m…

第十一周任务总结

本周任务总结 本周物联网方面主要继续进行网关的二次开发与规则引擎实现设备联动的实现 非物联网方面主要复习了docker的使用与算法的学习 1.网关的二次开发&#xff0c;本周将实现debug调试输出的文件下载到了网关&#xff0c;但网关出了问题无法连接&#xff0c;最终跟客服…

Elasticsearch:通过摄取管道加上嵌套向量对大型文档进行分块轻松地实现段落搜索

作者&#xff1a;VECTOR SEARCH 向量搜索是一种基于含义而不是精确或不精确的 token 匹配技术来搜索数据的强大方法。 然而&#xff0c;强大的向量搜索的文本嵌入模型只能按几个句子的顺序处理短文本段落&#xff0c;而不是可以处理任意大量文本的基于 BM25 的技术。 现在&…

Redis 访问控制列表(ACL)

Redis ACL 关于 Redis ACL与旧版本兼容ACL helpACL 配置模式redis.conf 配置模式外部 ACL File 配置模式 ACL 规则总结 关于 Redis ACL Redis ACL (访问控制列表) 是 Access Control List 的缩写&#xff0c;它允许某些连接在可以执行的命令和可以访问的密钥方面受到限制。它…

HTTP1.0协议详解

前言主要特点存在的不足与HTTP1.1的区别在Java中应用HTTP1.0协议知识拓展 前言 HTTP是由蒂姆伯纳斯李&#xff08;Tim Berners-Lee&#xff09;爵士创造的。他在1989年提出了一个构想&#xff0c;借助多文档之间相互关联形成的超文本&#xff08;HyperText&#xff09;&#x…

SQL Server如何建表

一、数据表的组成 实现完整性的约束有&#xff1a; –6个约束 –非空 not null –主键 primary key –唯一 unique –检查 check –默认 default –主键自增 identity 表约束 主键约束&#xff1a;值不能为null,且不能重复 非空约束&#xff1a;不能为null 默认约束&#xf…

6. hdfs的命令操作

简介 本文主要介绍hdfs通过命令行操作文件 操作文件有几种方式&#xff0c;看个人习惯 hdfs dfs hdfs fs hadoop fs个人习惯使用 hadoop fs 可操作任何对象&#xff0c;命令基本上跟linux命令一样 Usage [hadoophadoop01 ~]$ hadoop fs Usage: hadoop fs [generic option…

C++初级项目-webserver(1)

1.引言 Web服务器是一个基于Linux的简单的服务器程序&#xff0c;其主要功能是接收HTTP请求并发送HTTP响应&#xff0c;从而使客户端能够访问网站上的内容。本项目旨在使用C语言&#xff0c;基于epoll模型实现一个简单的Web服务器。选择epoll模型是为了高效地处理大量并发连接…

CF1899A Game with Integers(思维题)

题目链接 题目 题目大意 t 组测试样例 每组给一个正整数 n&#xff0c; 有两种操作&#xff1a; 1-1 A 和 B 轮流操作&#xff0c; 如果这个整数变成了一个能被3整除的数&#xff0c;A赢&#xff0c;输出First 如果在10次操作以内&#xff0c;n不能被3整数&#xff0c;B赢&…

Windows Server2012 R2修复SSL/TLS漏洞(CVE-2016-2183)

漏洞描述 CVE-2016-2183 是一个TLS加密套件缺陷&#xff0c;存在于OpenSSL库中。该缺陷在于使用了弱随机数生成器&#xff0c;攻击者可以利用此缺陷预测随机数的值&#xff0c;从而成功绕过SSL/TLS连接的加密措施&#xff0c;实现中间人攻击。这个漏洞影响了OpenSSL 1.0.2版本…

jenkins清理缓存命令

def jobName "yi-cloud-operation" //删除的项目名称 def maxNumber 300 // 保留的最小编号&#xff0c;意味着小于该编号的构建都将被删除 Jenkins.instance.getItemByFullName(jobName).builds.findAll { it.number < maxNumber }.each { it.delet…

python django 小程序点餐源码

开发工具&#xff1a; PyCharm mysql5.7&#xff0c;微信开发者工具 技术说明&#xff1a; python django html 微信小程序 代码注释齐全&#xff0c;没有多余代码&#xff0c;适合学习(毕设)&#xff0c;二次开发&#xff0c;包含论文技术相关文档。 功能介绍&#xff1a…

MySQL优化(1):B+树与索引

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 对于60%的程序员而言&a…

ER 图是什么

文章目录 前言什么是 ER图ER 图实例简化的 ER 图总结 前言 产品经理在梳理产业业务逻辑的过程中&#xff0c;非常重要的一项工作就是梳理各个业务对象之间的关系。如果涉及对象很对的时候&#xff0c;没有工具支持的话很难处理清楚。今天我们就来介绍一个梳理业务对象关系的工…

数据结构与集合源码

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷F

1、常见的加密算法可以分为对称加密算法和非对称加密算法&#xff0c;以下关于它们的描述正确的是 A、AES是一种常见的非对称加密算法 B、凯撒密码是一种非对称加密 C、非对称加密算法的解密使用的秘钥与加密不同 D、对称加密算法无法被暴力破解 答案&#xff1a;C 2、12根…

ESP32 MicroPython UART及小车类构造函数实验⑥

ESP32 MicroPython UART及小车类构造函数实验⑥ 1、实验目的2、实验内容3、参考代码4、实验结果 1、实验目的 控制小车动起来 2、实验内容 控制小车的前进、后退、左转、右转。读取小车 使用到的串口构造函数&#xff1a; uartmachine.UART(id,baudrate,rx,tx)uart:返回的构…