java8新特性
1 lambda表达式
1.1 定义
Lambda 是一个匿名函数,本质上是函数式接口的实例。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
1.2 细节
- 如果方法体中只有1条语句,可以省略{},如果是return语句,必须同时省略return和{};
- 如果参数列表只有一个参数,可以省略();
- 类型推断:参数列表中可以只写变量名,省略数据类型;
- 使用前提:当需要对一个函数式接口实例化时,才可以使用lambda表达式;
1.3 格式
-> : lambda操作符或者箭头操作符;
操作符左边:Lambda形参列表(接口中抽象方法的形参列表);
操作符右边:Lambda体(重写的抽象方法的方法体)
1.3.1 无参无返回值
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(111);
}
};
r.run();
如果方法体中只有1条语句,可以不用带{};
Runnable r1 = () -> {
System.out.println(2222);
System.out.println("1111");
};
r1.run();
1.3.2 有参无返回值
Consumer<String> c = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
c.accept("123");
如果只有一个参数,可以不写();
Consumer<String> c1 = s -> System.out.println(s);
c1.accept("456");
1.3.3 多个参数、多条语句且有返回值
Comparator<Integer> c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return Integer.compare(o1, o2);
}
};
int compare = c.compare(10, 8);
System.out.println(compare);
Comparator<Integer> c1 = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return Integer.compare(o1, o2);
};
System.out.println(c1.compare(8, 9));
Comparator<Integer> c1 = (o1, o2) -> {
return Integer.compare(o1, o2);
};
大括号与return必须同时去除;
Comparator<Integer> c1 = (o1, o2) -> Integer.compare(o1, o2);
2 函数式接口
2.1 定义
只包含一个抽象方法的接口称为函数式接口。
2.2 自定义函数式接口
@FunctionalInterface是函数式接口的注解,编译时可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
@FunctionalInterface
public interface MyFunctional {
void method();
}
2.3 与lambda表达式的关系
- 在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
- 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
2.4 作为参数传递的lambda表达式
@Test
public void test3() {
method1(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
}, "abc");
System.out.println("----------------------------");
method1(s -> System.out.println(s), "cde");
}
public void method1(Consumer<String> c, String str) {
c.accept(str);
}
2.5 Java 内置四大核心函数式接口
2.6 使用场景
在开发中,当我们需要定义一个函数式接口时,首先可以看一下jdk中提供的函数式接口是否满足我们的需求,如果满足,直接调用即可。
3 方法引用、构造器引用、数组引用
如果不熟悉方法引用,可以使用lambda表达式。
4 Stream API
4.1 概述
- Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。
4.2 Stream与Collection的区别
Stream是有关数据计算的,是面向cpu的,通过cpu实现计算;
Collection是静态的内存数据结构,面向内存,用内存存储数据。
4.3 细节
- Stream类似于迭代器,本身不存储元素;
- Stream不会改变源对象,它们会返回一个带有结果的新Stream;
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行;
4.4 三个步骤
- 实例化Stream对象;
- 中间操作(过滤、映射…);
- 终止操作,一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
4.5 Stream的实例化
4.5.1 通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Stream<Integer> stream = list.stream();
Stream<Integer> integerStream = list.parallelStream();
4.5.2 通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static Stream stream(T[] array): 返回一个流
int[] arr = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(arr);
String[] strs = {"a", "b"};
Stream<String> stream1 = Arrays.stream(strs);
4.5.3 通过Stream的静态方法of
public static Stream of(T… values) : 返回一个流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
4.5.4 创建无限流
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
迭代:public static Stream iterate(final T seed, final UnaryOperator f)
生成:public static Stream generate(Supplier s)
4.6 Stream的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
4.6.1 切片
// filter:筛选
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);
System.out.println();
// limit:截取前n个元素
employees.stream().limit(4).forEach(System.out::println);
System.out.println();
// skip:跳过前n个元素
employees.stream().skip(3).forEach(System.out::println);
System.out.println();
// distinct:根据流所生成的hashcode和equal去除重复元素
employees.stream().distinct().forEach(System.out::println);
4.6.2 映射
使用映射可以获取流中对象的某个属性,然后使用规约来计算所以对象属性的和。
- map
输出所有员工姓名长度大于3的姓名:先映射获取所有员工的姓名,然后再过滤名字长度小于等于3的
EmployeeData.getEmployees().stream().map(str -> str.getName()).filter(str -> str.length() > 3).forEach(System.out::println);
- flatMap
flatMap(Function f) 相当于List中的addAll方法,addAll是将另一个集合拆分后添加到当前集合中。如果flatMap中的参数是一个Stream,那么会将这个流拆分后添加到当前流中。
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
// list.add(Arrays.asList(4, 5, 6));[1, 2, 3, [4, 5, 6]]
list.addAll(Arrays.asList(4, 5, 6));[1, 2, 3, 4, 5, 6]
System.out.println(list);
public void test10(){
List<String> list = Arrays.asList("aa","bb","cc");
// 使用map:返回值是Stream中嵌套Stream,需要使用双层嵌套去遍历,相当于[[a,a],[b,b],[c,c]]
Stream<Stream<Character>> streamStream = list.stream().map(this::getCharacter);
streamStream.forEach(s -> s.forEach(System.out::println));
// 使用flatMap:返回值是Stream,直接遍历即可,相当于[a,a,b,b,c,c]
Stream<Character> characterStream = list.stream().flatMap(this::getCharacter);
list.stream().flatMap(this::getCharacter).forEach(System.out::print);
}
public Stream<Character> getCharacter(String str){
Character[] c = new Character[str.length()];
for (int i = 0; i < str.length(); i++) {
c[i] = str.charAt(i);
}
return Arrays.stream(c);
}
4.6.3 排序
- 自然排序要求生成Stream流的元素必须实现Comparable接口;
- 定制排序
定制排序,需要实现Compactor接口,此处使用方法引用
public void test11() {
List<Integer> list = Arrays.asList(5, 3, 1, 7, 9);
下面两种输出方式相同,只是lambda表达式可以输出更多内容
list.stream().sorted((Integer::compare)).forEach(o -> System.out.println(o + " "));1 3 5 7 9
list.stream().sorted((Integer::compare)).forEach(System.out::print); 13579
}
对EmployeeData中的元素进行排序,先按年龄进行排序,年龄相同按薪资排序
Stream<Employee> stream = EmployeeData.getEmployees().stream();
stream.sorted((o1, o2) -> {
int res = Integer.compare(o1.getAge(), o2.getAge());
if (res != 0) {
return res;
} else {
return Double.compare(o1.getSalary(), o2.getSalary());
}
}).forEach(System.out::println);
4.7 Stream的终止操作
4.7.1 细节
- 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
- 流进行了终止操作后,不能再次使用。
4.7.2 匹配
练习:检查所有员工的年龄是否都大于18
Stream<Employee> stream = EmployeeData.getEmployees().stream();
boolean b = stream.allMatch(employee -> employee.getAge() > 18);
4.7.3 查找
4.7.4 规约
public void test12() {
// 规约操作 reduce
// 练习1:计算自然数1-10的和
List<Integer> list = new ArrayList();
for (int i = 1; i < 11; i++) {
list.add(i);
}
// 第一个参数是初始值,第二个参数是运算规则
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);
}
// 计算公司中所有员工的工资总和
Stream<Employee> stream = EmployeeData.getEmployees().stream();
// 先使用map获取所有员工的工资,然后使用reduce将其累加
Optional<Double> reduce = stream.map(employee -> employee.getSalary()).reduce(Double::sum);
System.out.println(reduce);
4.7.5 收集
Collector 接口中方法的实现决定了如何对流执行收集的操作,也就是将stream流转换成其他数据结构(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
// 练习:查找工资大于6000的员工,结果返回一个List或Set
Stream<Employee> stream = EmployeeData.getEmployees().stream();
List<Employee> collect = stream.filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
Set<Employee> set = stream.filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
5 Optional类
5.1 定义
Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
5.2 作用
为了避免空指针异常,使用Optional,不需要再显式判断对象是否为null。
5.3 实例化Optional对象的三种方式
Optional.of(T t)
:t不能为null;Optional.empty()
:创建一个空的Optional实例;Optional.ofNullable(T t)
:t可以为null;
5.4 获取Optional容器的对象
T get():
如果调用对象不为空,返回该值,否则抛异常。T orElse(T other)
:如果有值则将其返回,否则返回指定的other对象。(一般使用此方法来避免空指针异常)T orElseGet(Supplier<? extends T> other)
:如果有值则将其返回,否则返回由Supplier接口实现提供的对象。TorElseThrow(Supplier<? extends X> exceptionSupplier)
:如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
5.5 判断Optional容器中是否包含对象
boolean isPresent()
: 判断是否包含对象。void ifPresent(Consumer<? super T> consumer)
:如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
5.6 实例
Optional<Object> o = Optional.of("123");
System.out.println(o);
Optional<Object> empty = Optional.empty();
System.out.println(empty);
o1中的对象为空,所以返回指定值“123”
Optional<Object> o1 = Optional.ofNullable(null);
Object o2 = o1.orElse("123");
System.out.println(o2); // 123