java 8 新特性

新特性的优势

  • 速度更快
  • 代码更少(新语法:Lambda 表达式
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常Optional

一、lambda 表达式

背景:用“λ演算法”去理解,为什么函数式编程会有更少的bug

Lambda 是匿名函数,可以把 Lambda 表达式理解为是一段可以当作数据去传递的代码,从而可以写出更简洁、更灵活的代码。

Lambda 表达式的专用操作符为->。它将 Lambda 分为两个部分:

  • 左侧:指定参数(只书写符号,编译器会自动推断参数类型)
  • 右侧:指定了 Lambda 体。

函数式接口

函数式接口只包含一个抽象方法。
函数式接口可以指向一个由Lambda表达式创建的对象。
函数式接口建议加上@FunctionalInterface注解,该注解用于检查接口是否为函数式接口。
如果将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

四种基本类型函数式接口

接口 参数 返回 方法
Consumer<T> T void void accept(T t)
Supplier<T> T T get()
Function<T, R> T R R apply(T t)
Predicate<T> T boolean boolean test(T t)

方法引用

方法引用可以简写lambda表达式中已经存在的方法。
使用操作符::将方法名和对象或类的名字分隔开来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Consumer con = (x) -> System.out.println(x);
// 等价于
c = System.out::println;

Binary<Double> bin = (x, y) -> Math.pow(x, y);
// 等价于
b = Math::pow;

// 实例方法引用
Comparator<String> cmp = String::compareTo;
System.out.println(cmp.compare("aa", "ab"));

// 构造器引用 ClassName::new
Function<Integer, MyClass> f1 = MyClass::new;
MyClass myclass = func.apply(1);

// 数组引用 type[]::new
Function<Integer, Integer[]> f2 = Integer[]::new;

二、Stream API(java.util.stream.*)

Stream 是根据数据源(集合、数组等)生成的元素序列。
Stream 用于处理集合,比如查询、过滤和映射数据,类似于SQL。

特性

  • Stream 不存储数据,它是数据源的一个视图。
  • Stream 不改变源数据,而是返回一个持有结果的新Stream。
  • Stream 操作不会立即执行,只有等到用户真正需要结果的时候才会执行。
  • Stream 遍历一次后就失效,如果要再次遍历需要重新生成。
  • Stream API 支持并行执行操作。

1. 创建流

  1. 通过已有集合创建流

    1
    2
    List<String> strings = Arrays.asList("A", "B", "C", "D", "E", "F");
    Stream<String> stream = strings.stream();
  2. 通过Stream创建流

1
Stream<String> stream = Stream.of("A", "B", "C", "D", "E", "F");

2. 常用中间操作

  • filter
    filter 方法用于通过设置的条件过滤出元素。
1
2
3
4
5
List<String> strings = Arrays.asList("A", "", "B", "C", "D");
// 过滤空字符串
strings.stream()
.filter(string -> !string.isEmpty())
.forEach(System.out::println);
  • map
    map 方法用于映射每个元素到对应的结果。
1
2
3
4
5
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6);
// 输出每个数字的平方
numbers.stream()
.map(i -> i*i)
.forEach(System.out::println);
  • limit/skip
    limit 返回 Stream 的前 n 个元素;skip 则是跳过前 n 个元素。
1
2
3
4
5
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6);
// 输出前4个数字
numbers.stream()
.limit(4)
.forEach(System.out::println);
  • sorted
    sorted 方法用于对流进行排序。
1
2
3
4
5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 降序输出数字
numbers.stream()
.sorted((a, b) -> b - a)
.forEach(System.out::println);
  • distinct
    distinct 方法给流中的元素去重。
1
2
3
4
5
List<Integer> numbers = Arrays.asList(1, 1, 3, 4, 4, 4);
// 去除重复数字
numbers.stream()
.distinct()
.forEach(System.out::println);

stream 中间操作的图示:
stream中间操作的图示

3. 常用最终操作

Stream的中间操作得到新Stream,最终操作则把新的Stream转换成需要的类型。
最终操作会消耗流,产生一个最终结果,所以最终操作后无法再使用流。

  • forEach
    forEach 来迭代流中的每个数据。

  • count
    count 对数据计数。

  • collect
    collect 就是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇总结果。

1
2
3
4
5
6
7
8
List<String> strings = Arrays.asList("A", "B", "C", "d", "D", "e", "f");
// 字符串转大写并且去重最后返回列表
strings = strings.stream()
.map(string -> string.toUpperCase())
.distinct()
.collect(Collectors.toList());

System.out.println(strings);

三、新日期时间 API(Java.time.*)

四、接口中的默认方法与静态方法

五、Optional 容器类(java.util.Optional)

六、重复注解与类型注解