Java8使用分享

函数式接口

什么是?

  • @FunctionalInterface 注解了的接口类
  • 接口只有一个抽象方法。

java.util.function

  • Function<T, R> 功能函数 map()

  • Consumer<T> 消费函数 action()

  • Supplier<T> 生产函数 collect()

  • Predicate<T> 谓语判断函数 filter()

  • 包内其他接口说明

    // 类型限制,入参类型固定,返回值固定
    IntFunction<R> IntConsumer
    
    // 数量限制:两个参数在接口类加前缀:Bi Binary 二元,2个输入参数类型
    BiConsumer<T, U>
    BiFunction<T, U, R>
    
    // Operator接口,入参和返回参数一样,一元和二元
    UnaryOperator<T> extends Function<T, T>
    BinaryOperator<T> extends BiFunction<T,T,T>
    

其他函数式接口

  • Comparator<T> 比较器
  • Runnable 线程接口

如何用?

  • 创建接口实现类

  • 创建内部匿名类

  • 使用Lambda表达式

Lambda表达式

什么是?

用来表示一个行为或传递代码,用来创造函数式接口的实例(对象),匿名内部类也可以完成同样的事情。

  • 箭头标识 ->
箭头标识:
() -> 表达式  , s -> { ... }  , (a,b)-> {...} 

方法引用

如果一个Lambda只是直接调用这个方法,那么还是用名称去调用,而不是描述如何调用它。仅仅涉及单一方法的Lambda语法糖。

  • 官网说明methodreferences

  • 双冒号标识 :: 方法引用简化Lambda表达式。

    双冒号标识::,前面是目标引用,后面是方法名称,不需要括号,因为没有实际调用这个方法。
    Integer::sum  list::add String::length HashMap::new
    示例:
    (Apple a) -> a.getWeight()   等效  Apple::getWeight
    (String s) -> System.out.println(s)   等效 System.out::println
    (str, i) -> str.substring(i)  等效  String::substring 
    
Lambda 等效的方法引用
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new

如何用?

// JDK8之前 匿名内部类写法
new Thread(new Runnable(){// 接口名
    @Override
    public void run(){// 方法名
        System.out.println("Thread run()");
    }
}).start();

// JDK8之后 Lambda表达式写法
new Thread(() -> {
    System.out.println("Thread run()");
}).start();// 省略接口名和方法名

//  JDK8之前
list.sort(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.length() - o2.length();
    }
});
// JDK8之后
list.sort((s1,s2) -> s1.length()-s2.length());
list.sort(comparing(String::length));

// -> 和 双冒号
List<String> list = Arrays.asList("aaaa", "bbbb", "cccc");
list.forEach(s -> System.out.println(s)); // lambda
list.forEach(System.out::println); // method reference

Stream流

什么是?

Stream接口,主要是对集合对象功能的增强。简单理解是某种数据源的视图,数据源不能是Map。

Stream跟函数式接口关系非常紧密,没有函数式接口stream就无法工作。stream只能被“消费”一次。

从有序集合生成流时,会保留原有的顺序。慎用并行BaseStream的parallel()

如何用?

  • 获取Stream

    • 调用Collection.stream()或者Collection.parallelStream() 并行。
    • 调用Arrays.stream(T[] array)方法 。Stream<T> of(T... values) 静态方法。
  • Stream的操作

    • 中间操作只会生成一个标记了该操作的新stream

    • 结束操作会触发实际计算,计算完成后stream就会失效

  • Stream常用api

    • Stream map(Function<? super T,? extends R> mapper) 映射器(个数不变,类型可能改变)

    • Stream filter(Predicate<? super T> predicate) 过滤器

    • Stream sorted(Comparator<? super T> comparator) 排序器

    • Stream distinct()去重

    • void forEach(Consumer<? super T> action) 迭代器

    • Optional<T> reduce(BinaryOperator<T> accumulator) 从一组元素生成一个值。

    • <R, A> R collect(Collector<? super T, A, R> collector) 收集器,生成一个集合或Map对象。

    • boolean anyMatch(Predicate<? super T> predicate); 一个匹配

    • boolean allMatch(Predicate<? super T> predicate); 所有匹配

    • boolean noneMatch(Predicate<? super T> predicate); 没有匹配

    • Optional<T> findAny(); 查找任意一个 并行操作更快

    • Optional<T> findFirst(); 查找第一个

Stream的归集操作 reduce 和 collect

  • reduce操作,max()min() 都是reduce操作

    • 源码分析

      // reduce()最常用的场景就是从一堆值中生成一个值。
      Optional<T> reduce(BinaryOperator<T> accumulator)
      // identity 初始值    
      T reduce(T identity, BinaryOperator<T> accumulator)
      // identity 初始值,accumulator: 累加器,,combiner 并行计算
      <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
      
      // 求最大值
      Optional<T> max(Comparator<? super T> comparator);
      Optional<T> min(Comparator<? super T> comparator);
      
    • 使用示例

      // 1. 找出最长的单词
      Stream<String> stream  = Stream.of("I", "am", "Strean", "reduceMethod");
      String max = stream.reduce((s1,s2) -> s1.length()-s2.length() > 0 ? s1:s2).get();
      String max = stream.reduce("",(s1,s2) -> s1.length()-s2.length() > 0 ? s1:s2); // 比较时慎用初始值
      String max = stream.max((s1,s2) -> s1.length() - s2.length()).get();
      
      // 2. 求出数之和
      Stream<String> stream  = Stream.of("I", "am", "Strean", "reduceMethod");
      Stream<Integer> stream  = Stream.of(1,2,3,4,5,6,7,8,9,10);
      int sum = stream.reduce(0,(a,b) -> a+b );
      
      // 采用并发流求长度之和
      List<String> list = Arrays.asList("I", "am", "Strean", "reduceMethod");
      Stream<String> stream  = list.stream();// 并发流:parallelStream()
      int lengthSum = stream.reduce(0, (sum, str) -> {
          System.out.println("sum:"+sum+",str:"+str);
          return sum+str.length();},(a,b) -> {
          System.out.println("a:"+a+",b:"+b); // 标准流式没有
          return a+b;
      });
      
  • collect操作,终极武器

    一般流先接map映射器方法,再来归集操作

    • Collector是为Stream.collect()方法量身打造的接口,而Collectors是产生Collector接口的工具类,包含了CollectorImpl实现类。

    • 源码分析

      // Stream 的collect方法
      <R, A> R collect(Collector<? super T, A, R> collector);
      
      // supplier目前容器生产方式,accumulator累加器,元素如何加到容器中
      <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
      
    • 使用示例

      List<String> list = Arrays.asList("I", "am", "Strean", "reduceMethod");
      // 基础用法:
      List<String> newList = list.stream().filter(s -> s.length() >3 ).collect(Collectors.toList());
      Set<String> newSet = list.stream().filter(s -> s.length() >3 ).collect(Collectors.toSet());
      
      // 将list集合转成Map
      Map<String,Integer> map= list.stream().collect(Collectors.toMap(t->t, t -> t.length()));
      Map<String,Integer> map= list.stream().collect(Collectors.toMap(Function.identity(),String::legth));
      

Optional类

什么是?

java.util.Optional<T> 是一个容器类,代表一个值存在或不存在,不能被序列化。

如何用?

  • 创建Optional对象

    Optional<Student> optStu = Optional.empty();// 1.空对象
    Optional<Student> optStu = Optional.of(student); // 2.非空值
    Optional<Student> optStu = Optional.ofNullable(student); // 3.允许null值
    
  • 常用API,支持链式操作。

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
    public T orElse(T other)
    public T orElseGet(Supplier<? extends T> other)
    public void ifPresent(Consumer<? super T> consumer)
    public Optional<T> filter(Predicate<? super T> predicate)
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
    

时间日期类

Java8之前,Date类都是可变类。而Java8的Date和Time API提供 线程安全的不可变类且线程安全的。

  • LocalDateLocalTime , LocalDateTimeYearDurationPeriod

    // 年月日
    System.out.println("LocalDate.now():" + LocalDate.now()); // 2019-07-09
    // 时分秒
    System.out.println("LocalTime.now():" + LocalTime.now()); // 09:38:26.014
    // 完整日期
    System.out.println("LocalDateTime.now():" + LocalDateTime.now()); // 2019-07-09T09:38:26.015
    // Duration是用来计算两个给定的日期之间包含多少秒,多少毫秒
    LocalDate localDate = LocalDate.now();
    // Period:两个给定的日期之间包含多少天,,多少月或者多少年。记住整数,年,月,日
    Period period = Period.between(localDate, localDate.plus(30, ChronoUnit.DAYS));
    System.out.println(period.getMonths());
    
  • DateTimeFormatter

    LocalDateTime localDateTime = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    System.out.println("localDateTime格式化:" + dtf.format(localDateTime)); // 2019-07-09T10:54:20.661
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
    System.out.println("formatter格式化:" + formatter.format(localDateTime)); // 2019-07-09 10:54:20
    //  String timeStr = "2019-07-09 10:48:05"; 如果使用这个parse会出现错误
    formatter.parse(timeStr).get(ChronoField.DAY_OF_MONTH) // 9
    DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
    String timeStr = "2019-07-09T11:02:01.840";
    LocalDateTime time = LocalDateTime.parse(timeStr,dtf);
    
    /**
     * DateTimeFormatter won't parse dates with custom format "yyyyMMddHHmmssSSS"
     * https://bugs.openjdk.java.net/browse/JDK-8031085
     */
    

Java8与重构

方法参数传递由值传递,转变为类(对象)传递,匿名类,Lambda,方法引用。这是:值参数化到行为参数化的转变。

为了因对不断变化的需求,行为参数化。抽象化,使代码更加灵活,后期维护代码更加方便!

总结

学习三部曲:什么是? what 如何用? how 为什么? why

函数式编程又称λ演算(lambda calculus),匿名函数。在编程语言中的函数通常指的是方法。从接口式编程,到函数式编程!

Java8 提倡函数式编程:

  1. 简少代码量:减少for...each 语句,实现类,匿名内部类。
  2. 增加代码清晰度:方法内直接写明操作行为,更加好维护
  3. 提高计算效率:让并行计算更加简单
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,227评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,755评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,899评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,257评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,617评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,757评论 1 221
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,982评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,715评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,454评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,666评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,148评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,512评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,156评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,112评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,896评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,809评论 2 279
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,691评论 2 272