java8 新特性

java8 新特性

1 Lambda表达式

  • lambda 是一个匿名函数,

lambda 表达式基本语法: java8中引入了一个新的操作符:-> lambda 操作符。

结构分为两部分:

  • 左侧:lambda表达式的参数列表
  • 右侧:Lambda表达式中需要执行的功能, lambda体

语法格式

  • 语法格式一: 无参数,无返回值
    • () -> {}, 例如: Runnable
  • 语法格式二: 一个参数,无返回值, 小括号可以省略不写
    • (x) -> {}, 例如: Consumer,
  • 语法格式三:有两个以上参数,有返回值, 如果只有一条语句, return和大括号可以省略不写
    • (x, y) -> {return }, 例如:Comparator
  • 语法格式六:lambda的参数列表的数据类型可以省略, 因为JVM编译器通过上下文推断出,数据类型,即“类型推断”。

上联:左右遇一括号省,

下联:左侧推断类型省

横批: 能省则省

lambda表达式需要函数式接口支持。

2 函数式接口

函数式接口: 接口中只有一个抽象方法的接口, 可以使用 @FunctionalInterface 修饰。

2.1 java核心函数式接口

  • Consumer<T>: 消费型接口

    • void accept(T t); :accept:
    //Consumer<T> 消费型接口
    @Test
    public void test1() {
       happy(1000, x -> System.out.println("吃饭花了" + x + "元"));
    }
    public void happy(double money, Consumer<Double> consumer) {
       consumer.accept(money);
    }
    
  • Supplier<T>: 供给型接口

    • T get();
        @Test
        public void test2() {
            List<Integer> list = getNumberlist(10, () -> (int) (Math.random() * 100));
            list.forEach(System.out::println);
        }
        //需求:产生指定一些整数, 并放入集合中
        public List<Integer> getNumberlist(int num, Supplier<Integer> supplier) {
            List<Integer> list = new ArrayList<>();
    
            for (int i = 0; i < num; i++) {
                list.add(supplier.get());
            }
            return list;
        }
    
  • Function<T, R> : 函数型接口

    • R apply(T t);
        //Function<T,R>  函数型接口
        @Test
        public void test3() {
            System.out.println(getLength("ddd", x -> x.length()));
        }
        public Integer getLength(String str, Function<String, Integer> function) {
            return function.apply(str);
        }
    
  • Predicate<T>: 断言型接口

    • boolean test(T t)
        @Test
        public void test4() {
            List<String> list = Arrays.asList("aaa", "bbb", "aa");
            List<String> filterList = filter(list, x -> x.length() > 2);
            filterList.forEach(System.out::println);
        }
        public List<String> filter(List<String> list, Predicate<String> predicate) {
            List<String> list1 = new ArrayList<>();
            for (String s : list) {
                if (predicate.test(s)) {
                    list1.add(s);
                }
            }
            return list1;
        }
    

2.2 java 其他函数式接口

函数式接口 参数类型 返回类型 用途
BiFunction<T,U,R> T, U U 对类型为T,R 参数应用操作,返回 R 类型的结果。包含方法为 R apply(T t, U u)
UnaryOperator<T> T T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为 T apply(T t);
BinaryOperator<T> T, T T 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果。包含方法: T apply(T t1, T t2)
BiConsumer<T,U> T, U void 代表了一个接受两个输入参数的操作,并且不返回任何结果。 void accept(T t, U u)
ToIntFunction<T> ToLongFunction<T> ToDoubleFunction<T> T int long double 接受两个输入参数,分别返回一个int,long,double类型结果。
BiPredicate<T,U> T,U boolean 代表了对两个参数的断言操作(基于Boolean值的方法)

函数式接口通过一个单一的功能来表现。例如,带有单个compareTo方法的比较接口,被用于比较的场合。Java 8 开始定义了大量的函数式接口来广泛地用于lambda表达式。

BiFunction<Integer, Integer, Employee> bf1 = (x, y) -> new Employee(x, y);  //返回第三个参数
BiPredicate<String, String> bp = (x, y) -> x.equals(y);  //返回 boolean

3 :black_nib:引用

3.1 方法引用

:notebook:语法格式

  • 对象:: 实例方法名:
Consumer<String> con = (x) -> System.out.println(x);
Consumer<String> consumer = System.out::print;
  • 类::静态方法名
Supplier<Double> supplier = Math::random;
  • 类:: 实例方法名
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp1 = String::equals;
  • lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
  • 若 lambda 参数列表中的第一参数是实例方法的调用者, 而第二个参数是实例方法的参数时,可以使用 ClassName::method

3.2 构造器引用

格式:

  • ClassName::new

实例:

  • 无参数的构造器引用
//lambda
Supplier<Employee> sup = () -> new Employee();
//构造器引用
Supplier<Employee> sup2 = Employee::new;
  • 一个参数的构造器引用
//lambda
Function<Integer, Employee> function = x -> new Employee(x);
function.apply(111);
// 有1个参数的构造器引用
Function<Integer, Employee> function1 = Employee::new;
function1.apply(101);  // return new Employee(101)
  • 有2个参数的构造器引用
BiFunction<Integer, Integer, Employee> bf1 = (x, y) -> new Employee(x, y);
// 有2个参数的构造器引用
BiFunction<Integer, Integer, Employee> bf = Employee::new;
bf.apply(10, 1000);  //return new Employee(10, 1000);

注意: 需要调用的构造器参数列表要与函数式接口中抽象方法的参数列表保持一致。

3.3 数组引用

语法格式:

  • Type::new
//lambda
Function<Integer, String[]> function = (x) -> new String[x];
String[] apply = function.apply(10);

//数组引用
Function<Integer, String[]> function1 = String[]::new;
String apply1 = function1.apply(10);  //new String[10]

4. :artificial_satellite:Stream API

Stream API (java.util.stream.*)

4.1 创建 Stream 流

  1. 通过 Collection 的 stream(串行) 方法或者 parallelStream(并行) 方法创建集合流
Stream<String> stream = list.stream();
Stream<String> stringStream = list.parallelStream();
  1. 通过 Arrays 的静态方法 stream 获取数组流
Employee[] employees = new Employee[10];
Stream<Employee> stream1 = Arrays.stream(employees);
  1. 通过 Stream 类的静态方法of获取流
Stream<String> stream = Stream.of("aa", "bb", "cc");
  1. 创建无限流

    1. 迭代

      Stream<Integer> stream = Stream.iterate(0, x -> x + 2);
      
    2. 生成

      Stream<Double> stream2 = Stream.generate(() -> Math.random());
      

4.2 中间操作

多个中间操作可以连接起来形成一个流水线, 除非流水线上触发终止操作, 否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

筛选与切片

方法 描述
filter(Predicate p) 接收 lambda, 从流中排除某些元素
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素(可能需要自己手动重写方法)
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素, 返回一个扔掉了前n个元素的流。若流中元素不足n个, 则返回一个空流。与 limit(n) 互补。

映射

方法 描述
map 接收 lambda, 将元素转换成其他形式或提取信息, 接收一个函数作为参数,该函数会被应用到每个元素。
flatMap 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream

map 和 flatMap 类似于 List 的 add方法和 addAll 方法

排序

方法 描述
sorted() 自然排序
sorted(Comparator com) 定制排序
Comparator<String> comparator = (x,y) ->  x.length() - y.length();
        list.stream()
                .sorted(comparator)
                .forEach(System.out::println);

4.3 终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值。例如:List, Integer, 甚至是 void。

查找和匹配

方法 描述
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素个数
max() 返回流中最大值
min() 返回流中最小值
  • Optional
Optional<Employee> op = employees.stream()
                .sorted((e1, e2) -> -Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst(); //有可能为空就返回 Optional
System.out.println(op.get());

规约

方法 描述
reduce(T identity, BinaryOperator) 可以将流中元素反复结合起来, 得到一个值。ideantity: 起始值, 返回值为 T
reduce(BinaryOperator) 返回值为 Optinal ,因为没有起始值,可能为 null
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
Integer sum = list.stream().reduce(0, (x, y) -> x + y);

map 和 reduce 的连接通常称为 map-reduce 模式, 因 Google 用它来进行网络搜索而出名。

收集

方法 描述
collect(Collector collector);
collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner);

Collector 接口中方法的实现决定了如何对流进行收集操作(如收集到List , Set, Map)。但是 Collectors 工具类提供了很多静态方法, 可以方便地创建常见收集器实例, 具体方法与实例如下表。

        //转化为 List
        List<String> list = employees.stream().map(Employee::getUsername).collect(Collectors.toList());
        //转化为 Set
        Set<Double> set = employees.stream().map(Employee::getSalary).collect(Collectors.toSet());
        //转化为 HashSet
        HashSet<String> set1 = employees.stream().map(Employee::getUsername).collect(Collectors.toCollection(HashSet::new));
        //转化为 LinkedBlockingQueue
        LinkedBlockingQueue<Employee> queue = employees.stream().collect(Collectors.toCollection(LinkedBlockingQueue::new));
        //元素总数
        Long sum = employees.stream().collect(Collectors.counting());
        //工资平均值
        Double avg = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        //总和
        Double sum1 = employees.stream().collect(Collectors.summingDouble(Employee::getSalary));
        //最大值
        Optional<Employee> max = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        //最小值
        Optional<Employee> min = employees.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        //分组
        Map<Employee.Status, List<Employee>> map = employees.stream().collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
        //字符串拼接
        String str = employees.stream().map(Employee::getUsername).collect(Collectors.joining(","));
        System.out.println(str);
        //多级分组
        Map<Employee.Status, Map<String, List<Employee>>> map1 = employees.stream().collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(x -> x.getAge() > 30 ? "青年" : "少年")));
        System.out.println(map1);
        //分区
        Map<Boolean, List<Employee>> map2 = employees.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 33));
        //一次得到最大值,最小值,平均值...
        DoubleSummaryStatistics summary = employees.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(summary.getMax());

5 :musical_note:并行流和串行流

并行流就是把一个内容分成多个数据块, 并用不同的线程分别处理每个数据块的流。

java8 中将并行进行了优化, 我们可以很容易的对数据进行并行操作。``Stream API可以声明性地通过parallel()sequential()` 在并行流与顺序流之间进行切换。

5.1 fork/join 框架

Fork/Join 框架: 就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总 。(类似于分治算法)

[图片上传失败...(image-119780-1583213642769)]

和线程池区别

采用 “工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果
某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行。这种方式减少了线程的等待时间,提高了性能 。

fork/join 使用例子

public class ForkJoinCalcalue extends RecursiveTask<Long> {
    private long start;
    private long end;

    private static final long THRESHOLD = 10000;

    public ForkJoinCalcalue(long start, long end) {
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;
        //小于临界值进行累加
        if (length <= THRESHOLD) {
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long mid = start + ((end - start) >> 1);
            ForkJoinCalcalue left = new ForkJoinCalcalue(start, mid);
            //拆分子任务,同时压入线程队列
            left.fork();
            ForkJoinCalcalue right = new ForkJoinCalcalue(mid + 1, end);
            right.fork();
            //合并
            return left.join() + right.join();
        }
    }
}

//调用
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinCalcalue(0, 100000000000L);
Long sum = pool.invoke(task);

在java8后,对其进行了简化,java8的并行流底层就是 fork/join, 简化代码:

OptionalLong sum = LongStream.rangeClosed(0, 100000000000L)
                .parallel()
                .reduce(Long::sum);

6 Optional 类

Optional<T> 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

常用方法:

Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional  

7 默认方法和静态方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。

接口默认方法的” 类优先” 原则

若一个接口中定义了一个默认方法,而另外一个父类或接口中
又定义了一个同名的方法时。

  • 选择父类中的方法。如果一个父类提供了具体的实现,那么
    接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一个父接口提供一个默认方法,而另一个接
    口也提供了一个具有相同名称和参数列表的方法(不管方法
    是否是默认方法), 那么必须覆盖该方法来解决冲突

关于作者: ziftness
github: https://github.com/zfitness
博客: https://zfitness.github.io/

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

推荐阅读更多精彩内容

  • 本笔记来自 计算机程序的思维逻辑 系列文章 Lambda表达式 Lambda表达式 语法 匿名函数,由 -> 分隔...
    码匠阅读 445评论 0 6
  • 为什么使用 Lambda 表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传...
    强某某阅读 14,707评论 0 15
  • 1.Lamda表达式 Lambda表达式的作用主要是用来简化接口的创建,使用Lambda表达式接口必须是函数式接口...
    影风扬阅读 342评论 0 0
  • Java Lambda概要 Java Lambda表达式是一种匿名函数;它没有声明的方法,即没有访问修饰符、返回值...
    小波同学阅读 1,909评论 0 47
  • 一、重要数据结构和JVM的改动 1.HashMap的改动 HashMap维护了一个Entry数组,put(K ke...
    一只爱java的猫阅读 445评论 0 0