java8新特性-Stream和Optional

Stream

Stream 是Java SE 8类库中新增的关键抽象,它被定义于 java.util.stream (这个包里有若干流类型: Stream 代表对象引用流,此外还有一系列特化流,如 IntStream,LongStream,DoubleStream等 ),Java 8 引入的的Stream主要用于取代部分Collection的操作,每个流代表一个值序列,流提供一系列常用的聚集操作,可以便捷的在它上面进行各种运算。集合类库也提供了便捷的方式使我们可以以操作流的方式使用集合、数组以及其它数据结构;
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

stream 相对于 Collection 的优点
1、无存储:流并不存储值;流的元素源自数据源(可能是某个数据结构、生成函数或I/O通道等等),通过一系列计算步骤得到;
2、函数式风格:对流的操作会产生一个结果,但流的数据源不会被修改;
3、惰性求值:多数流操作(包括过滤、映射、排序以及去重)都可以以惰性方式实现。这使得我们可以用一遍遍历完成整个流水线操作,并可以用短路操作提供更高效的实现;
4、无需上界:不少问题都可以被表达为无限流(infinite stream):用户不停地读取流直到满意的结果出现为止(比如说,枚举 完美数 这个操作可以被表达为在所有整数上进行过滤);集合是有限的,但流可以表达为无线流;
5、代码简练:对于一些collection的迭代处理操作,使用 stream 编写可以十分简洁,如果使用传统的 collection 迭代操作,代码可能十分啰嗦,可读性也会比较糟糕;

Stream流的创建

1、集合接口有两个方法来生成流:
stream() − 为集合创建串行流。
parallelStream() − 为集合创建并行流。parallelStream默认的并行线程数为cpu核数。

public static void main(String[] args) {
    /**
         * 定义集合l1 并为集合创建串行流
         */
    List<String> l1 = Arrays.asList("周星驰", "周杰伦", "周星星", "周润发");
    // 返回串行流
    l1.stream();
    // 返回并行流
    l1.parallelStream();
}

2、值创建流
Stream.of(T…) : Stream.of(“aa”, “bb”) 生成流

//值创建流 生成一个字符串流
Stream<String> stream = Stream.of("java8", "Spring", "SpringCloud");
stream.forEach(System.out::println);

3、数组创建流
根据参数的数组类型创建对应的流。
Arrays.stream(T[ ])
Arrays.stream(int[ ])
Arrays.stream(double[ ])
Arrays.stream(long[ ])

// 只取索引第 1 到第 2 位的:
int[] a = {1, 2, 3, 4};
Arrays.stream(a, 1, 3).forEach(System.out :: println);

4、文件生成流

//每个元素是给定文件的其中一行
Stream<String> stream02 = Files.lines(Paths.get("data.txt"));

5、函数生成流
两个方法:
iterate : 依次对每个新生成的值应用函数
generate :接受一个函数,生成一个新的值

/生成流,首元素为 0,之后依次加 2
Stream.iterate(0, n -> n + 2)
//生成流,为 0 到 1 的随机双精度数
Stream.generate(Math :: random)
//生成流,元素全为 1
Stream.generate(() -> 1)
stream 的操作种类

中间操作
当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”;
中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线;
stream 提供了多种类型的中间操作如下:
1、筛选与切片
filter——接收 Lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素

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

3、排序
sorted()——自然排序(Comparable)
sorted(Comparator com)——定制排序

终止操作
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——归约:可以将流中元素反复结合起来,得到一个值。
collect——收集:将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法,转换list,set等,参数为Collectors中的 toList,toSet,toCollection,toMap。

常用操作
1、两个集合匹配并赋值

            list3= list1.stream()
                    .map(e -> list2.stream()
                            .filter(e1 -> e.name().equals(e1.name()))
                            .findFirst()
                            .map(e1 -> {
                                e.setSex(e1.getSex());
                                e.setAge(e1.getAex());
                                return e;
                            }).orElse(null))
                    .collect(Collectors.toList());
Optional

Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。Optional 类的引入很好的解决空指针异常。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

看如下代码:

String[] strs = {"1","2"};
if(strs == null){
      System.out.println("0");
 }else{
     System.out.println(strs.length);
 }

这是打印strs数组长度的实现。
如用optional实现如下:

String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElse(0));

可以看到用Optional的方式更加优雅。

Optional对象的创建

我们看下Optional类的部分源码:

private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

从源码可以看出optional为一个单例,两个构造方法都是private型的。
Optional类提供了三个静态方法empty()、of(T value)、ofNullable(T value)来创建Optinal对象,示例如下:

// 1、创建一个包装对象值为空的Optional对象
Optional<String> optStr = Optional.empty();
// 2、创建包装对象值非空的Optional对象
Optional<String> optStr1 = Optional.of("optional");
// 3、创建包装对象值允许为空的Optional对象
Optional<String> optStr2 = Optional.ofNullable(null);
Optional 类典型接口的使用

下面以一些典型场景为例,列出Optional API常用接口的用法,并附上相应代码。
get()方法
简单看下get()方法的源码:

public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

可以看到,get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常。
isPresent()方法
isPresent()方法的源码:

public boolean isPresent() {
        return value != null;
    }

可以看到,isPresent()方法用于判断包装对象的值是否非空。此方法有点鸡肋,需谨慎使用。

ifPresent()方法
ifPresent()方法的源码:

public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

ifPresent()方法接受一个Consumer对象(消费函数),如果包装对象的值非空,运行Consumer对象的accept()方法。示例如下:

public static void printName(Student student)
    {
        Optional.ofNullable(student).ifPresent(u ->  System.out.println("The student name is : " + u.getName()));
    }

上述示例用于打印学生姓名,由于ifPresent()方法内部做了null值检查,调用前无需担心NPE问题。

filter()方法
filter()方法的源码:

public Optional<T> filter(Predicate<? super T> predicate) {
       Objects.requireNonNull(predicate);
       if (!isPresent())
           return this;
       else
           return predicate.test(value) ? this : empty();
   }

filter()方法接受参数为Predicate对象,用于对Optional对象进行过滤,如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象。举例如下:

public static void filterAge(Student student)
    {
        Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u ->  System.out.println("The student age is more than 18."));
    }

上述示例中,实现了年龄大于18的学生的筛选。

map()方法
map()方法的源码:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
       Objects.requireNonNull(mapper);
       if (!isPresent())
           return empty();
       else {
           return Optional.ofNullable(mapper.apply(value));
       }
   }

map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)。举例如下:

  public static Optional<Integer> getAge(Student student)
    {
        return Optional.ofNullable(student).map(u -> u.getAge()); 
    }

上述代码中,先用ofNullable()方法构造一个Optional对象,然后用map()计算学生的年龄,返回Optional对象(如果student为null, 返回map()方法返回一个空的Optinal对象)。

flatMap()方法
flatMap()方法的源码:

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

跟map()方法不同的是,入参Function函数的返回值类型为Optional类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象。以上面map中示例功能为例,进行faltMap()改写如下:

 public static Optional<Integer> getAge(Student student)
    {
        return Optional.ofNullable(student).flatMap(u -> Optional.ofNullable(u.getAge())); 
    }

orElse()方法
orElse()方法的源码:

public T orElse(T other) {
        return value != null ? value : other;
    }

orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)。如之前提到的代码:

String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElse(0));

orElseGet()方法
orElseGet()方法的源码:

 public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

orElseGet()方法与orElse()方法类似,区别在于orElseGet()方法的入参为一个Supplier对象,用Supplier对象的get()方法的返回值作为默认值。如:

String[] strs = {"1","2"};
System.out.println(Optional.ofNullable(strs).map(str->str.length).orElseGet(()->0));

orElseThrow()方法
orElseThrow()方法的源码:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
       if (value != null) {
           return value;
       } else {
           throw exceptionSupplier.get();
       }
   }

orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出:

String[] strs = {"1","2"};
        System.out.println(Optional.ofNullable(strs).map(str->str.length).orElseThrow(()-> new RuntimeException("0")) );

orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景。

参考:
https://www.cnblogs.com/chenglc/p/8087578.html
https://blog.csdn.net/Al_assad/article/details/82356606
https://www.jianshu.com/p/d81a5f7c9c4e#comments

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值