java8新特性

Posted jarvankuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java8新特性相关的知识,希望对你有一定的参考价值。

java8新特性:函数式编程,stream流,Optional 类!

参考文献:

理解、学习与使用 Java 中的 Optional

Java8之Stream流(一)基础体验

java8 stream流操作的flatMap(流的扁平化)

java中使用Lambda表达式的5种语法

Lambda语法

  • 标准写法

    Arrays.sort(arr, (String m, String n) -> Integer.compare(m.length(), n.length()));
    

    lambda表达式的标准写法由下面几点构成:

    • 以逗号分隔,以()关闭的形参:(Dog m, Dog n)
    • 箭头标记:->
    • 主体部分则是一个单表达式或者声明代码块。如下是单表达式形式:Integer.compare(m.getWeight(), n.getWeight())
  • 参数类型可以推断

    如果参数的类型可以根据上下文推断出来,则可以省略掉类型

  • 存在多行代码

    需要{} 括起来,且代码应该有明确的返回语句

    Arrays.sort(arr, (String m, String n) -> {
        if (m.length() > n.length())
            return -1;
        else
            return 0;
    });
    
  • 单个参数并可推断类型

    可以省略参数 “x” 的括号

  • 方法引用

    写成::形式,并省略参数。

    stream.forEach(System.out::println);
    
  • 没有参数

    () -> {for(int i=0; i<10; i++) doSomthing();}

Optional 类

包含有可选值的包装类,既可以含有对象也可以为空。

常用方法

  • ofNullable()

    创建Optional对象:传入的对象即可能是 null 也可能是非 null,不会抛出异常NullPointerException

  • ifPresent()

    选择性执行语句:该方法除了执行检查,还接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda 表达式:

    opt.ifPresent( u -> assertEquals(user.getEmail(), u.getEmail()));
    

    这个例子中,只有 user 用户不为 null 的时候才会执行断言。

  • orElseGet()

    返回默认值:这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果:

    User result = Optional.ofNullable(user).orElseGet( () -> user2);
    

    orElse() 和 orElseGet() 的不同之处: Optional 对象返回非空值后,orElse() 方法仍然创建了 默认值 对象,而orElseGet() 方法不创建 默认值 对象。

  • orElseThrow()

    在对象为空的时候抛出异常,而不是返回备选的值:

    @Test(expected = IllegalArgumentException.class)
    public void whenThrowException_thenOk() {
        User result = Optional.ofNullable(user)
          .orElseThrow( () -> new IllegalArgumentException());
    }
    

    这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException

  • map()

  • filter()

  • Optional 类的链式方法

    String result = Optional.ofNullable(user)
      .flatMap(User::getAddress)
      .flatMap(Address::getCountry)
      .map(Country::getIsocode)
      .orElse("default");
    

Stream流

  • 概念

    Java 8 中的 Stream 是对集合(Collection)对象功能的增强

    • 专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation)
    • 大批量数据操作 (bulk data operation)
    • 支持 Lambda 表达式
    • 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的
    • 作用更像一个高级版本的 Iterator,与迭代器不同点,Stream 可以并行化操作,迭代器只能命令式地、串行化操作
    • Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程
  • "终端操作"&"中间操作"

    • 终端操作: 会消费流,这种操作会产生一个结果的

      iterator()、 spliterator()、min()、max()、forEach()

    • 中间操作:会产生另一个流,类似管道

  • 缩减操作

    缩减操作的三个约束条件

    • 无状态
    • 不干预
    • 关联性

    reduce()-通用的方法,而min()和max(),count()这些操作称为特例缩减。

    T reduce(T identity, BinaryOperator<T> accumulator);//1
    Optional<T> reduce(BinaryOperator<T> accumulator);//2
    

    第一个版本的identity为初始化值(兼容单个值的情况),求和的时候为0,求积的时候它的值为1。

    其中的accumulator是一个BinaryOperator的类型,他是java.util.function包中声明的函数式接口,它扩展了BiFunction函数式接口.

    @FunctionalInterface
    public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    }
    
    @FunctionalInterface
    public interface BiFunction<T, U, R> {
       R apply(T t, U u);//notice
    }
    

    apply()对他的两个操作数(t,u)应用到同一个函数上,并返回结果

    # 直接函数式编程
    reduce((a, b) -> a + b)
    
  • 并行流

    对流调用一下parallel(),或者Collection.parallelStream()

    在reduce()的第三版本比较适合并行流,accumulator被称为累加器, combiner被称为合成器, combiner定义的函数将accumulator提到的两个值合并起来

    public interface Stream<T> extends BaseStream<T, Stream<T>> {
    //、、、忽略其他无关紧要的元素
    <U> U reduce(U identity,
              BiFunction<U, ? super T, U> accumulator,
              BinaryOperator<U> combiner);
    }
    
  • 顺序流&并行流&无序流之间的切换操作

    在使用并行流时,有时候流是无序的就能获得性能上的提升,可以通过BaseStream接口提供的unordered()方法把流转换成一个无序流之后,再进行各种操作

    forEach()方法不一定会保留并行流的顺序,如果在对并行流的每个元素执行操作时,也希望保留顺序,那么可以使用forEachOrdered()方法,它的用法和forEach()是一样的。

    forEach底层采用的是迭代器的方式。如果数据结构是ArrayList这种数据结构,那你可以采用for,但是你的数据结构如果是LinkList那你千万别再用for,应该果断采用forEach,因为数据一多起来的,for此时的效率低得可怜

  • 映射

    • filter方法

      filter方法的参数是一个Predicate对象,即一个从T到boolean的函数,返回为true的

    • map方法

      对一个流中的值进行某种形式的转换,返回的是对应类型的Stream对象。

    • flatMap方法

      flat -- 摊平,流的扁平化

      flatMap()操作能把原始流中的元素进行一对多的转换,并且将新生成的元素全都合并到它返回的流里面,即重新整合为一个流。

      将多个Stream连接成一个Stream,这时候不是用新值取代Stream的值,与map有所区别,这是重新生成一个Stream对象取而代之。

      String[] words = new String[]{"Hello","World"};
      List<String> a = Arrays.stream(words)
          .map(word -> word.split(""))
          .flatMap(Arrays::stream)
          .distinct()
          .collect(toList());
      a.forEach(System.out::print);
      // 输出 ["H","e","l","o","W","r","d"]
      
  • 收集功能

    • Collectors

      public static <T>  Collector<T, ?, List<T>> toList()
      public static <T> Collector<T, ?, Set<T>> toSet()
      

      用法:steam.collect(Collectors.toList)

    • collect方法

       <R> R collect(Supplier<R> supplier,
                        BiConsumer<R, ? super T> accumulator,
                        BiConsumer<R, R> combiner);
      

      类似上面的缩减操作。其中supplier指定如何创建用于保存结果的对象,比如,要使用ArrayList作为结果的集合,需要指定它的构造函数,accumulator函数是将一个元素添加到结果中,而combiner函数合并两个部分的结果

      private static void learnCollect() {
              List<HeroPlayerGold> lists = new ArrayList<>();
              lists.add(new HeroPlayerGold("盖伦", "RNG-Letme", 100));
              lists.add(new HeroPlayerGold("诸葛亮", "RNG-Xiaohu", 300));
              lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));
              lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));
              lists.add(new HeroPlayerGold("牛头", "RNG-Ming", 500));
      
      
              lists.stream().collect(HashSet::new,
                                     HashSet::add,
                                     HashSet::addAll
              ).forEach(System.out::println);
      }
      
  • Spliterator

    Spliterator是Java8新增的一种迭代器,Spliterator支持并行迭代。

Stream 常用方法

参考:JDK 8 通过Stream 对List,Map操作和互转

  • list 转 map

    List<Student> list = Arrays.asList(new Student(1, 18, "阿龙", GenderColumn.BOY.getCode()),
                                           new Student(2, 17, "小花", GenderColumn.GIRL.getCode()),
                                           new Student(3, 17, "阿浪", GenderColumn.LADYBOY.getCode()));
    // value 为对象 student -> student jdk1.8返回当前对象
    Map<Integer, Student> map = list.stream().collect(Collectors.toMap(Student::getId, student -> student));
    
  • map 转 map

    Map<String, Map<String, Object>> map1 = aggregationMap.entrySet().stream().collect(Collectors.toMap(
                    e -> e.getKey(),
                    e -> {
                        List<String> options = ((StringTerms) e.getValue()).getBuckets().stream().map(StringTerms.Bucket::getKeyAsString).collect(Collectors.toList());
                        Map<String, Object> map = new HashMap<>();
                        map.put("options", options);
                        return map;
                    }
            ));
    
    
  • map 转 list

    // stream map 转 list
    List<Map<String, Object>> paramMapList = aggregationMap.entrySet().stream().map((e) -> {
                List<String> options = ((StringTerms) e.getValue()).getBuckets().stream().map(StringTerms.Bucket::getKeyAsString).collect(Collectors.toList());
                Map<String, Object> map = new HashMap<>();
                map.put("k", e.getKey());
                map.put("options", options);
                return map;
    }).collect(Collectors.toList());
    
    

以上是关于java8新特性的主要内容,如果未能解决你的问题,请参考以下文章

2020了你还不会Java8新特性?Java 8新特性介绍

java8新特性——Lambda表达式

Java8新特性

java8新特性总结

Java8新特性

Java8新特性-官方库新特性