1.5、Optional类

1、定义

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

2、声明

以下是一个 java.util.Optional<T> 类的声明:

  1. public final class Optional<T> extends Object

3、类方法

  1. 序号 方法 & 描述
  2. 1 static <T> Optional<T> empty()
  3. 返回空的 Optional 实例。
  4.  
  5. 2 boolean equals(Object obj)
  6. 判断其他对象是否等于 Optional
  7.  
  8. 3 Optional<T> filter(Predicate<? super <T> predicate)
  9. 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional
  10.  
  11. 4 <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)
  12. 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
  13.  
  14. 5 T get()
  15. 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
  16.  
  17. 6 int hashCode()
  18. 返回存在值的哈希码,如果值不存在 返回 0
  19.  
  20. 7 void ifPresent(Consumer<? super T> consumer)
  21. 如果值存在则使用该值调用 consumer , 否则不做任何事情。
  22.  
  23. 8 boolean isPresent()
  24. 如果值存在则方法会返回true,否则返回 false
  25.  
  26. 9 <U>Optional<U> map(Function<? super T,? extends U> mapper)
  27. 如果存在该值,提供的映射方法,如果返回非null,返回一个Optional描述结果。
  28.  
  29. 10 static <T> Optional<T> of(T value)
  30. 返回一个指定非null值的Optional
  31.  
  32. 11 static <T> Optional<T> ofNullable(T value)
  33. 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional
  34.  
  35. 12 T orElse(T other)
  36. 如果存在该值,返回值, 否则返回 other
  37.  
  38. 13 T orElseGet(Supplier<? extends T> other)
  39. 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
  40.  
  41. 14 <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
  42.  
  43. 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
  44.  
  45. 15 String toString()
  46. 返回一个Optional的非空字符串,用来调试

注意: 这些方法是从 java.lang.Object 类继承来的。

4、示例

  1. import java.util.Optional;
  2.  
  3. public class Java8Tester {
  4. public static void main(String args[]){
  5.  
  6. Java8Tester java8Tester = new Java8Tester();
  7. Integer value1 = null;
  8. Integer value2 = new Integer(10);
  9.  
  10. // Optional.ofNullable - 允许传递为 null 参数
  11. Optional<Integer> a = Optional.ofNullable(value1);
  12.  
  13. // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
  14. Optional<Integer> b = Optional.of(value2);
  15. System.out.println(java8Tester.sum(a,b));
  16. }
  17.  
  18. public Integer sum(Optional<Integer> a, Optional<Integer> b){
  19.  
  20. // Optional.isPresent - 判断值是否存在
  21.  
  22. System.out.println("第一个参数值存在: " + a.isPresent());
  23. System.out.println("第二个参数值存在: " + b.isPresent());
  24.  
  25. // Optional.orElse - 如果值存在,返回它,否则返回默认值
  26. Integer value1 = a.orElse(new Integer(0));
  27.  
  28. //Optional.get - 获取值,值需要存在
  29. Integer value2 = b.get();
  30. return value1 + value2;
  31. }
  32. }

输出

  1. 第一个参数值存在: false
  2. 第二个参数值存在: true
  3. 10

1.6、Stream流

1、定义

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

  • <strong元素队列< strong="">元素是特定类型的对象,形成一个队列。 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)实现。

由于操作过程完全由Java处理,因此它可以根据当前硬件环境选择最优的方法处理,我们也无需编写复杂又容易出错的多线程代码了。

2、生成流

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

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

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

3、流的操作种类

流的操作分为两种,分别为中间操作 和 终端操作。
中间操作
  当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。
  中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。
  中间操作都是filter()、distinct()、sorted()、map()、flatMap()等,其一般是对数据集的整理(过滤、排序、匹配、抽取等)
终端操作
  当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终端操作。
  终端操作将返回一个执行结果,这就是你想要的数据。
  终止方法往往是完成对数据集中数据的处理,如forEach(),还有allMatch()、anyMatch()、findAny()、 findFirst(),数值计算类的方法有sum、max、min、average等等。终止方法也可以是对集合的处理,如reduce()、 collect()等等。reduce()方法的处理方式一般是每次都产生新的数据集,而collect()方法是在原数据集的基础上进行更新,过程中不产生新的数据集。

4、流的操作过程

使用流一共需要三步:
准备一个数据源

  1. 1、集合
  2. 这种数据源较为常用,通过stream()方法即可获取流对象:
  3. List<Person> list = new ArrayList<Person>();
  4. Stream<Person> stream = list.stream();
  5. 2、数组
  6. 通过Arrays类提供的静态函数stream()获取数组的流对象:
  7. String[] names = {"chaimm","peter","john"};
  8. Stream<String> stream = Arrays.stream(names);
  9. 3.直接将几个值变成流对象:
  10. Stream<String> stream = Stream.of("chaimm","peter","john");
  11. 4、文件
  12. try(Stream lines = Files.lines(Paths.get(“文件路径名”),Charset.defaultCharset())){
  13. //可对lines做一些操作
  14. }catch(IOException e){
  15. }
  16. PSJava7简化了IO操作,把打开IO操作放在try后的括号中即可省略关闭IO的代码。

执行中间操作 【中间操作可以有多个,它们可以串连起来形成流水线。】、执行终端操作 【执行终端操作后本次流结束,你将获得一个执行结果。】

5、流转换为其他数据结构

  1. // 1. Array
  2. String[] strArray1 = stream.toArray(String[]::new);
  3. // 2. Collection【list,set,stack】
  4. List<String> list1 = stream.collect(Collectors.toList());
  5. List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
  6. Set set1 = stream.collect(Collectors.toSet());
  7. Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
  8. // 3. String
  9. String str = stream.collect(Collectors.joining()).toString();

6、使用

6.1、筛选filter

filter函数接收一个Lambda表达式作为参数,该表达式返回boolean,在执行过程中,流将元素逐一输送给filter,并筛选出执行结果为true的元素。

  1. List<Person> result = list.stream()
  2. .filter(p->p.getIsStudent())
  3. .collect(toList());

6.2、去重distinct

返回一个有唯一元素的stream(根据stream中元素的equals实现)。内部需要重写equals

  1. List<Person> result = list.stream()
  2. .distinct()
  3. .collect(toList());

6.3、截取limit

截取流的前N个元素:

  1. List<Person> result = list.stream()
  2. .limit(3)
  3. .collect(toList());

6.4、跳过【skip】

跳过流的前n个

  1. List<Person> result = list.stream()
  2. .skip(3)
  3. .collect(toList());

6.5、映射【map】

对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的Lambda表达式,最后将执行结果存入一个新的流中。

  1. final List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
  2. final List<Integer> doubleNumbers = numbers.stream().map(number -> number * 2).collect(Collectors.toList());
  3. System.out.println(doubleNumbers);

6.6、合并【flagmap】

  1. List<String> list = new ArrayList<String>();
  2. list.add("I am a boy");
  3. list.add("I love the girl");
  4. list.add("But the girl loves another girl");
  5. list.stream()
  6. .map(line->line.split(" "))
  7. .flagmap(Arrays::stream)
  8. .distinct()
  9. .collect(toList());

6.7、排序【sorted】

不指定一个自定义的Comparator则会使用默认排序。

  1. result = list.stream()
  2. .sorted((a,b)->a.getAge().compareTo(b.getAge()))
  3. .collect(toList());
  4.  
  5. result = list.stream()
  6. .sorted(Comparator.comparing(Person::getAge))
  7. .collect(toList());

6.8、是否匹配任意一个元素anyMatch

anyMatch用于判断流中是否存在至少一个元素满足指定的条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。

  1. //如,判断list中是否有学生:
  2. boolean result = list.stream().anyMatch(Person::isStudent);
  3. //等价于
  4. boolean result = list.stream().anyMatch(p->p.getIsStudent());

6.9、是否匹配所有元素:allMatch

allMatch用于判断流中的所有元素是否都满足指定条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。

  1. 如,判断是否所有人都是学生:
  2. boolean result = list.stream().allMatch(Person::isStudent);

6.10、是否未匹配所有元素:noneMatch

noneMatch与allMatch恰恰相反,它用于判断流中的所有元素是否都不满足指定条件:

  1. boolean result = list.stream().noneMatch(Person::isStudent);

6.11、获取任一元素findAny

findAny能够从流中随便选一个元素出来,它返回一个Optional类型的元素。

  1. Optional<Person> person = list.stream().findAny();

6.12、获取第一个元素

  1. Optional<Person> person = list.stream().findFirst();

7、聚合操作-Stream.reduce

  Stream.reduce,常用的方法有average, sum, min, max, and count,返回单个的结果值,并且reduce操作每处理一个元素总是创建一个新值

  归约是将集合中的所有元素经过指定运算,折叠成一个元素输出,如:求最值、平均数等,这些操作都是将一个集合的元素折叠成一个元素输出。
  在流中,reduce函数能实现归约。

  1. T reduce(T identity, BinaryOperatoraccumulator)
  1. // identity:它允许用户提供一个循环计算的初始值。accumulator:计算的累加器,其方法签名为apply(T t,U u),在该reduce方法中第一个参数t为上次函数计算的返回值,
    // 第二个参数u为Stream中的元素,这个函数把这两个值计算apply,得到的和会被赋值给下次执行这个方法的第一个参数。有点绕看代码:
  2. int value = Stream.of(1, 2, 3, 4).reduce(100, (sum, item) -> sum + item);
  3. Assert.assertSame(value, 110);
  4. /* 或者使用方法引用 */
  5. value = Stream.of(1, 2, 3, 4).reduce(100, Integer::sum);
  6. //这个例子中100即为计算初始值,每次相加计算值都会传递到下一次计算的第一个参数。

reduce还有其它两个重载方法:
Optionalreduce(BinaryOperatoraccumulator):与上面定义基本一样,无计算初始值,所以他返回的是一个Optional。
U reduce(U identity, BiFunction accumulator, BinaryOperator combiner):与前面两个参数的reduce方法几乎一致,你只要注意到BinaryOperator其实实现了BiFunction和BinaryOperator两个接口。
Integer类还提供了min、max等一系列数值操作,当流中元素为数值类型时可以直接使用。

8、 聚合操作-Stream.collect

  收集器用来将经过筛选、映射的流进行最后的整理,可以使得最后的结果以不同的形式展现。
  流由一个个元素组成,归约就是将一个个元素“折叠”成一个值,如求和、求最值、求平均值都是归约操作。

Collectors
  toList()、toSet、joining、mapping、sum、age、group、max、min

8.1、count

  1. long count = list.stream().count();
  2. Long count2 = list.stream().collect(Collectors.counting());

8.2、max,min

  1. Optional<Person> personOptionalMax = list.stream().max(Comparator.comparing(p -> p.getAge()));
  2. Optional<Person> personOptionalMax1 = list.stream().collect(maxBy(Comparator.comparing(Person::getAge)));
  3.  
  4. Optional<Person> personOptionalMin = list.stream().min(Comparator.comparing(p -> p.getAge()));
  5. Optional<Person> personOptionalMin1 = list.stream().collect(minBy(Comparator.comparing(Person::getAge)));

8.3、sum、avg

  1. Integer sum = list.stream().collect(summingInt(Person::getAge));
  2. Double avg = list.stream().collect(averagingInt(Person::getAge));

8.4、summarizingInt一次性计算所有归约操作

Collectors.summarizingInt函数能一次性将最值、均值、总和、元素个数全部计算出来,并存储在对象IntSummaryStatisics中。
可以通过该对象的getXXX()函数获取这些值。

  1. DoubleSummaryStatistics dss = list.stream().collect(Collectors.summarizingDouble(p->p.getAge()));
  2. double average=dss.getAverage();
  3. double max=dss.getMax();
  4. double min=dss.getMin();
  5. double sum1=dss.getSum();
  6. double count1=dss.getCount();

8.5、Join

  1. String names = list.stream().collect(Collectors.joining());
  2. //每个字符串默认分隔符为空格,若需要指定分隔符,则在joining中加入参数即可:
  3. String names = list.stream().collect(Collectors.joining(", "));

8.6、自定义规约

若你需要自定义一个归约操作,那么需要使用Collectors.reducing函数,该函数接收三个参数:
第一个参数为归约的初始值
第二个参数为归约操作进行的字段
第三个参数为归约操作的过程

  1. //例:计算所有人的年龄总和
  2. Optional<Integer> sumAge = list.stream().collect(Collectors.reducing(0,Person::getAge,(i,j)->i+j));

Collectors.reducing方法还提供了一个单参数的重载形式。
你只需传一个归约的操作过程给该方法即可(即第三个参数),其他两个参数均使用默认值。
第一个参数默认为流的第一个元素
第二个参数默认为流的元素
这就意味着,当前流的元素类型为数值类型,并且是你要进行归约的对象。

  1. //例:采用单参数的reducing计算所有人的年龄总和
  2. Optional<Integer> sumAge = list.stream().filter(Person::getAge).collect(Collectors.reducing((i,j)->i+j));

8.7、结果收集

Collectors.toList()、Collectors.toMap、Collectors.toSet

注意:其中Collectors.toMap方法的第三个参数为键值重复处理策略,如果不传入第三个参数,当有相同的键时,会抛出一个IlleageStateException。

8.8、分组和分区

对具有相同特性的值进行分组是一个很常见的任务,Collectors提供了一个groupingBy方法,方法签名为:

  1. Collector<T,?,Map> groupingBy(Function classifier, Collector downstream)

classifier:一个获取Stream元素中主键方法。downstream:一个操作对应分组后的结果的方法。这里可进行很多分组后的二次操作,如再次分组,统计等

  1. //示例 根据年龄分组
  2. Map<Integer, List<Person>> collect = list.stream().collect(Collectors.groupingBy(p -> p.getAge(), Collectors.toList()));

多级分组

  1. //先按照年龄分组,再分组内再次按照名称分组
  2. Map<Integer, Map<String, List<Person>>> collect1 = list.stream().collect(
  3. Collectors.groupingBy(p -> p.getAge(), Collectors.groupingBy(t -> t.getName())));

分区是分组的一种特殊情况,它只能分成true、false两组。

  分区使用partitioningBy方法,该方法接收一个Lambda表达式,该表达是必须返回boolean类型,partitioningBy方法会将Lambda返回结果为true和false的元素各分成一组。

  partitioningBy方法返回的结果为Map< Boolean,List< T>>。

  此外,partitioningBy方法和groupingBy方法一样,也可以接收第二个参数,实现二级分区或对分区结果进行统计。

  groupingBy与partitioningBy,它们的区别是partitioningBy为键值为Boolean类型的groupingBy,这种情况下它比groupingBy更有效率。

9、数值流的使用

采用reduce进行数值操作会涉及到基本数值类型和引用数值类型之间的装箱、拆箱操作,因此效率较低。 当流操作为纯数值操作时,使用数值流能获得较高的效率。

1、将普通流转换成数值流
  StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream,也提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。
  如,将Person中的age转换成数值流:

  1. IntStream stream = list.stream().mapToInt(Person::getAge);

2 数值计算

  每种数值流都提供了数值计算函数,如max、min、sum等。
  如,找出最大的年龄:

  1. OptionalInt maxAge = list.stream().mapToInt(Person::getAge).max();

  由于数值流可能为空,并且给空的数值流计算最大值是没有意义的,因此max函数返回OptionalInt,它是Optional的一个子类,能够判断流是否为空,并对流为空的情况作相应的处理。
  此外,mapToInt、mapToDouble、mapToLong进行数值操作后的返回结果分别为:OptionalInt、OptionalDouble、OptionalLong

 

010-jdk1.8版本新特性二-Optional类,Stream流的更多相关文章

  1. 【Java8新特性】Optional类在处理空值判断场景的应用 回避空指针异常 编写健壮的应用程序

    一.序言 空值异常是应用运行时常见的异常,传统方式为了编写健壮的应用,常常使用多层嵌套逻辑判断回避空指针异常.Java8新特性之Optional为此类问题提供了优雅的解决方式. 广大程序员朋友对空值异 ...

  2. java8新特性之Optional类

    NullPointException可以说是所有java程序员都遇到过的一个异常,虽然java从设计之初就力图让程序员脱离指针的苦海,但是指针确实是实际存在的,而java设计者也只能是让指针在java ...

  3. JAVA 8 新特性 __ Optional 类

    Optional 类是一个可以作为null容器的对象,若值存在调用isPresent()就返回 true,调用get()会返回该对象. Optional是一个容器,可以保存类型T的值,或者仅仅保存nu ...

  4. java8新特性六-Optional 类

    Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者仅仅保 ...

  5. 【Java8新特性】Optional 类

    概述 Optional 类是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. Optional 是个容器:它可以保存类型T的值,或者 ...

  6. Java8新特性之方法引用&Stream流

    Java8新特性 方法引用 前言 什么是函数式接口 只包含一个抽象方法的接口,称为函数式接口. 可以通过 Lambda 表达式来创建该接口的对象.(若 Lambda 表达式抛出一个受检异常(即:非运行 ...

  7. JDK1.8新特性之Optional

    概念 Optional 是JDK1.8中出现的一个容器类,代表一个值存在或者不存在.原来使用null表示一个值不存在,现在Optional可以更好的表达这个概念.并且可以避免空指针异常. 场景分析 需 ...

  8. 各版本JDK1.5-1.8新特性

    概述 一jdk15新特性 泛型 foreach 自动拆箱装箱 枚举 静态导入Static import 元数据Metadata 线程池 Java Generics 二jdk16新特性 Desktop类 ...

  9. JDK各版本新特性!

    1.JDK1.5 新特性 1.自动装箱与拆箱:自动装箱的过程:每当需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中.自动拆箱的过程:每当需要一个值时,被装箱对象中的值就被自动地提取 ...

随机推荐

  1. 关于vc工程包含多个lib库老是提示无法打开问题

    在一个VC项目中,我要包含五个lib库,我在连接器->常规->附加库目录中输入了正确的库包含路径,然后再连接器->输入->附加依赖项中输入:ws2_32.lib;wsock32 ...

  2. Xcode - 添加自定义代码提示

    在开发过程中我们要学会去模仿苹果的一些用法,这样才能让开发更有效率,更规范. 1.苹果自带的代码片段提示 代码片段就是你在Xcode中敲for然后回车,你会看到 for (<#initializ ...

  3. 安装windows7/8/10到U盘或移动硬盘

    https://jingyan.baidu.com/article/e52e36156f6ad240c60c518c.html jpg改rar

  4. open-falcon之HBS

    功能 处理agent心跳请求,并将agent信息(ip.hostname.agent_version.plugin_version)等信息入库(portal库) 为agent提供执行run api的白 ...

  5. 微信小程序实例源码大全2

    wx-gesture-lock  微信小程序的手势密码 WXCustomSwitch 微信小程序自定义 Switch 组件模板 WeixinAppBdNovel 微信小程序demo:百度小说搜索 sh ...

  6. 【java工具】AES CBC加密

    一.定义 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先 ...

  7. os.walk的用法

    import os path = 'C:\\aa' for root,dirs,files in os.walk(path): print("Root=",root,'dirs=' ...

  8. 基于spring-cloud的微服务(1) 服务注册中心eureka

    eureka是Netflix提供的服务注册中心组建,springcloud将其做了封装,作为自己的微服务架构中的一个注册中心组建 下面的例子在IDEA中启动一个eureka的实例,然后提供一个prov ...

  9. 9.11 Django视图 view和路由

    2018-9-11 16:34:16 2018-9-11 19:00:24 越努力,.越幸运! Django框架参考: https://www.cnblogs.com/liwenzhou/p/8296 ...

  10. 关于webpy模板自动HTML转义的问题

    注意: web.py 将会转义任何任何用到的变量,所以当你将 name 的值设为是一段 HTML 时,它会被转义显示成纯文本.如果要关闭该选项,可以写成 $:name 来代替 $name. 如果我们想 ...