Java 8 新特性之Stream API

1. 概述

1.1 简介

Java 8 中有两大最为重要的改革,第一个是 Lambda 表达式,另外一个则是 Stream API(java.util.stream.*)。

Stream 是 Java 8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.2 流(Stream)到底是什么呢?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算”

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如 filter, map, reduce, find, match, sorted 等。

和以前的 Collection 操作不同, Stream 操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过 Iterator 或者 For-Each 的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

注意 :

  1. Stream 自己不会存储元素
  2. Stream 不会改变源对象,相反,它们会返回一个持有结果的新 Stream
  3. Stream 操作时延迟执行的。这意味着它们会等到需要结果的时候才执行

1.3 Stream 操作的三个步骤

  1. 创建 Stream

    • 一个数据源(集合、数组等),获取一个流
  2. 中间操作(聚合操作)

    • 一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作)

    • 一个终止操作,执行中间操作链,并产生结果

Java Stream

2. 创建 Stream(流)

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。

创建 Stream 的 5 种方式

@Test
public void t1() {// 1. Collection 提供了两个方法  stream() 与 parallelStream()List<String> list = new ArrayList<>();Stream<String> stream = list.stream(); //获取一个顺序流Stream<String> parallelStream = list.parallelStream(); //获取一个并行流// 2. 通过 Arrays 中的 stream() 获取一个数组流Integer[] nums = new Integer[10];Stream<Integer> stream1 = Arrays.stream(nums);// 3. 通过 Stream 类中静态方法 of()Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);// 4. 创建无限流 - 迭代Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(20);stream3.forEach(System.out::println);// 5. 创建无限流 - 生成Stream<Double> stream4 = Stream.generate(Math::random).limit(5);stream4.forEach(System.out::println);
}

3. Stream 的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而是在终止操作时一次性全部处理,称为“惰性求值”

提供基础的操作数据

List<Employee> emps = Arrays.asList(new Employee(1, "a1", 28, 3888.99),new Employee(2, "a2", 49, 336.66),new Employee(3, "a3", 18, 3323.33),new Employee(4, "a4", 38, 6666.77),new Employee(5, "a5", 8, 80.88),new Employee(5, "a5", 8, 80.88),new Employee(5, "a5", 8, 80.88),new Employee(6, "a6", 56, 100.66)
);

3.1 筛选与切片

  • filter 接收Lambda,从流中排除某些元素。
  • limit 截断流,使元素不超过给定数量
  • skip(n) 跳过元素,返回一个扔掉了前 n 个元素的流,若流中元素不足 n 个,则返回一个空流,与 limit 互补
  • distinct 筛选去重,通过流所生成元素的 hashCode()equals() 去除重复元素

1. filter 接收Lambda,从流中排除某些元素

@Test
public void t2() {// 中间操作:不会执行任何操作Stream<Employee> stream = emps.stream().filter((e) -> {System.out.println("中间操作");return e.getAge() > 20;});// 终止操作:一次性执行全部内容,即"惰性求值"stream.forEach(System.out::println);}

2. limit 截断流

@Test
public void t3() {emps.stream().filter((e) -> {// 当达到 limit 为 2 时将不继续遍历,称为短路,以提高效率System.out.println("短路");return e.getSalary() > 3000;}).limit(2).forEach(System.out::println);
}

3. skip 跳过元素

@Test
public void t4() {emps.stream().filter(e -> e.getSalary() > 100).skip(2).forEach(System.out::println);
}

4. distinct 筛选

@Test
public void t5() {emps.stream().distinct().forEach(System.out::println);
}

要使用 distinct 需要重写 EmployeehashCode()equals() 方法

@Override
public int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + id;result = prime * result + ((name == null) ? 0 : name.hashCode());long temp;temp = Double.doubleToLongBits(salary);result = prime * result + (int) (temp ^ (temp >>> 32));return result;
}@Override
public boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Employee other = (Employee) obj;if (age != other.age)return false;if (id != other.id)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))return false;return true;
}

3.2 映射

  • map 接收 Lambda,将元素转换成其它形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
  • flatMap 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

1. map

将原有的元素进过函数处理,让后映射(覆盖)成一个新的元素

@Test
public void t6() {List<String> list = Arrays.asList("aa","bb","cc","dd");list.stream().map((s) -> s.toUpperCase()).forEach(System.out::println);System.out.println("------------------------------------");emps.stream().map(Employee::getName).forEach(System.out::println);
}

2. flatMap

基础方法

/*** 将字符串分解成字符 list,并返回 Stream* * @param str 待分解字符串* @return Stream*/
public static Stream<Character> filterCharacter(String str) {List<Character> list = new ArrayList<>();for (Character ch : str.toCharArray()) {list.add(ch);}return list.stream();
}

正常情况下,当 filterCharacter 返回的也是一个 Stream 时,相当于流里面还有子流,接收的结果就是 Stream<Stream<Character>>,如果我们要进行遍历的话,就需要使用两层 forEach 才能遍历完成。

@Test
public void t7() {List<String> list = Arrays.asList("aa","bb","cc","dd");Stream<Stream<Character>> stream = list.stream().map(StreamTest1::filterCharacter);// 因为 Stream 还是 Stream 所以需要嵌套 forEach 才能进行遍历stream.forEach((sm) -> {sm.forEach(System.out::println);});
}

但如果使用 flatMap 就可以将每个子流都合并成一个流,这样遍历的时候只使用一层 forEach 就可以了

@Test
public void t8() {List<String> list = Arrays.asList("aa","bb","cc","dd");Stream<Character> stream = list.stream().flatMap(StreamTest1::filterCharacter);stream.forEach(System.out::println);
}

3.3 排序

  • sorted 自然排序(Comparable)
  • sorted(Comparator com) 定制排序(Comparator)

1. sorted 自然排序

@Test
public void t9() {List<String> list = Arrays.asList("cc","aa","dd","bb");list.stream().sorted().forEach(System.out::println);
}

2. sorted(Comparator com) 定制排序

@Test
public void t10() {emps.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println);
}

4. Stream 终止操作

4.1 查找与匹配

  • allMatch 检查是否匹配所有元素
  • anyMatch 检查是否至少匹配一个元素
  • noneMatch 检查是否没有匹配的元素
  • findFirst 返回第一个元素
  • findAny 返回当前流中的任意元素
  • count 返回流中元素的总个数
  • max 返回流中最大值
  • min 返回流中最小值

基础数据

List<Employee> emps = Arrays.asList(new Employee(1, "a1", 28, 3888.99, Employee.Status.BUSY),new Employee(2, "a2", 49, 336.66, Employee.Status.FREE),new Employee(3, "a3", 18, 3323.33, Employee.Status.VOCATION),new Employee(4, "a4", 38, 6666.77, Employee.Status.FREE),new Employee(5, "a5", 8, 80.88, Employee.Status.VOCATION),new Employee(6, "a6", 56, 100.66, Employee.Status.BUSY)
);

1. allMatch 检查是否匹配所有元素

@Test
public void t1() {boolean bool = emps.stream().allMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

2. anyMatch 检查是否至少匹配一个元素

@Test
public void t2() {boolean bool = emps.stream().anyMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

3. noneMatch 检查是否没有匹配的元素

@Test
public void t3() {boolean bool = emps.stream().noneMatch((e) -> e.getStatus().equals(Employee.Status.BUSY));System.out.println(bool);
}

4. findFirst 返回第一个元素

@Test
public void t4() {Optional<Employee> op = emps.stream().sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();System.out.println(op.get());
}

5. findAny 返回当前流中的任意元素

@Test
public void t5() {Optional<Employee> op = emps.stream().filter((e) -> e.getStatus().equals(Employee.Status.FREE)).findAny();System.out.println(op.get());
}

6. count 返回流中元素的总个数

/*** 查询空闲人数*/
@Test
public void t6() {Long count = emps.stream().filter((e) -> e.getStatus().equals(Employee.Status.FREE)).count();System.out.println("count : " + count);
}

7. max 返回流中最大值

/*** 查询工资最高的人*/
@Test
public void t7() {Optional<Employee> op = emps.stream().max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));System.out.println(op.get());
}

8. min 返回流中最小值

/*** 获取工资最少的人的工资*/
@Test
public void t8() {Optional<Double> op = emps.stream().map(Employee::getSalary).min(Double::compare);System.out.println(op.get());
}

4.2 规约(reduce)

  • T reduce(T identity, BinaryOperator<T> accumulator) 可以将流中的元素反复结合起来,得到一个值,返回 T
  • Optional<T> reduce(BinaryOperator<T> accumulator) 可以将流中的元素反复结合起来,得到一个值,返回 Optional<T>
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名

1. 实例

@Test
public void t9() {List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);Integer sum = list.stream().reduce(0, (x, y) -> x + y);System.out.println("sum : " + sum);
}
说明 :
首先将起始值 0 给 x,然后在流中取出一个元素 1 给了 y,然后 x y 相加结果为 1,再赋给 x,然后再取出一个元素 2 赋给y,然后 x y 相加结果为 3,以此类推

2. 实例

/*** 计算所有人工资的总和*/
@Test
public void t10() {Optional<Double> op = emps.stream().map(Employee::getSalary).reduce(Double::sum);System.out.println("Salary Sum : " + op.get());
}

4.3 收集(collect)

  • collect 将流转换为其它形式,接收一个 Collector(收集器) 接口的实现,用于给 Stream 中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是 Collector 实现类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

1. 实例 - 将结果收集到 List、Set 等容器

@Test
public void t1() {List<String> list = emps.stream().map(Employee::getName).collect(Collectors.toList());list.forEach(System.out::println);System.out.println("------------------------------------------");Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());set.forEach(System.out::println);System.out.println("------------------------------------------");HashSet<String> hs = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));hs.forEach(System.out::println);
}

2. 实例 - 计算

@Test
public void t2() {Long count = emps.stream().collect(Collectors.counting());System.out.println("总数 : " + count);System.out.println("------------------------------------------");Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));System.out.println("工资平均值 : " + avg);System.out.println("------------------------------------------");Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));System.out.println("工资总和 : " + sum);System.out.println("------------------------------------------");Optional<Employee> max = emps.stream().collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));System.out.println("工资最多的员工 : " + max.get());System.out.println("------------------------------------------");Optional<Double> min = emps.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));System.out.println("工资最少的员工 : " + min.get());}

3. 实例 - 计算的另一种实现方式

@Test
public void t6() {DoubleSummaryStatistics dss = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));System.out.println("sum : " + dss.getSum());System.out.println("max : " + dss.getMax());System.out.println("avg : " + dss.getAverage());System.out.println("count : " + dss.getCount());System.out.println("min : " + dss.getMin());
}

4.4 分组

分组就相当于 SQL 语句中的 group by,按一个类别或多个类别进行分组

1. 实例

@Test
public void t3() {Map<Employee.Status, List<Employee>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus));// 格式化输出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

2. 实例 多级分组

 @Test
public void t4() {Map<Employee.Status, Map<String, List<Employee>>> map = emps.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {if (e.getAge() <= 35) {return "青年";} else if (e.getAge() <= 50) {return "中年";} else {return "老年";}})));// 格式化输出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

4.5 分区

分区是一种特殊的分组,结果 map 至少包含两个不同的分组一个true,一个false

@Test
public void t5() {Map<Boolean,List<Employee>> map = emps.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() > 1000));// 格式化输出,方便查看Gson gson = new GsonBuilder().setPrettyPrinting().create();System.out.println(gson.toJson(map));
}

4.6 连接

将结果进行连接

@Test
public void t8() {String s1 = emps.stream().map(Employee::getName).collect(Collectors.joining());System.out.println("连接 : " + s1);String s2 = emps.stream().map(Employee::getName).collect(Collectors.joining(","));System.out.println("添加中间分隔符 : " + s2);String s3 = emps.stream().map(Employee::getName).collect(Collectors.joining(",", "==", "=="));System.out.println("添加左右分隔符 : " + s3);
}
本文首发于凌风博客:Java 8 新特性之Stream API
作者:凌风

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

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

相关文章

Ubuntu中NS2安装详细教程

前言&#xff1a; NS2是指 Network Simulator version 2&#xff0c;NS&#xff08;Network Simulator&#xff09; 是一种针对网络技术的源代码公开的、免费的软件模拟平台&#xff0c;研究人员使用它可以很容易的进行网络技术的开发&#xff0c;而且发展到今天&#xff0c;它…

14.vue路由脚手架

一.vue路由&#xff1a;https://router.vuejs.org/zh/ 1、定义 let router new VueRouter({mode:"history/hash",base:"基本路径" 加一些前缀 必须在history模式下有效linkActiveClass:"active", 范围选择linkExactActiveClass:"exact&qu…

linux-buff/cache过大导致内存不足-程序异常

2019独角兽企业重金招聘Python工程师标准>>> 问题描述 Linux内存使用量超过阈值&#xff0c;使得Java应用程序无可用内存&#xff0c;最终导致程序崩溃。即使在程序没有挂掉时把程序停掉&#xff0c;系统内存也不会被释放。 找原因的过程 这个问题已经困扰我好几个月…

Android 适配(一)

一、Android适配基础参数1.常见分辨率&#xff08;px&#xff09;oppx 2340x1080oppR15 2280x1080oppor11sp 2160*10801080*1920 (主流屏幕16&#xff1a;9)1080*216018:9 手机主流分辨率&#xff1a; 1080*2160高端 16:9 手机主流分辨率&#xff1a; 1080P (1080*1920) 或 2K …

Source Insight 创建工程(linux-2.6.22.6内核源码)

1. 软件设置 安装完Source Insight&#xff0c;需要对其进行设置添加对“.S”汇编文件的支持&#xff1a; 2. 新建linux-2.6.22.6工程 1&#xff09;选择工程存放的路径&#xff1a; 2&#xff09;下载linux-2.6.22.6内核源码&#xff0c;并解压。在Source Insight中 指定源码的…

课时20:内嵌函数和闭包

目录&#xff1a; 一、global关键字 二、内嵌函数 三、闭包 四、课时20课后习题及答案 ******************** 一、global关键字 ******************** 全局变量的作用域是整个模块&#xff08;整个代码段&#xff09;&#xff0c;也就是代码段内所有的函数内部都可以访问到全局…

盛严谨,严谨,再严谨。_评估员工调查的统计严谨性

盛严谨,严谨,再严谨。The human resources industry relies heavily on a wide range of assessments to support its functions. In fact, to ensure unbiased and fair hiring practices the US department of labor maintains a set of guidelines (Uniform Guidelines) to …

开根号的笔算算法图解_一个数的开根号怎么计算

一个数的开根号怎么计算2020-11-08 15:46:47文/钟诗贺带根号的式子可以直接进行开平方的运算。一些特殊的根号运算有;√2≈1.414、1/2-√3≈0.5-1.732≈-1.232、2√5≈22.236≈4.236、√7-√6≈2.646-2.449≈0.197。开平方的笔算方法1&#xff0e;将被开方数的整数部分从个位起…

arima 预测模型_预测未来:学习使用Arima模型进行预测

arima 预测模型XTS对象 (XTS Objects) If you’re not using XTS objects to perform your forecasting in R, then you are likely missing out! The major benefits that we’ll explore throughout are that these objects are a lot easier to work with when it comes to …

bigquery_在BigQuery中链接多个SQL查询

bigqueryBigquery is a fantastic tool! It lets you do really powerful analytics works all using SQL like syntax.Bigquery是一个很棒的工具&#xff01; 它使您能够使用像语法一样SQL来进行真正强大的分析工作。 But it lacks chaining the SQL queries. We cannot run …

大理石在哪儿 (Where is the Marble?,UVa 10474)

题目描述&#xff1a;算法竞赛入门经典例题5-1 1 #include <iostream>2 #include <algorithm>3 using namespace std;4 int maxn 10000 ;5 int main()6 {7 int n,q,a[maxn] ,k0;8 while(scanf("%d%d",&n,&q)2 && n &&q…

mysql 迁移到tidb_通过从MySQL迁移到TiDB来水平扩展Hive Metastore数据库

mysql 迁移到tidbIndustry: Knowledge Sharing行业&#xff1a;知识共享 Author: Mengyu Hu (Platform Engineer at Zhihu)作者&#xff1a;胡梦瑜(Zhhu的平台工程师) Zhihu which means “Do you know?” in classical Chinese, is the Quora of China: a question-and-ans…

XCode、Objective-C、Cocoa 说的是几样东西

大部分有一点其他平台开发基础的初学者看到XCode&#xff0c;第一感想是磨拳擦掌&#xff0c;看到 Interface Builder之后&#xff0c;第一感想是跃跃欲试&#xff0c;而看到Objective-C的语法&#xff0c;第一感想就变成就望而却步了。好吧&#xff0c;我是在说我自己。 如果…

递归函数基例和链条_链条和叉子

递归函数基例和链条因果推论 (Causal Inference) This is the fifth post on the series we work our way through “Causal Inference In Statistics” a nice Primer co-authored by Judea Pearl himself.这是本系列的第五篇文章&#xff0c;我们通过“因果统计推断”一书进行…

java lock 信号_java各种锁(ReentrantLock,Semaphore,CountDownLatch)的实现原理

先放结论&#xff1a;主要是实现AbstractQueuedSynchronizer中进入和退出函数&#xff0c;控制不同的进入和退出条件&#xff0c;实现适用于各种场景下的锁。JAVA中对于线程的同步提供了多种锁机制&#xff0c;比较著名的有可重入锁ReentrantLock&#xff0c;信号量机制Semapho…

Intent.ACTION_MAIN

1 Intent.ACTION_MAIN String: android.intent.action.MAIN 标识Activity为一个程序的开始。比较常用。 Input:nothing Output:nothing 例如&#xff1a; 1 <activity android:name".Main"android:label"string/app_name">2 <intent-filter…

足球预测_预测足球热

足球预测By Aditya Pethe通过阿蒂亚皮特(Aditya Pethe) From September to January every year, football takes over America. Games dominate TV Sunday and Monday nights, and my brother tears his hair out each week over his consistently underperforming fantasy te…

C#的特性Attribute

一、什么是特性 特性是用于在运行时传递程序中各种元素&#xff08;比如类、方法、结构、枚举、组件等&#xff09;的行为信息的声明性标签&#xff0c;这个标签可以有多个。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号&am…

python3中朴素贝叶斯_贝叶斯统计:Python中从零开始的都会都市

python3中朴素贝叶斯你在这里 (You are here) If you’re reading this, odds are: (1) you’re interested in bayesian statistics but (2) you have no idea how Markov Chain Monte Carlo (MCMC) sampling methods work, and (3) you realize that all but the simplest, t…

【转载】移动端布局概念总结

布局准备工作及布局思想及概念: 一个显示器&#xff08;pc端显示器 及 手机屏显示器&#xff09;&#xff0c;既有物理像素&#xff0c;又有独立像素&#xff08;独立像素也叫作css像素&#xff0c;用于前端人员使用&#xff09;&#xff1b; -->重要 首先确定设计稿的尺寸…