java8新特性-Stream和Optional

  • A+
所属分类:Java

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

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: