17.1 Lambda表达式
1、为什么使用Lambda表达式
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简介、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
2、Lambda表达式使用举例
import org.junit.jupiter.api.Test; import java.util.Comparator; public class LambdaTest { @Test public void test1() { Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱北京***"); } }; r1.run(); Runnable r2 = () -> System.out.println("我爱北京***"); r2.run(); } @Test public void test2() { Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } }; int compare1 = com1.compare(12, 21); System.out.println(compare1); System.out.println("*****************"); Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2); int compare2 = com2.compare(12, 21); System.out.println(compare2); System.out.println("*****************"); Comparator<Integer> com3 = Integer::compare; } }
3、Lambda表达式的使用
格式:
->:Lambda操作符(或箭头操作符)
->左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
->右边:Lambda体(其实就是重写的抽象方法的方法体)Lambda表达式的使用(6种情况):
- 语法格式一:无参,无返回值。
- 语法格式二:一个参数,无返回值。
- 语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”。
- 语法格式四:若只需要一个参数时,参数的小括号可以省略。
- 语法格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值。
- 语法格式六:当Lambda体只有一条语句时,return、大括号若有,都可以省略。
import org.junit.jupiter.api.Test; import java.util.Comparator; import java.util.function.Consumer; public class LambdaTest { /** * 语法格式一:无参,无返回值 */ @Test public void test1() { Runnable r1 = new Runnable() { @Override public void run() { System.out.println("我爱北京***"); } }; r1.run(); System.out.println("***************************"); Runnable r2 = () -> { System.out.println("我爱北京***"); }; r2.run(); } /** * 语法格式二:一个参数,无返回值 */ @Test public void test2() { Consumer<String> con1 = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con1.accept("谎言和誓言的区别是什么?"); System.out.println("************************"); Consumer<String> con2 = (String s) -> { System.out.println(s); }; con2.accept("一个是听的人当真了,一个是说的人当真了。"); } /** * 语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断” */ @Test public void test3() { Consumer<String> con1 = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con1.accept("谎言和誓言的区别是什么?"); System.out.println("************************"); Consumer<String> con2 = (s) -> { System.out.println(s); }; con2.accept("一个是听的人当真了,一个是说的人当真了。"); } /** * 语法格式四:若只需要一个参数时,参数的小括号可以省略 */ @Test public void test4() { Consumer<String> con1 = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con1.accept("谎言和誓言的区别是什么?"); System.out.println("************************"); Consumer<String> con2 = s -> { System.out.println(s); }; con2.accept("一个是听的人当真了,一个是说的人当真了。"); } /** * 语法格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值 */ @Test public void test5() { Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(com1.compare(12, 21)); System.out.println("*************************"); Comparator<Integer> com2 = (o1, o2) -> { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(com2.compare(12, 21)); } /** * 语法格式六:当Lambda体只有一条语句时,return、大括号若有,都可以省略 */ @Test public void test6() { Comparator<Integer> com1 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }; System.out.println(com1.compare(12, 21)); System.out.println("*************************"); Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2); System.out.println(com2.compare(12, 21)); } }
4、Lambda表达式的本质
- 函数式接口的实例(对象)。
17.2 函数式(Functional)接口
@FunctionalInterface public interface Runnable { public abstract void run(); }
1、什么是函数式(Functional)接口
- 只包含一个抽象方法的接口,称为函数式接口。
- 我们可以通过Lambda表达式来创建该接口的对象(若Lambda表达式抛出一个非运行时异常,那么该异常需要在目标接口的抽象方法上进行声明)。
- 我们可以在一个接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
- 在java.util.function包下定义了Java8的丰富的函数式接口。
2、如何理解函数式接口
- Java从诞生日起就一直倡导“一切皆对象”,在Java里面向对象(OOP)编程是一切。但是随着Python、Scala等语言的兴起和新技术的挑战,Java不得不做出挑战以便支持更加广泛的技术要求,也即Java不但可以支持OOP还可以支持OFP(面向函数编程)。
- 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的编程语言中,Lambda表达式的类型是函数。但是在Java8中有所不同。在Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的对象类型——函数式接口。
- 简单地说,在Java8中,Lambda表达式就是一个函数式接口的实例。这就是Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。
- 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
3、Java内置四大核心函数式接口
4、其他接口
5、示例
import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; public class LambdaTest { @Test public void test1() { happyTime(400, money -> System.out.println(money + 1)); } public void happyTime(double money, Consumer<Double> con) { con.accept(money); } @Test public void test2() { List<String> list = Arrays.asList("北京", "南京", "天津", "东京", "西京", "普京"); List<String> filterStrs = filterString(list, new Predicate<String>() { @Override public boolean test(String s) { return s.contains("京"); } }); System.out.println(filterStrs); List<String> filterStrs1 = filterString(list, s -> s.contains("京")); System.out.println(filterStrs1); } /** * 根据给定的规则,过滤集合中的字符串,此规则由Predicate的方法决定 * @param list * @param pre * @return */ public List<String> filterString(List<String> list, Predicate<String> pre) { ArrayList<String> filterList = new ArrayList<>(); for (String s : list) { if(pre.test(s)) { filterList.add(s); } } return filterList; } }
17.3 方法引用与构造器引用
1、方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表、返回值类型,必须与方法引用的方法的参数列表、返回值类型保持一致。
格式:使用操作符“::”将类(或对象)与方法名分隔开来。
分为如下三种主要使用情况:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
import org.junit.jupiter.api.Test; import java.util.Comparator; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; public class LambdaTest { /** * 情况一:对象::实例方法 */ @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); Consumer<String> con2 = System.out::println; con2.accept("北京"); System.out.println("*******************"); Person p = new Person("Tom", 18); Supplier<String> sup1 = () -> p.getName(); System.out.println(sup1.get()); Supplier<String> sup2 = p::getName; System.out.println(sup2.get()); } /** * 情况二:类::静态方法 * Comparator: int compare(T t1, T t2) * Integer: int compare(T t1, T t2) * ================================ * Function: R apply(T t) * Math: Long round(double d) */ @Test public void test2() { Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2); System.out.println(com1.compare(12, 21)); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12, 21)); System.out.println("********************"); Function<Double, Long> func1 = (d) -> Math.round(d); System.out.println(func1.apply(1.5)); Function<Double, Long> func2 = Math::round; System.out.println(func2.apply(1.5)); } /** * 情况三:类::实例方法 * Comparator: int compare(T t1, T t2) * String: int t1.compareTo(t2) * ======================= * BiPredicate: boolean test(T t1, T t2) * String: boolean t1.equals(t2) */ @Test public void test3() { Comparator<String> com1 = (t1, t2) -> t1.compareTo(t2); System.out.println(com1.compare("abc", "abc")); Comparator<String> com2 = String::compareTo; System.out.println(com2.compare("abc", "abc")); System.out.println("************************"); BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2); System.out.println(pre1.test("abc", "abc")); BiPredicate<String, String> pre2 = String::equals; System.out.println(pre2.test("abc", "abc")); } }
2、构造器引用
和方法引用类似,函数式接口的抽象方法的形参列表,和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型。
import org.junit.jupiter.api.Test; import java.util.Comparator; import java.util.function.*; public class LambdaTest { /** * Supplier: T get() * Person: Person() */ @Test public void test1() { Supplier<Person> sup = new Supplier<Person>() { @Override public Person get() { return new Person(); } }; Person person = sup.get(); Supplier<Person> sup1 = () -> new Person(); Person person1 = sup1.get(); Supplier<Person> sup2 = Person::new; Person person2 = sup2.get(); } /** * Function: R apply(T t) */ @Test public void test2() { Function<Integer, Person> func1 = id -> new Person(id); Person person = func1.apply(1001); System.out.println(person); Function<Integer, Person> func2 = Person::new; Person person2 = func2.apply(1002); System.out.println(person2); } /** * BiFunction: R apply(T t, U u) */ @Test public void test3() { BiFunction<Integer, String, Person> func1 = (id, name) -> new Person(id, name); System.out.println(func1.apply(1001, "Tom")); BiFunction<Integer, String, Person> func2 = Person::new; System.out.println(func2.apply(1002, "Mary")); } }
3、数组引用
- 可以将数组看做是一个特殊的类,则写法与构造器引用一致。
import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Comparator; import java.util.function.*; public class LambdaTest { /** * Function: R apply(T t) */ @Test public void test1() { Function<Integer, String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); Function<Integer, String[]> func2 = String[]::new; String[] arr2 = func2.apply(5); System.out.println(Arrays.toString(arr2)); } }
17.4 Stream API
1、Stream API说明
Stream关注的是对数据的运算,与CPU打交道。
集合关注的是数据的存储,与内存打交道。
注意:
- Stream自己不会存储元素。
- Stream不会改变源对象,相反,它们会返回一个持有结果的新Stream。
- Stream操作时延迟执行的,这意味着它们会等到需要结果的时候才执行。
2、Stream操作的三个步骤
- 创建Stream:一个数据源(如:集合、数组),获取一个流。
- 中间操作:一个中间操作链,对数据源的数据进行处理。
- 终止操作(终端操作):一旦执行终止操作,就执行中间操作链,产生结果。之后,不会再被使用。
3、创建Stream的方式
- 方式一:通过集合
Java8中的Collection接口被扩展,提供了两个获取流的方法:
- default Stream<e> stream():返回一个顺序流</e>
- default Stream<e> parallelStream():返回一个并行流</e>
- 方式二:通过数组
Java8中的Arrays的静态方法stream()可以获取数组流:
static <t> Stream<t> stream(T[] array):返回一个流</t></t>
方式三:Stream.of()
方式四:创建无限流(Stream.iterate()、Stream.generate())
import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamAPITest { /** * 方式一:通过集合。 */ @Test public void test1() { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7); // default Stream<E> stream():返回一个顺序流 Stream<Integer> stream = integers.stream(); // default Stream<E> parallelStream():返回一个并行流 integers.parallelStream(); } /** * 方式二:通过数组。 */ @Test public void test2() { int[] arr = new int[]{1, 2, 3, 4, 5, 6}; // 调用Arrays的static <T> Stream<T> stream(T[] array):返回一个流 IntStream stream = Arrays.stream(arr); } /** * 方式三:Stream.of() */ @Test public void test3() { Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6); } /** * 方式四:创建无限流(Stream.iterate()、Stream.generate())。 */ @Test public void test4() { // 迭代 Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println); // 生成 Stream.generate(Math::random).limit(10).forEach(System.out::println); } }
4、Stream的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理。而在终止操作时一次性全部处理,称为“惰性求值”。
- 筛选与切片
- 映射
- 排序
- 案例
import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamAPITest { /** * 筛选与切片 */ @Test public void test1() { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 5, 6, 7); Stream<Integer> stream = integers.stream(); // filter(Predicate p):接收Lambda,从流中排除某些元素 stream.filter(i -> i > 5).forEach(System.out::print); System.out.println(); // limit(n):截断流,使其元素不超过给定数量 Stream<Integer> stream1 = integers.stream(); stream1.limit(3).forEach(System.out::print); System.out.println(); // skip(n):跳过元素 Stream<Integer> stream2 = integers.stream(); stream2.skip(3).forEach(System.out::print); System.out.println(); // distinct():筛选,通过流所生成元素的hashCode()和equals()去除重复元素 Stream<Integer> stream3 = integers.stream(); stream3.distinct().forEach(System.out::print); } /** * 映射 */ @Test public void test2() { List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); // map(Function f) list.stream().map(str -> str.toUpperCase()).forEach(System.out::print); System.out.println(); Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest::fromStringToStream); streamStream.forEach(s -> { s.forEach(System.out::print); }); System.out.println(); // flatMap(Function f) Stream<Character> characterStream = list.stream().flatMap(StreamAPITest::fromStringToStream); characterStream.forEach(System.out::print); } public static Stream<Character> fromStringToStream(String str) { ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()) { list.add(c); } return list.stream(); } /** * 排序 */ @Test public void test3() { List<Integer> list = Arrays.asList(12, 32, 13, 454, 64, 11, 54, 76); // sorted() list.stream().sorted().forEach(System.out::println); System.out.println("+++++++++++++++++++++++++++"); // sorted(Comparator com) list.stream().sorted((m, n) -> -Integer.compare(m, n)).forEach(System.out::println); } }
5、终止操作
- 匹配与查找
import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamAPITest { /** * 匹配与查找 */ @Test public void test1() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 5, 6, 7); // allMatch(Predicate p) boolean b = list.stream().allMatch(i -> i < 8); System.out.println(b); System.out.println("+++++++++++++++++++++++++++++++++"); // anyMatch() System.out.println(list.stream().anyMatch(i -> i > 6)); System.out.println("+++++++++++++++++++++++++++++++++"); // noneMatch() System.out.println(list.stream().noneMatch(i -> i > 8)); System.out.println("+++++++++++++++++++++++++++++++++"); // findFirst() Optional<Integer> first = list.stream().findFirst(); System.out.println(first); System.out.println("+++++++++++++++++++++++++++++++++"); // findAny() Optional<Integer> any = list.parallelStream().findAny(); System.out.println(any); System.out.println("+++++++++++++++++++++++++++++++++"); } @Test public void test2() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 5, 6, 7); // count() long count = list.stream().filter(i -> i > 4).count(); System.out.println(count); System.out.println("+++++++++++++++++++++++++++++++++"); // max() Optional<Integer> max = list.stream().max(Integer::compareTo); System.out.println(max); System.out.println("+++++++++++++++++++++++++++++++++"); // min() Optional<Integer> min = list.stream().min(Integer::compareTo); System.out.println(min); System.out.println("+++++++++++++++++++++++++++++++++"); // forEach() list.stream().forEach(System.out::print); } }
- 归约
- 收集
- collect(Collector c)
- Collectors
import org.junit.jupiter.api.Test; import java.util.*; import java.util.stream.Collector; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamAPITest { /** * 归约 */ @Test public void test1() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 5, 6, 7); // reduce(T identity, BinaryOperator operator) Integer reduce = list.stream().reduce(0, Integer::sum); System.out.println(reduce); } /** * 收集 */ @Test public void test2() { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 5, 6, 7); // collect(Collector c) Set<Integer> collect = list.stream().collect(Collectors.toSet()); } }
17.5 Optional类
Optional<t>类(java.util.Optional)是一个容器,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。</t>
原来用null表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。
import org.junit.jupiter.api.Test; import java.util.Optional; public class OptionalTest { /** * Optional.of(T t):创建一个Optional实例,t必须非空 * Optional.empty():创建一个空的Optional实例 * Optional.ofNullable(T t):t可以是null * orElse(T t1):如果当前的Optional内部封装的t是非空的,则返回内部的t; * 如果内部的t是空的,则返回orElse()方法中的参数t1。 */ @Test public void test1() { Person person = new Person(); Optional<Person> person1 = Optional.of(person); Optional<Person> person2 = Optional.ofNullable(person); } public static String getName(Person person) { Optional<Person> p = Optional.ofNullable(person); Person person1 = p.orElse(new Person()); return person1.getName(); } @Test public void test2() { Person person = null; String name = getName(person); System.out.println(name); } }