什么是Stream?
Stream是JDK8 API的新成员,它允许以声明性方式处理数据集合
特点
- 代码简洁: 函数式编程写出的代码简洁且意图明确,使用stream接口让你从此告别for循环
- 多核友好: Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法
为什么要使用Stream API?
- 实际开发中,项目中多数数据源都来自于MySQL,Oracle等.但现在数据源可以更多了,有MongoDB,Redis等,而这些NoSQL的数据就需要Java层面去处理
- Stream和Collection集合的区别:Collection是一种静态的内存数据结构,而Stream是有关计算的.前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算
注意:
- Stream自己不会存储元素
- Stream不会改变源对象.相反,他们会返回一个持有结果的新Stream
- Stream操作是延迟执行的.这意味着他们会等到需要结果的时候才执行
步骤
- 创建Stream
一个数据源(集合,数组等),获取一个流 - 中间操作
一个中间操作链,对数据源的数据进行处理 - 终止操作
一旦执行终止操作,就执行中间操作链,并产生结果.之后,不会再被使用
Stream的实例化
/**
* @PROJECT_NAME: myTest
* @DESCRIPTION: Stream的方法测试
* @USER: 罗龙达
* @DATE: 2021/2/21 2:33
*/
public class streamTest {
@Test
public void createStream(){
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jk");
//1. 通过集合创建stream -- 返回一个顺序流
Stream<String> listStream = strings.stream();
//返回一个并行流
Stream<String> paraStream = strings.parallelStream();
//2. 通过数组创建stream
IntStream arrStream = Arrays.stream(new int[]{1, 3, 45, 6, 877, 12});
//3. 通过Stream的of()方法
Stream<Integer> integerStream = Stream.of(1, 3, 5, 7, 9);
//4. 创建无限流
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
//5. 生成
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
中间操作的部分API测试
@Test
public void InOperationTest() {
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "abc", "jk");
//1. 通过集合创建stream -- 返回一个顺序流
Stream<String> listStream = strings.stream();
System.out.println("-----------测试filter--------------");
/**
* filter(Predicate predicate) -- 接收lambda,从流中排除某些元素
* 查询列表中不为null的元素
*/
//
listStream.filter(s -> !s.equals("")).forEach(System.out::println);
System.out.println("--------------测试limit------------");
/**
*limit(n) -- 截断流,使元素不超过给定数量
*/
strings.stream().limit(3).forEach(System.out::println);
System.out.println("-------------测试skip--------------");
/**
*skip(n) -- 跳过元素,返回一个扔掉了前n个元素的流,如果流中元素不足n个,则返回一个空流
*/
strings.stream().skip(3).forEach(System.out::println);
System.out.println("------------测试distinct-----------");
/**
*distinct() -- 筛选,通过流所产生元素的hashCode()和equals()去除重复元素
*/
strings.stream().distinct().forEach(System.out::println);
}
Map映射API的部分测试
@Test
public void mapTest(){
System.out.println("----------------将字符串转换成大写---------------");
List<String> strings = Arrays.asList("aaa", "bb", "cccc", "d","eeeee","ff");
/**
* 接受一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素
*/
strings.stream().map(String::toUpperCase).forEach(System.out::println);
System.out.println("------------字符串长度大于3的元素的大写-----------");
/**
* 返回字符串长度大于3的元素的大写
*/
strings.stream().filter(s -> s.length() >3).map(String::toUpperCase).forEach(System.out::println);
/**
* flatMap(Function f) -- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
*/
strings.stream().flatMap(streamTest::fromStringToStream).forEach(System.out::println);
}
//将字符串中的多个字符构成的集合转换为对应的流
public static Stream<Character> fromStringToStream(String s){
ArrayList<Character> list = new ArrayList<>();
for (char c : s.toCharArray()) {
list.add(c);
}
return list.stream();
}
map与flatMap的区别
- 从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 flatMap。
- flatMap 把 inpuStream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字。
排序API测试
@Test
public void sortTest() {
List<Integer> list = Arrays.asList(23, 45, -12, 2, 7, 89, 5);
System.out.println("----------从小到大排序测试---------");
list.stream().sorted().forEach(System.out::println);
System.out.println("----------从大到小排序测试---------");
list.stream().sorted((i1, i2) -> i2 - i1).forEach(System.out::println);
}
终止操作的部分API测试
@Test
public void termOperationTest(){
List<Integer> list = Arrays.asList(23, 45, -12, 2, 7, 89, 5);
System.out.println("-----------allMatchTest-------------");
/**
* allMatch(Predicate predicate) -- 检查是否匹配所有元素
*/
boolean allMatch = list.stream().allMatch(integer -> integer > 0);
System.out.println(allMatch);
System.out.println("-----------anyMatchTest-------------");
/**
* allMatch(Predicate predicate) -- 检查是否匹配所有元素
*/
boolean anyMatch = list.stream().anyMatch(integer -> integer > 0);
System.out.println(anyMatch);
System.out.println("----------noneMatchTest-------------");
/**
* noneMatch(Predicate predicate) -- 检查是否没有匹配的元素
*/
boolean noneMatch = list.stream().noneMatch(integer -> integer > 0);
System.out.println(noneMatch);
System.out.println("----------findFirstTest-------------");
/**
* findFirst() -- 返回流中第一个元素
*/
Optional<Integer> first = list.stream().findFirst();
System.out.println(first);
System.out.println("------------findAnyTest-------------");
/**
* findAny() -- 返回流中任一元素
*/
Optional<Integer> any = list.parallelStream().findAny();
System.out.println(any);
System.out.println("--------------countTest-------------");
/**
* count() -- 返回流中元素的总个数
*/
long count = list.stream().filter(i -> i > 0).count();
System.out.println(count);
System.out.println("------------max / min Test----------");
/**
* max/min(Comparator comparator) -- 返回流中最大值 / 最小值
*/
Optional<Integer> max = list.stream().max((i1, i2) -> i1 - i2);
System.out.println("max = " + max);
Optional<Integer> min = list.stream().min((i1, i2) -> i1 - i2);
System.out.println("min = " + min);
System.out.println("--------------forEachTest------------");
/**
* forEach(Consumer c) -- 内部迭代
*/
// list.stream().forEach(System.out::println);
list.forEach(System.out::println);
}
规约操作部分API测试
@Test
public void reduceTest(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/**
* reduce(T identity, BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值
*/
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println("reduce = " + reduce);
/**
* reduce(BinaryOperator) -- 可以将流中元素反复结合起来,得到一个值,返回optional
*/
Optional<Integer> reduce2 = list.stream().reduce(Integer::sum);
Optional<Integer> reduce3 = list.stream().reduce((i1, i2) -> i1 + i2);
System.out.println("reduce2 = " + reduce2);
System.out.println("reduce3 = " + reduce3);
}
collect部分API测试
@Test
public void collectTest(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/**
*collect(Collector c) 将流转换为其他形式.接受一个Collector接口的实现
* 查找大于4的数字,结果返回一个list / set
*/
List<Integer> collectList = list.stream().filter(i -> i > 4).collect(Collectors.toList());
collectList.forEach(System.out::println);
Set<Integer> collectSet = list.stream().filter(i -> i > 4).collect(Collectors.toSet());
collectSet.forEach(System.out::println);
}
Optional类
- 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.
以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,鼓励程序员写更干净的代码.受到Google Guava的启发,Optional类已经成为Java 8 类库的一部分 - Optional类是一个容器类,他可以保存类型T的值,代表这个值存在,或者仅仅保存null,表示这个值不存在.原来用null表示一个值不存在,现在Optional可以更好地表达这个概念.并且可以避免空指针异常
- Optional类的Javadoc描述如下: 这是一个可以为null的容器对象.如果值存在在isPresent()方法会返回true,调用get()方法会返回该对象
相关方法
# 1 创建Optional类对象的方法
- Optional.of(T t): 创建一个Optional实例,`t必须非空`
- Optional.empty(): 创建一个空的Optional实例
- Optional.ofNullable(T t): t可以为null
# 2 判断Optional容器中是否包含对象:
- boolean isPresent(): 判断是否包含对象
- void ifPresent(Consumer consumer): 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它
# 3 获取Optional容器的对象
- T get(): 如果调用对象包含值,返回该值,否则抛异常
- T orElse(): 如果有值则将其返回,否则返回指定的other对象
- T orElseGet(Supplier other): 如果有值将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier exceptionSupplier): 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
相关方法代码测试
- Girl实体类
public class Girl {
public Girl() {
}
public Girl(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- Boy实体类
public class Boy {
private Girl girlFriend;
private String name;
public Boy() {
}
public Boy(Girl girlFriend, String name) {
this.girlFriend = girlFriend;
this.name = name;
}
public Girl getGirlFriend() {
return girlFriend;
}
public void setGirlFriend(Girl girlFriend) {
this.girlFriend = girlFriend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
public void OptionalTest(){
Girl girl = new Girl();
girl=null;
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
System.out.println("optionalGirl = " + optionalGirl);
}
//原始的getName方法
public String getGirlName(Boy boy){
return boy.getGirlFriend().getName();
}
//优化后的getName方法
public String getGirlNameAfterOptimizing(Boy boy){
Optional<Boy> boyOptional = Optional.ofNullable(boy);
//此时boy1一定非空
Boy boy1 = boyOptional.orElse(new Boy(new Girl("杨幂"), "我"));
Girl girlFriend = boy1.getGirlFriend();
Optional<Girl> girlFriend1 = Optional.ofNullable(girlFriend);
//girlOption一定非空
Girl girlOptional = girlFriend1.orElse(new Girl("赵丽颖"));
return "女朋友的名字 : " + girlOptional.getName();
}
@Test
public void testGetName(){
Boy boy = new Boy();
// boy = null;
boy.setGirlFriend(null);
// boy.setGirlFriend(new Girl("迪丽热巴"));
System.out.println(getGirlNameAfterOptimizing(boy));
}