JAVA8 Stream的系列操作,Optional使用---- java养成

Java养成计划----学习打卡第六十天

Java入门到精通(打卡第六十天)


Java8流操作后续


时间过得真快,学习java2个月了,前面半个月因为复习的事情耽误了很多进度,还有精力花在算法和刷Java基础题上,发现很多东西真不会,还有几个重要的模块之后要单独讲解,一个就是数据结果后半部分,数据结构前面以前学着就不难,届时一篇博客总结一下顺序表到队列,着重分析hash表和后面的B树,还有图论,hash和线程要分析源码,比如CurrentHashMap,HashMap,HashSet, ThreadLocal,Simp……很多源码非常易考

准备正式结束java8,进入数据库了,增删查改还是要会的,🚶时间真紧啊

Stream回顾

之前的分享有点久远了,因为中间很久都没有使用java8的stream,所以下来简单回顾一下,首先关于Java8的Lambda表达式和方法引用都是针对函数式接口的,也就是只有一个抽象方法的接口。而stream则是用来处理数据的,比如noSQL就是需要在Java的service中来处理数据,处理数据我们就要使用到流,流的使用分为几步,是首先是流的创建,流的创建有多种方式,可以通过集合,也可以通过数组,还可以直接创建无限流,Stream类为泛型类,所以要给一个流的数据类型,这里简单写一下流的创建

List<Employee> employees = Arrays.asList(new NameListService().getAllEmployees());
Stream<Employee> stream = employees.stream();  //使用集合对象.stream创建
Stream<Employee> parallstream = employees.parallelStream();       //并行流,数据没有顺序,就像多线程一样
parallstream.forEach(System.out :: println); //对每一个流中的数据进行操作,这里括号里的是函数式接口,使用了方法引用
		
Employee[] employees1 = new NameListService().getAllEmployees();
Stream<Employee> stream1 = Arrays.stream(employees1); //使用数组,对数组的操作可以看成集合,所以使用Arrrays工具的stream来创建
stream1.forEach(t -> System.out.println(t == new Employee(8, "c风", 30, 19800.0))); //使用Lambda表达式


//如果不是通过集合或者而数组,还可以直接使用Stream的of方法
Stream<Employee> stream2 = Stream.of(employees1);   //用Stream接口中的of方法
		
//还有就是Stream的生成无限流
//1.通过迭代的方式;给一个种子和函数就可以创建无限流
Stream.iterate(2, t -> t * 2).limit(10).forEach(System.out :: println);
//2.通过generate【生成】方式,需要提供一个可以自动生成对象的函数
Stream.generate(Math :: random).limit(20).forEach(System.out :: println);

首先就是集合对象可以直接使用其的stream或者parallstream方法来创建流;而数组对象可以使用Arrays的stram方法或者Stram接口的of方法都可以;还可以使用 接口中的generate方法来创建流,我们分析前两种的流

流创建之后就是中间的执行过程,还有一个结束

Stream的中间操作

多个中间操作可以连接起来成为一个流水线,在上一篇博客中我在最后放了一张图片就是一个Stream的流水线。除非流水线上触发终止条件,否则中间操作不会执行任何的处理终止操作的时候会一次性全部处理,叫做‘’惰性处理‘这在之前也强调过,和finalize一样,只能使用一次,想继续处理只能重新创建流

这里可以看一下效果

stream1.filter(e -> e.getSalary() < 7000).forEach(System.out :: println);   //里面是一个判断的函数式接口,所以这里就写一个判断语句
stream1.limit(3).forEach(System.out :: println);

这里上面已经有了中间操作和终止操作,下面又再次想要操作这个流

java.lang.IllegalStateException: stream has already been operated upon or closed

程序就直接报错了🏷

筛选和排序

涉及的方法有几种

  • filter(Predicated p) ------ 接收Lambda,从流中排除某些元素。过滤
List<Employee> employees = Arrays.asList(new NameListService().getAllEmployees());
Stream<Employee> stream1 = employees.stream();
//stream1.forEach(System.out :: println);
//我们使用filter过滤工资小于7000的员工
stream1.filter(e -> e.getSalary() < 7000).forEach(System.out :: println);;   //里面是一个判断的函数式接口,所以这里就写一个判断语句,没有终止操作不执行,这里就使用了foreach的操作来执行

1 C神 35 3000.0
2 C云 34 3500.0 程序员 FREE 联想T4 【6000 】
3 C强 30 3800.0 设计师 FREE 3000.0 戴尔 【NEC17寸 】
4 李L 31 3100.0 程序员 FREE 戴尔 【三星 17寸 】
6 张Q 22 6800.0 程序员 FREE 华硕 【三星 17寸 】
10 丁D 21 6600.0 程序员 FREE 戴尔 【NEC 17寸 】

  • distinct() ------ 筛选,通过流的生成元素的hashCode()和equals()方法去除重复元素
Stream<Integer> stream2 = Stream.of(1,2,2,2,2,2,2,2,2,2);
stream2.distinct().forEach(System.out :: print);

//12
  • limit(long maxSize) ---- 截断流,使其元素不超过给定值
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream2.limit(3).forEach(System.out :: println);

1
2
3

  • skip(long n) – -------- 跳过元素,返回一个扔掉前n个元素的流,若流中元素不足n,则返回空流,与limit互补
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream2.limit(3).forEach(System.out :: print);
45678910

所以刚好互补,比如limit(3)和skip(3), 就刚好是流的前3个和流的除了前3个后面的数据

映射

主要方法都是和map相关,也即是映射

  • map(Function f) -------------- 接收一个函数作为参数,该函数会被应用到每一个元素上,并将其变为一个新的元素 就是x变到y】
List<String> strs = Arrays.asList("i","am","c","feng","happy");
strs.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);

/*
I
AM
C
FENG
HAPPY
*/
//再来一波极限操作
List<Employee> employees = Arrays.asList(new NameListService().getAllEmployees());
employees.stream().map(Employee :: getName).filter(str -> str.length() >= 3).forEach(System.out :: println);
//Employee是之前调度中的类
//这里首先以集合创建一个流之后,使用getName映射得到新流name流,再对name流进行过滤输出
//输出结果为刘大W
  • mapToDouble(ToDoubleFunction f) ------接收一个函数作为参数,该函数会被应用到每一个元素上,并产生一个新的DoubleStream
  • mapToInt(ToIntFunction f) ------- 接收一个函数作为参数,该函数会被应用到每一个元素上,并会产生一个新的IntStream
  • mapToLong(ToLongFunction f) -------- 接收一个函数作为参数,该函数会被应用到每一个元素上,产生一个新的LongStream
  • flatMap(Function f) --------------------- 接收一个函数作为参数,将流中的每一个值都换成另一个流,然后将所有流连成一个流

map和flatmap的区别

这个有点类似集合的合并,这里举一个例子

ArrayList list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
		
ArrayList list2 = new ArrayList<>();
list2.add(3);
list2.add(4);
System.out.println(list1);
//list1.add(list2);
list1.addAll(list2);
System.out.println(list1);

[1, 2]
[1, 2, [3, 4]]

[1, 2, 3, 4]

这里两种添加的方式不同,所以获得结果不同,直接添加表获得的是一个广义表,而添加元素则还是一个普通的表

那这个和map又什么关系?🏷

map就类似与add方法

flatmap就类似于addAll方法

也就是普通的map映射如果装载一个流,则该流是以整体装入,而flatmap则是将所有的流连接起来

这里来测试一下这两个方法

public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for(Character c : str.toCharArray()) {//将字符串变为了字符数组
	list.add(c); //将数组添加进入集合
	}
	return list.stream();
}

List<String> list = Arrays.asList("cfeng","clei","cyu","cdian");
//list.stream().map(str -> fromStringToStream(str));//相当于流的流
list.stream().map(StreamTest :: fromStringToStream).forEach(System.out :: println);

这里的流经过映射之后每一个元素都变成了一个Stream< Character>,也就是变成了流的流,那么遍历出来的

java.util.stream.ReferencePipeline$Head@6c629d6e
java.util.stream.ReferencePipeline$Head@5ecddf8f
java.util.stream.ReferencePipeline$Head@3f102e87
java.util.stream.ReferencePipeline$Head@27abe2cd

这不是我们想要得到的结果

那想要获得数据只能再次嵌套,对每个数据的流都要遍历

list.stream().map(StreamTest :: fromStringToStream).forEach(s -> {
s.forEach(System.out :: println);
});

这样之后获取的是一个一个的字符

flatmap就简单一些,识别每一个映射后都变成字符流,那么就直接连接再以前形成新的流

list.stream().flatMap(StreamTest :: fromStringToStream).forEach(System.out :: println);

连接之后形成的是字符组成的流,就不再是流的流了

排序

该操作就是可以对流中的数据进行排序

  • sorted() ----------- 产生一个新流,按自然顺序排序
  • sorted(Comparator com) ----------- 产生一个新流,其中按比较器顺序排序

排序想到的就是Comparable接口,如果数据的类实现了接口,就可以使用自然排序调用实现的这个接口来排序,如果没有实现接口,就会报错

public final class String
implements java.io.Serializable, Comparable,

比如String类就实现了接口,就可以使用自然排序

List<String> list = Arrays.asList("cfeng","clei","cyu","cdian");
list.stream().sorted().forEach(System.out :: println);

cdian
cfeng
clei
cyu

没有实现接口,那么就使用定制排序了,所以就使用Lambda表达式来写出这个comparator接口就可以了

Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
stream2.sorted().forEach(System.out :: println);
//没有实现接口,会报错
// java.lang.ClassCastException: class pers.Cfeng.groupsheduing.datafield.Programmer cannot be cast to class java.lang.Comparable (pers.Cfeng.groupsheduing.datafield.Programmer is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')

那这里就简单按照年龄排序

Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
stream2.sorted( (e1,e2) -> Integer.compare(e1.getAge(), e2.getAge())).forEach(System.out :: println);

10 丁D 21 6600.0 程序员 FREE 戴尔 【NEC 17寸 】
6 张Q 22 6800.0 程序员 FREE 华硕 【三星 17寸 】
11 张C 25 7100.0 程序员 FREE 华硕 【三星 17寸 】
9 C雨 26 9800.0 设计师 FREE 5500.0 惠普m6 【5800 】
12 C杨 27 9600.0 设计师 FREE 4800.0 惠普m6 【5800 】
5 雷J 28 10000.0 设计师 FREE 5000.0 佳能 2900 【激光 】
7 柳Y 29 10800.0 设计师 FREE 5200.0 华硕 【三星 17寸 】
3 C强 30 3800.0 设计师 FREE 3000.0 戴尔 【NEC17寸 】
8 C风 30 19800.0 架构师 FREE 15000.0 2500 爱普生20K 【针式 】
13 刘大W 30 19500.0 架构师 FREE 17000.0 3000 HP M401d 【喷墨 】
4 李L 31 3100.0 程序员 FREE 戴尔 【三星 17寸 】
2 C云 34 3500.0 程序员 FREE 联想T4 【6000 】
1 C神 35 3000.0

可以看到就是按照年龄的大小排序的

Stream的终止操作

stream的终止操作会从流的流水线生成结果,结果可以不是任何流的值,可以是List,Integer,void;流进行终止操作后,不能够再次使用

终止操作也有几种类型

匹配与查找

  • allMatch(Predicate p) 检查是否匹配所有元素
  • anyMatch(Predicate p) 检查是否至少匹配一个元素
  • noneMatch(Predicate p) 检查是否没有匹配元素
  • findFirst() 返回第一个元素
  • findAny() 返回流中的任意元素
  • count() 返回流中元素的总个数
  • max
  • min
Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
boolean is =  stream2.allMatch(e -> e.getAge() > 18);//这是boolean类型,要输出
System.out.println(is);

打印的结果就是true

Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
Optional<Employee> is =  stream2.findFirst();
System.out.println(is);

这样打印的结果为

Optional[1 C神 35 3000.0]

归约

map和reduce的连接通常称为map-reduce模式

  • reduce(T iden, BinaryOperator b) 可以将流中的元素反复结合起来,得到一个值,返回T
  • reduce(BinaryOperator b) 可以流中的元素反复结合起来,得到一个值,返回Optional
//计算1到10自然数的和 -- 得到55
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0,Integer ::  sum);
System.out.println(sum);
Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
System.out.println(stream2.map(Employee :: getSalary).reduce(Double :: sum));

Optional[113400.0]

收集

Collectors使用类提供了很多静态方法方便创建收集器

Collector接口中的方法的实现决定了如何对流执行收集的操作,如收集到Map,List

  • collect(Collector c) 将流转换为其他形式,接收一个Collector接口的实现,用于Stream中的元素做汇总
Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
List list = stream2.filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
System.out.println(list);

也就是处理流后,将流收集起来装到容器中

这里就是放入List中

[5 雷J 28 10000.0 设计师 FREE 5000.0 佳能 2900 【激光 】, 6 张Q 22 6800.0 程序员 FREE 华硕 【三星 17寸 】, 7 柳Y 29 10800.0 设计师 FREE 5200.0 华硕 【三星 17寸 】, 8 C风 30 19800.0 架构师 FREE 15000.0 2500 爱普生20K 【针式 】, 9 C雨 26 9800.0 设计师 FREE 5500.0 惠普m6 【5800 】, 10 丁D 21 6600.0 程序员 FREE 戴尔 【NEC 17寸 】, 11 张C 25 7100.0 程序员 FREE 华硕 【三星 17寸 】, 12 C杨 27 9600.0 设计师 FREE 4800.0 惠普m6 【5800 】, 13 刘大W 30 19500.0 架构师 FREE 17000.0 3000 HP M401d 【喷墨 】]

可以写成

list.forEach(System.out :: println);

这样子打印就整齐了🏮

Optional类

java8的另外一个变革就是引入了Optaional类,java8很多东西就是互相支持,其实好像最核心的是Stream,Lambda表达式和方法引用的作用就是给其提供支持;而昨天的

Employee[] emp = new NameListService().getAllEmployees();
Stream<Employee> stream2 = Arrays.stream(emp);
Optional<Employee> is =  stream2.findFirst();
System.out.println(is);

使用is来存储流中的第一个数据就使用到了Optional类,那么Optional类的作用到底是什么呢?

如果写过很多java代码,就可以意识到java程序失败的一个重要的原因就是空指针,在C/C++中,最主要的一个错误就是指针问题,比如空指针,比如野指针;

而Optional< T>类(java.util.Optional)是一个 容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null ,表示这个值不存在,原来用null表示一个值不存在,现在可以更好的一个值不存在,现在Optional可以更好的避免空指针异常

Optional ----- 可以为null的容器对象,如果值存在,就isPresent方法会返回true,并用get方法返回对象

创建Optional类对象

  • Optional.of(T t) 创建一个Optional实例,t必须非空
Employee em = new Employee(1, "Cfeng", 34, 3000.0);
Optional<Employee> optionaltest = Optional.of(em); //使用Alt + Shift + L可以快速生成变量
System.out.println(optionaltest);

//输出的结果为Optional[1	Cfeng	34	3000.0]

//Employee em = null;
//则会报错 in thread "main" java.lang.NullPointerException
  • Optional.empty() 创建一个空的Optional实例
  • Optional.ofNullable(T t) t可以为null

of方法创建对象不能为空,而ofNullable则可以为空

Employee em = null;
Optional<Employee> optionaltest = Optional.ofNullable(em); 
System.out.println(optionaltest);

Optional.empty

所以之前我们调取时总是

Cannot invoke “pers.Cfeng.groupsheduing.datafield.Employee.getName()” because “em” is null

而为了保证这个情况要写很多if语句

if( 非空) —

有了Optional类之后就可以转换一下方式

这里使用Programmer的getEquipment

//原来的方式来获取eqt的name
public String getEquipmentName(Programmer emp) {
    if(emp != null) {
        Equipmemt eqt = emp.getEquipment();
        if(eqt != null) {
            return eqt.getName();
        }
    }
    return null;
}

//使用Optional方式获取name
public String getEquipmentName(Programmer emp1) {
    //Optional就是一个包装的类,就像Integer对int进行包装,Optional对普通的类进行包装之后能够避免空指针
    //判空就先包装,再解开包装
    Optional<Programmer> optionalEmp = Optional.ofNullable(emp1);//使用ofNullable来避免空指针导致的异常
    Programmmer emp = optionalEmp.orElse(new Programmer(1, "C风", 0, 0, null));
    Equipment eqt = emp.getEquipment();
    Optional<Equipment> optionalEqt = Optional.ofNullable(eqt);
    Equipment eqt1 = optionalEqt.orElse(new Equipment("TP-LINK"));
    return eqt1.getName();
}

判断Optional容器中是否包含对象

  • boolean isPresent() 判断是否包含对象
  • void ifPresent(Consumer< ? super T) consumer) 如果有值,就执行Consumer接口的代码,该值会作为参数传给接口

获取Optional容器的对象

  • T get() 如果调用的对象包含值,返回该值,否则抛出异常
  • T orElse(T other) 如果有值就返回,否则返回指定的oher对象
  • TorElseGet(Supplier< ? extends T> other) 如果有值就将其返回,否则返回Supplier接口指定的对象
  • T orElseThrow(Supplier< ? extends T> exceptionSupplier) 如果有值就返回,否则返回Supplier接口提供的异常
Employee em = null;
Employee em1 = new Employee(1, "Cfeng", 34, 3000.0);
Optional<Employee> optionaltest = Optional.ofNullable(em); 
System.out.println(optionaltest.orElse(em1));

1 Cfeng 34 3000.0

所以我们常用的方法时ofNullable和orElse

 Optional<Equipment> optionalEqt = Optional.ofNullable(eqt);
 Equipment eqt1 = optionalEqt.orElse(new Equipment("TP-LINK"));

先将可能为空的对象使用Optional的ofNullable包装,之后使用orElse拆包,之后正常使用,可方便判断空

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值