Lambda表达式
- lambda表达式:本质就是一个函数式接口的实例对象。
- 语法:
lambda形参列表 箭头操作符 lambda体;
lambda形参列表其实就是函数式接口中抽象方法的形参列表,lambda体就是重写函数式接口中抽象方法的方法体。- 语法格式1:无参数,无返回值
// lambda体中只有一条语句,可以省略大括号 // Runnable runnable = () -> {System.out.println("HelloWorld");}; Runnable runnable = () -> System.out.println("HelloWorld"); runnable.run();
- 语法格式2:lambda形参列表有一个以上的参数,无返回值
// 因为编译器存在类型推断,所以形参列表中的形参的数据类型可以省略 Consumer<String> consumer = (str) -> { str = str.toUpperCase(); System.out.println(str); }; consumer.accept("helloworld"); // 当lambda形参列表的形参值有一个时,形参外面的小括号可以省略 consumer = str -> { str = str.toUpperCase(); System.out.println(str); }; consumer.accept("helloworld");
- 语法格式3:lambda形参列表有一个以上的参数,有返回值
Comparator<String> comparator = (str1, str2) -> { return str1.compareTo(str2); }; System.out.println(comparator.compare("Helm", "Hell")); // lambda体只有一条return语句时可以省略大括号和return关键字 comparator = (str1, str2) -> str1.compareTo(str2); System.out.println(comparator.compare("Helm", "Hell"));
- 应用:
- 作为实参传递给方法形参为函数式接口的方法中
String[] strs = new String[]{"Hi", "A", "Hello", "you"}; Arrays.sort(strs, (str1, str2) -> str1.length() - str2.length()); for (String str : strs) { System.out.println(str); }
- 赋值给接口定义的变量。在没有lambda表达式之前,是将匿名实现类对象赋值给接口定义的变量的。
// 匿名实现类 Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; System.out.println(comparator.compare(100, 200)); // lambda表达式 comparator = (o1, o2) -> Integer.compare(o1, o2); System.out.println(comparator.compare(100, 200));
函数式接口
- 只包含一个抽象方法的接口叫做函数式接口。
- 在一个接口上面使用
@FunctionalInterface
注解,可以检验该接口是否为函数式接口。 - Java中内置的函数式接口
- 消费性接口Consumer
:对类型为T的对象应用操作,包含一个抽象方法accept。 - 供给型接口Supplier
:返回类型为T的对象,包含一个抽象方法get。 - 函数型接口Function<T, R>:对类型为T的对象进行操作,返回类型为R的对象,包含抽象方法apply。
- 断定型接口Predicate
:检验类型为T的对象是否满足指定的条件,返回boolean类型的值。包含抽象方法test。 - 函数式接口BiFunction<T, U, R>:对类型为T和U的两个对象进行操作,返回类型为R的对象,包含抽象方法apply。
- UnaryOperator
:Function<T, T>的子接口,对类型为T的对象进行一元运算操作,返回类型为T的对象 - BinaryOperator
:BiFunction<T,T,T>的子接口,对类型为T的两个对象进行操作,返回类型为T的对象 - BiConsumer<T, U>:对类型为T和U的两个对象应用操作
- BiPredicate<T, U>:检验类型为T和U的两个对象是否满足指定的条件,返回boolean类型的值
- ToIntFunction
、ToLongFunction 、ToDoubleFunction :对T类型的对象进行计算,分别返回int、long、double类型的返回值。 - IntFunction
、LongFunction 、DoubleFunction :分别对int、long、double类型的参数进行计算返回R类型的对象。
- 消费性接口Consumer
@Test
public void testFilter() {
// 获取集合中包含张字的字符串
List<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
list.add("张莎");
list = filter(list, str -> str.contains("张"));
System.out.println(list);
}
public List<String> filter(List<String> list, Predicate<String> predicate) {
ArrayList<String> resultList = new ArrayList<>();
for (String str : list) {
if (predicate.test(str)) {
resultList.add(str);
}
}
return resultList;
}
方法引用、构造器引用
1.方法引用
- 方法引用:使用lambda表达式时,对于lambda体中的操作,已经存在现成的方法可以使用,则可以直接使用。方法引用本质就是lambda表达式,它也可以作为一个函数式接口的实例对象。
- 方法引用的使用格式:类或者对象::方法名。具体分为三种情况:
- 对象::实例方法
Consumer<String> consumer = (str) -> System.out.println(str); consumer.accept("helloworld"); // 方法引用的情况1实例 consumer = System.out::println; consumer.accept("helloworld");
- 类::静态方法。对于情况1和情况2要求:函数式接口中抽象方法的形参列表和返回值类型与方法引用的方法相同。
Comparator<Integer> comparator = (t1, t2) -> Integer.compare(t1, t2); System.out.println(comparator.compare(100, 200)); // 方法引用的情况2示例 comparator = Integer::compare; System.out.println(comparator.compare(100, 200));
- 类::实例方法,这种情况下,lambda形参列表中的第一个参数作为方法的目标。
BiPredicate<String, String> predicate = (str1, str2) -> str1.equals(str2); System.out.println(predicate.test("Hello", "Hello")); // 方法引用的情况3示例 predicate = String::equals; System.out.println(predicate.test("Hello", "Hello"));
2.构造器引用
- 构造器引用:和方法引用类似,函数式接口中抽象方法的形参列表和构造器的形参列表一样。抽象方法的返回值类型即为构造器所属的类的类型。
BiFunction<String, Integer, Student> function = (name, age) -> new Student(name, age);
Student student = function.apply("NrvCer", 23);
System.out.println(student);
// 构造器引用
function = Student::new;
student = function.apply("NrvCer", 24);
System.out.println(student);
- 数组引用:将数组作为一种类型,则写法和构造器引用一样
Function<Integer, String[]> function = length -> new String[length];
String[] strings = function.apply(4);
System.out.println(Arrays.toString(strings));
// 数组引用
function = String[]::new;
strings = function.apply(10);
System.out.println(Arrays.toString(strings));
Stream API
Stream:用于操作数据源所生成的元素序列,Java中的集合用于存储数据,而Stream一般用于计算。
1.Stream的创建
- 从数据源中获取Stream。创建Stream的方式有如下几种:
- 通过Collection接口的stream和parallelStream方法,前者返回顺序流、后者返回并行流
List<Student> students = Student.getStudents(); // 返回一个顺序流 Stream<Student> stream = students.stream(); // 返回一个并行流 Stream<Student> parallelStream = students.parallelStream();
- 通过Arrays的静态方法stream获取数组流
int[] arr = new int[]{1, 2, 3, 4, 5, 6}; IntStream stream = Arrays.stream(arr);
- 通过Stream的of方法
Stream<List<Student>> stream = Stream.of(Student.getStudents());
- 使用Stream的静态方法iterate和generate创建无限流
Stream.iterate(100, value -> value + 100) .limit(10) .forEach(System.out::println); Stream.generate(Math::random) .limit(3) .forEach(System.out::println);
2.Stream的中间操作
一个中间操作链,对数据源的数据进行处理。只有触发了Stream的终止操作,中间操作才会执行。
- 筛选:
- filter(Predicate p):接收lambda表达式,从流中排除符合条件的元素
- distinct():通过元素的hashCode和equals方法去除重复元素
List<Student> students = Student.getStudents(); Stream<Student> stream = students.stream(); // 查询学生集合中年龄大于24岁的学生信息 stream.filter(student -> student.getAge() > 24) .forEach(System.out::println); // 去除流中的重复元素 // 一旦一个流执行了终止操作,其状态会改变 // stream has already been operated upon or closed stream = students.stream(); stream.distinct().forEach(System.out::println);
- 切片:
- limit(long maxSize):截断流,使得其元素不超过给定的数量
- skip(long n):跳过元素,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。
- 映射:
- map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。类似的还有mapToDouble、mapToInt、mapToLong
// 获取学生集合中学生年龄大于24的学生年龄 List<Student> students = Student.getStudents(); Stream<Integer> ageStream = students.stream().map(Student::getAge); // 大于24岁的年龄集合 List<Integer> result = new ArrayList<>(); ageStream.filter(age -> age > 24).forEach(age -> result.add(age));
- flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
- 排序
- sorted:产生一个新流,其中按照自然顺序排序
- sorted(Comparator comparator):产生一个新流,其中按照比较器顺序排序
List<Integer> integers = Arrays.asList(23, 12, 34, 11, 56, 42); // 自然排序 integers.stream().sorted().forEach(System.out::println); // 定制排序:对学生集合先按照年龄从小到大排序, // 年龄相同再按照姓名排序 List<Student> students = Student.getStudents(); students.stream() .sorted((v1, v2) -> { int ageResult = Integer.compare(v1.getAge(), v2.getAge()); return ageResult == 0 ? v1.getName().compareTo(v2.getName()) : ageResult; }).forEach(System.out::println);
3.Stream的终止操作
一旦执行终止操作,就执行中间操作链,并产生结果。流进行了终止操作后,不能再次使用。
- 匹配与查找
- allMatch(Predicate p):检查是否匹配所有元素
- anyMatch(Predicate p):检查是否至少匹配一个元素
- noneMatch(Predicate p):检查是否没有匹配的元素
- findFirst():返回流中第一个元素
- findAny():返回当前流中的任意元素
List<Student> students = Student.getStudents();
// 检查是否所有学生的年龄大于20
boolean allMatch = students.stream().allMatch(student -> student.getAge() > 20);
System.out.println(allMatch);
// 检查是否存在学生的姓名叫做张三
boolean anyMatch = students.stream().anyMatch(student -> "张三".equals(student.getName()));
System.out.println(anyMatch);
// 检查是否存在学生的姓名包含刘
boolean noneMatch = students.stream().noneMatch(student -> student.getName().contains("刘"));
System.out.println(noneMatch);
// 获取学生集合中的第一个学生
Optional<Student> student = students.stream().findFirst();
System.out.println(student.orElse(null));
// 获取学生集合中的任意一个
Optional<Student> any = students.stream().findAny();
System.out.println(any.orElse(null));
- 统计
- count():返回流中元素总数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c):迭代
List<Student> students = Student.getStudents();
// 获取学生集合中的学生个数
System.out.println(students.stream().count());
// 获取年龄最高的学生
Student student = students.stream().max((v1, v2) -> Integer.compare(v1.getAge(), v2.getAge()))
.orElse(null);
System.out.println(student);
// 获取长度最短的姓名
Stream<String> nameStream = students.stream().map(Student::getName);
System.out.println(nameStream.min((s1, s2) -> s1.length() - s2.length()));
- 规约
- reduce(T identity, BinaryOperator b):可以将流中的元素反复结合起来,得到一个值,返回T
- reduce(BinaryOperator b):可以将流中的元素反复结合起来,得到一个值,返回Optional
// 计算一个整形数组中元素的和
List<Integer> integers = Arrays.asList(0, 10, 20, 40, 60, 80, 100);
Integer result = integers.stream().reduce(0, Integer::sum);
System.out.println(result);
- 收集
- collect(Collector c):将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
// 查找学生集合中年龄大于23岁的学生,结果保存在另一个集合中
List<Student> students = Student.getStudents();
List<Student> ageList = students.stream().filter(student -> student.getAge() > 23)
.collect(Collectors.toList());
ageList.forEach(System.out::println);
Optional类
Optional类:为了避免程序中出现空指针异常
1.Optional类常用的方法:
- 创建Optional类型的对象的方法:
- Optional.of(Tt):创建一个Optional实例,t必须非空;如果t为空,则抛出空指针异常
- Optional.ofNullable(T t):创建一个Optional实例,t可以为null
- Optional.empty() : 创建一个空的Optional实例
// 空的Optional实例即 private static final Optional<?> EMPTY = new Optional<>();
- 获取Optional容器包装的对象的方法
- T get():如果容器包装的value值不为空,则返回该值。否则抛出异常。
- T orElse(T other):
- orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t.如果内部的t是空的,则返回orElse()方法中的参数t1
- orElseGet(Supplier<? extends T> other):如果Optional容器包装的对象不为空,则返回它。否则返回供给型接口提供的对象。
- orElseThrow(Supplier<? extends X> exceptionSupplier):如果Optional容器包装的对象不为空,则返回它。否则返回供给型接口提供的异常。
- 判断Optional容器是否包含对象
- boolean isPresent()
- void ifPresent(Consumer<? super T> consumer):如果容器包装的value不为空,则执行消费性接口中的方法
Student student = null;
// ofNullable方法可以提供空的实例对象
Optional<Student> optional = Optional.ofNullable(student);
// Optional内部封装的t为空,则返回orElse的形参
Student stu = optional.orElse(new Student("default", -1));
System.out.println(stu);
student = null;
Optional<Student> optional = Optional.ofNullable(student);
student = optional.orElseGet(() -> new Student("nrvcer", 23));
System.out.println(student);
student = optional.orElseThrow(() -> new NullPointerException("student对象为空!"));
// 空指针异常对象
System.out.println(student);