目录
强大的Stream API
一、创建流
二、中间操作
1.筛选与切片
2.映射
3.排序
三、终止操作
1.匹配与查找
2. 归约
3. 收集 --- Collectors类
Stream常用方法
Optional 类
使用Optional类优化
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
为什么要是用 Stream API?
实际开发中,项目中多数数据源都来自于Mysql,Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,主要面向内存,存储在内存中。而 Stream 是有关计算的,主要是面向 CPU,通过 CPU 实现计算。
Stream关注的是对数据的运算,与CPU打交道 集合关注的是数据的存储,与内存打交道
①Stream 自己不会存储元素。
②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream 执行流程
① Stream的实例化
② 一系列的中间操作(过滤、映射、...)
③ 终止操作说明
①一个中间操作链,对数据源的数据进行处理
②一旦执行终止操作,就执行中间操作链,并产生结果。之后,此Stream不能再被使用
static |
concat(Stream extends T> a, Stream extends T> b) 创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。(可以合并两个集合) |
static |
of(T... values) 通过显示值创建一个流。它可以接收任意数量的参数。返回一个顺序流 |
public class StreamAPITest {
//创建 Stream方式一: 通过集合
@Test
public void test1(){
List employees = EmployeeData.getEmployees();
// default Stream stream() : 返回一个顺序流
Stream stream = employees.stream();
// default Stream parallelStream() : 返回一个并行流(多线程)
Stream parallelStream = stream.parallel();
}
//创建 Stream方式二:通过数组
@Test
public void test2(){
int[] arr = new int[]{1,2,3,4,5,6};
// 调用Arrays类static Stream stream(T[] array): 返回一个流
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Jerry");
Employee[] arr1 = new Employee[]{e1,e2};
Stream stream1 = Arrays.stream(arr1);
}
//创建 Stream方式三:通过Stream的of(
@Test
public void test3(){
Stream stream = Stream.of(1, 2, 3, 4, 5, 6);
}
//创建 Stream方式四:创建无限流(较少使用)
@Test
public void test4(){
//迭代
//public static Stream iterate(final T seed, final UnaryOperator f)
//遍历前10个偶数
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
//生成
//public static Stream generate(Supplier s)
Stream.generate((Math::random)).limit(10).forEach(System.out::println);;
}
}
Stream |
filter(Predicate p) 接收 Lambda , 从流中排除某些元素,保留符合条件的元素 |
Stream |
distinct() 筛选,通过流所生成元素的equals() 去除重复元素 |
Stream |
limit(long maxSize) 截断流,使其元素不超过给定数量 |
Stream |
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
/*
* 测试Stream的中间操作
*
* */
public class StreamAPITest1 {
// 1- 筛选与切片
@Test
public void test1() {
List list = EmployeeData.getEmployees();
// filter(Predicate p) --- 接受Lambda,从流中排除某些元素。
Stream stream = list.stream();
//查询员工表中薪资大于7000的员工信息
stream.filter(new Predicate() {
@Override
public boolean test(Employee e) {
return e.getSalary()>7000;
}
}).forEach(System.out::println);
//stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println("-------------------------");
// limit(n) --- 截断流,使其元素不超过给定数量。
//限制输出前5个元素 list.stream():之前的流已被终止,生成一个新的Stream
list.stream().limit(5).forEach(System.out::println);
System.out.println("-------------------------");
// skip(n) --- 跳过元素,返回一个扔掉了前 n 个元素的流。若流中的元素不足 n 个,则返回一个空流。与limit(n)互补。
//跳过前三个数据
list.stream().skip(3).forEach(System.out::println);
System.out.println("-------------------------");
// distinct()-- 筛选,通过流锁生成元素的hashCode() 和equals()去除重复元素
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
list.add(new Employee(1010,"刘强东",40,8000));
//System.out.println(list);
list.stream().distinct().forEach(System.out::println);
}
}
|
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
DoubleStream |
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
IntStream |
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
LongStream |
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
|
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
Stream |
peek(Consumer action) 可以使用foreack查看中间的数据,只能有一个终止操作 接收Lambda,对流中的每个数据执行Lambda体操作 |
@Test// 2- 映射
public void test2(){
//map(Function f)
//接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List list = Arrays.asList("aa", "bb", "cc", "dd");
//将字符串转换为大写
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
System.out.println("-------------------------");
//练习1: 获取员工姓名长度大于3的员工名字
List employees = EmployeeData.getEmployees();
Stream namsStream = employees.stream().map(e -> e.getName()); //获取全部员工名字 的string
namsStream.filter(name -> name.length() > 3).forEach(System.out :: println); //获取长度大于3
System.out.println("-------------------------");
//练习2: Map 实现 flatMap 功能(悲壮) 类似for嵌套for
Stream> streamStream = list.stream().map(StreamAPITest1::fromStringToStream);
streamStream.forEach(s ->{
s.forEach(System.out::println);
});
System.out.println("-------------------------");
//flatMap(Function f) 理解参考test3
//接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
Stream characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream);
characterStream.forEach(System.out::println);
}
//将字符串中的多个字符构成的集合转换为对应的Stream的实例
public static Stream fromStringToStream(String str){
ArrayList list = new ArrayList<>();
for(Character c : str.toCharArray()){
list.add(c);
}
return list.stream();
}
@Test
public void test3(){
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
ArrayList list2 = new ArrayList();
list2.add(4);
list2.add(5);
list2.add(6);
//list1.add(list2); //4元素
list1.addAll(list2); //6元素
System.out.println(list1);
}
Stream |
sorted() 产生一个新流,其中按自然顺序排序。 |
Stream |
sorted(Comparator com) 产生一个新流,其中按比较器顺序排序 |
// 3- 排序
@Test
public void test4(){
//sorted() -- 自然排序
List list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
list.stream().sorted().forEach(System.out::println);
//抛异常,原因: EmployeeData 没有实现 Comparable接口
// List employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out::println);
//sorted(Comparator com) -- 定制排序
List employees = EmployeeData.getEmployees();
/*employees.stream().sorted((e1,e2) -> {
return Integer.compare(e1.getAge(),e2.getAge());
}).forEach(System.out::println);*/
employees.stream().sorted((e1,e2) -> Integer.compare(e1.getAge(),e2.getAge()))
.forEach(System.out::println);
}
}
void | forEach(Consumer c) 迭代遍历流 |
boolean | allMatch(Predicate p) 检查是否匹配所有元素 |
boolean | anyMatch(Predicate p) 检查是否至少匹配一个元素 |
boolean | noneMatch(Predicate p) 检查是否没有匹配所有元素 |
long | count() 返回流中元素总数 |
Optional |
findFirst() 返回第一个元素 |
Optional |
findAny() 返回当前流中的任意元素 |
Optional |
max(Comparator c) 返回流中最大值 |
Optional |
min(Comparator c) 返回流中最小值 |
T | reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T |
U | reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional |
R | collect(Collector c) (将数据收集、转为集合) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
/*
* 测试Stream的终止操作
*
* */
public class StreamAPITest2 {
// 1 - 匹配与查找
@Test
public void test1(){
List employees = EmployeeData.getEmployees();
//allMatch(Predicate p) --- 检查是否匹配所有元素。
// 练习:是否所有的员工的年龄都大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch); //false
//anyMatch(Predicate p) --- 检查是否至少匹配一个元素。
// 练习:是否存在员工的工资大于10000
boolean anyMatch = employees.stream().allMatch(e -> e.getSalary() > 10000);
System.out.println(anyMatch); //false
//noneMatch(Predicate p) --- 检查是否没有匹配的元素。
// 练习: 是否存在员工姓"雷" startsWith:是否以*开头
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch); //false
//findFirst --- 返回第一个元素
Optional employee = employees.stream().findFirst();
System.out.println(employee);
//findAny --- 返回当前流中的任意元素
Optional employee1 = employees.parallelStream().findAny();
System.out.println(employee1);
}
@Test
public void test2(){
List employees = EmployeeData.getEmployees();
//count --- 返回流中元素的总个数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
System.out.println(count); //5
//max(Comparator c) --- 返回流中最大值
// 练习: 返回最高的工资
Stream salaryStream = employees.stream().map(e -> e.getSalary());
Optional maxSalary = salaryStream.max(Double::compare);
System.out.println(maxSalary);
//min(Comparator c) --- 返回流中最小值
// 练习: 返回最低工资的员工
Optional employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(employee);
//forEach(Consumer c) --- 内部迭代
employees.stream().forEach(System.out::println);
//使用集合的遍历操作方法 (与上面不同)
employees.forEach(System.out::println);
}
}
T | reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T |
U | reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional |
// 2 - 归约
@Test
public void test3(){
//reduce(T identity,BinaryOperator b) --- 可以将流中元素反复结合起来,得到一个值。返回T
//练习1: 计算1-10的自然数的和
List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, Integer::sum);//0:初始值
System.out.println(sum);
//reduce(BinaryOperator b) --- 可以将流中元素反复结合起来,得到一个值。返回 Optional
//练习2: 计算公司所有员工工资的综合
List employees = EmployeeData.getEmployees();
Stream salaryStream = employees.stream().map(e -> e.getSalary());
//Optional sumMoney = salaryStream.reduce((s1,s2) -> s1 + s2);
Optional sumMoney = salaryStream.reduce(Double::sum);
System.out.println(sumMoney); //Optional[48424.08]
}
R | collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
// 3 - 收集
@Test
public void test4(){
//collect(Collector c) --- 将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中元素做汇总的方法
//练习1: 查找工资大于6000的员工 结果返回为一个List或Set
List employees = EmployeeData.getEmployees();
List employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out::println);
System.out.println("-------------------------------");
Set employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
static |
of(T... values) 返回其元素是指定值的顺序排序流。 |
static |
of(T t) 返回包含单个元素的顺序 |
Stream |
filter(Predicate super T> predicate) 接受Lambda,从流中排除某些元素。 |
Stream |
limit(long maxSize) 截断流,使其元素不超过给定数量。 |
Stream |
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中的元素不足 n 个,则返回一个空流。与limit(n)互补。 |
Stream |
distinct() 筛选,通过流锁生成元素的hashCode() 和equals()去除重复元素 |
|
map(Function super T,? extends R> mapper) 接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
|
flatMap(Function super T,? extends Stream extends R>> mapper) 接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 |
终止操作 | ------------------------------------------------------------------------------ |
void |
forEach(Consumer super T> action) 对此流的每个元素执行操作。 |
boolean |
allMatch(Predicate super T> predicate) 检查是否匹配所有元素。 |
boolean |
anyMatch(Predicate super T> predicate) 检查是否至少匹配一个元素。 |
boolean |
noneMatch(Predicate super T> predicate) 检查是否没有匹配的元素。 |
Optional |
findFirst() 返回第一个元素 |
Optional |
findAny() 返回当前流中的任意元素 |
long |
count() 返回此流中的元素数。 |
Optional |
max(Comparator super T> comparator) 根据提供的 |
Optional |
min(Comparator super T> comparator) 根据提供的 |
T |
reduce(T identity, BinaryOperator 可以将流中元素反复结合起来,得到一个值。返回T |
Optional |
reduce(BinaryOperator 可以将流中元素反复结合起来,得到一个值。返回 Optional |
U |
reduce(U identity, BiFunction accumulator, BinaryOperator combiner) 执行 reduction在此流中的元素,使用所提供的身份,积累和组合功能。 |
static |
builder() 返回一个 |
|
collect(Collector super T,A,R> collector) 使用 Collector对此流的元素执行 mutable reduction |
|
collect(Supplier 对此流的元素执行 mutable reduction操作。 |
static |
empty() 返回一个空的顺序 |
DoubleStream |
flatMapToDouble(Function super T,? extends DoubleStream> mapper) 返回一个 |
IntStream |
flatMapToInt(Function super T,? extends IntStream> mapper) 返回一个 |
LongStream |
flatMapToLong(Function super T,? extends LongStream> mapper) 返回一个 |
void |
forEachOrdered(Consumer super T> action) 如果流具有定义的遇到顺序,则以流的遇到顺序对该流的每个元素执行操作。 |
static |
generate(Supplier 返回无限顺序无序流,其中每个元素由提供的 |
static |
iterate(T seed, UnaryOperator 返回有序无限连续 |
DoubleStream |
mapToDouble(ToDoubleFunction super T> mapper) 返回一个 |
IntStream |
mapToInt(ToIntFunction super T> mapper) 返回一个 |
LongStream |
mapToLong(ToLongFunction super T> mapper) 返回一个 |
Stream |
peek(Consumer super T> action) 返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作。 |
Stream |
sorted() 返回由此流的元素组成的流,根据自然顺序排序。 |
Stream |
sorted(Comparator super T> comparator) 返回由该流的元素组成的流,根据提供的 |
Object[] |
toArray() 返回一个包含此流的元素的数组。 |
A[] |
toArray(IntFunction generator) 使用提供的 |
|
返回一个 |
|
如果空指针,返回 |
|
如果空指针,则调用 |
|
如果空指针,则抛出由提供的供应商创建的异常。 |
创建Optional类对象
创建Optional类对象的方式:
- Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
- Optional.empty() : 创建一个空的 Optional 实例
- Optional.ofNullable(T t):t可以为nul
//方法
orElse() 如果为空则
@Test
public void test1(){
Girl girl = new Girl();
girl = null;
Optional optionalGirl = Optional.of(girl);
//NullPointerException
}
@Test
public void test2(){
Girl girl = new Girl();
girl = null;
Optional optionalGirl = Optional.ofNullable(girl);
System.out.println(optionalGirl);//Optional.empty
//orElse(T t): 如果当前的Optional内部封装的t是非空的,则返回内部的t
//如果内部的t是空的,则返回orElse()方法中的参数t1
Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
System.out.println(girl1);//Girl{name='赵丽颖'}
}
出现空指针的情况
@Test
public void test3(){
Boy boy = new Boy();
String girlName = getGirlName(boy);
System.out.println(girlName);
//NullPointerException
}
public String getGirlName(Boy boy){
return boy.getGirl().getName();
}
优化后
@Test
public void test4(){
Boy boy = new Boy();
String girlName = getGirlName1(boy);
System.out.println(girlName);//null
}
//优化以后的getGirName();
public String getGirlName1(Boy boy){
if(boy != null){
Girl girl = boy.getGirl();
if(girl != null){
return girl.getName();
}
}
return null;
}
@Test
public void test5(){
Boy boy = null;
boy = new Boy();
String girlName = getGirlName2(boy);
System.out.println(girlName); //迪丽热巴
}
//使用Optional类的getGirName();
public String getGirlName2(Boy boy){
Optional boyOptional = Optional.ofNullable(boy);
//此时的boy1一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴")));
Girl girl = boy1.getGirl();
Optional girlOptional = Optional.ofNullable(girl);
//此时的girl1一定非空
Girl girl1 = girlOptional.orElse(new Girl("古力娜扎"));
return girl1.getName();
}