- 1、Lambda表达式
- 1.1 实例
- 1.2 变量作用域
- 2、函数式接口
- 2.1 四大核心函数式接口
- 2.2 实例
- 3、方法引用
- 3.1 举例
- 4、Stream
- 4.1 创建Stream
- 4.1.1 通过集合
- 4.1.2 通过数组
- 4.1.3 通过Stream类的of()
- 4.1.4 创建无限流
- 4.2 中间操作
- 4.2.1 筛选与切片
- 4.2.2 映射
- 4.2.3 排序
- 4.3 终止操作
- 4.3.1 匹配与查找
- 4.3.2 归约
- 4.3.3 收集
- 5、Optional 类
-
格式:(parameters) -> expression 或 (parameters) -> { statements; }
-
特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回值:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
- 举例:
// 1. 不需要参数,返回值为 5 () -> 5 // 2. 接收一个参数(数字类型),返回其2倍的值 x -> 2 * x // 3. 接受2个参数(数字),并返回他们的差值 (x, y) -> x – y // 4. 接收2个int型整数,返回他们的和 (int x, int y) -> x + y // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) (String s) -> System.out.print(s)1.1 实例
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
1.2 变量作用域
- Lambda表达式可以无限制的使用实例变量和静态变量
- 使用的局部变量必须显式声明为final,或隐式具有final的语义
- Lambda表达式中不能声明一个与局部变量同名的参数
-
仅有一个抽象方法的接口称为函数式接口。(可使用@FunctionalInterface注解检查是否是一个函数式接口)
-
Java8新增函数式接口位于 java.util.function 包下
Consumer2.2 实例(消费型接口) 包含方法:void accept(T t) Supplier (供给型接口) 包含方法:T get() Function (函数型接口) 包含方法:R apply(T t) Predicate (断定型接口) 包含方法:boolean test(T t)
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
public static void main(String args[]){
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate predicate = n -> true
// n 是一个参数传递到 Predicate 接口的 test 方法
// n 如果存在则 test 方法返回 true
System.out.println("输出所有数据:");
// 传递参数 n
eval(list, n->true);
// Predicate predicate1 = n -> n%2 == 0
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n%2 为 0 test 方法返回 true
System.out.println("输出所有偶数:");
eval(list, n-> n%2 == 0 );
// Predicate predicate2 = n -> n > 3
// n 是一个参数传递到 Predicate 接口的 test 方法
// 如果 n 大于 3 test 方法返回 true
System.out.println("输出大于 3 的所有数字:");
eval(list, n-> n > 3 );
}
public static void eval(List list, Predicate predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
3、方法引用
-
格式: 类(或对象):: 方法名
-
三种使用方式:
- 对象 :: 实例方法
- 类 :: 静态方法
- 类 :: 实例方法
-
使用要求:接口中抽象方法的形参列表和返回值类型与方法引用中方法的形参列表和返回值类型相同
- 传入静态方法
public class Main {
public static void main(String[] args) {
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, Main::cmp);
System.out.println(String.join(", ", array));
}
static int cmp(String s1, String s2) {
return s1.compareTo(s2);
}
}
- 传入实例方法
public class Main {
public static void main(String[] args) {
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, String::compareTo);
System.out.println(String.join(", ", array));
}
}
//String.compareTo()方法的定义:
public final class String {
public int compareTo(String o) {
...
}
}
//该实例方法有一个隐含的this参数,相当于静态方法:public static int compareTo(this, String o);
- 传入构造方法
- 构造方法虽然没有return语句,但它会隐式返回this实例
public class Main {
public static void main(String[] args) {
List names = List.of("Bob", "Alice", "Tim");
List persons = names.stream().map(Person::new).collect(Collectors.toList());
System.out.println(persons);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
public String toString() {
return "Person:" + this.name;
}
}
4、Stream
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和Collection操作不同, Stream操作还有两个基础的特征:
-
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
-
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
-
Stream 操作的三个步骤:
- 创建 Stream
- 中间操作
- 终止操作
- Java8中的Collection接口被扩展,提供了两个获取流的方法:
- default Stream stream():返回一个顺序流
- default Stream parallelStream():返回一个并行流
public void test1(){
List strings = Arrays.asList("a", "b", "c");
Stream stream1 = strings.stream();
Stream stream2 = strings.parallelStream();
}
4.1.2 通过数组
-
Java8中Arrays的静态方法stream()可以获取数组流
- static Stream stream(T[] array):返回一个流
-
重载形式:
- public static IntStream stream(int[] array)
- public static LongStream stream(long[] array)
- public static DoubleStream stream(double[] array)
public void test2(){
IntStream stream = Arrays.stream(new int[]{1, 2, 3, 4, 5});
stream.forEach(System.out::println);
}
4.1.3 通过Stream类的of()
-
可以调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数
-
public static Stream of(T… values):返回一个流
public void test3(){
Stream integerStream = Stream.of(1, 2, 3, 4);
integerStream.forEach(System.out::println);
}
4.1.4 创建无限流
- 可以使用静态方法 Stream.iterate() 和 Stream.generate() 创建无限流
- 迭代:public static Stream iterate(final T seed, final UnaryOperator f)
- 生成:public static Stream generate(Supplier s)
public void test4(){
//输出前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//输出10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
4.2 中间操作
4.2.1 筛选与切片
- filter(Predicate p):接收Lambda,从流中排除某些元素(过滤)
- distinct():去重,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
- limit(long maxSize):截断流,使其元素不超过给定数量
- skip(long n):跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
public void test5(){
List integers = Arrays.asList(1, 2, 2, 3, 4, 5, 6);
integers.stream().filter(n -> n % 2 == 0).forEach(System.out::println);
integers.stream().limit(3).forEach(System.out::println);
integers.stream().skip(3).forEach(System.out::println);
integers.stream().distinct().forEach(System.out::println);
}
4.2.2 映射
-
map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
-
flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
public void test6(){
List strings = Arrays.asList("aa", "bb", "cc", "dd");
strings.stream().map(String::toUpperCase).forEach(System.out::println);
List strings1 = Arrays.asList("aa", "bb", "cc", "dd");
Stream> streamStream = strings1.stream().map(StreamTest::fromStringToStream);
streamStream.forEach(s -> {
s.forEach(System.out::println);
});
Stream characterStream = strings1.stream().flatMap(StreamTest::fromStringToStream);
characterStream.forEach(System.out::println);
}
public static Stream fromStringToStream(String str){
ArrayList res = new ArrayList();
for(Character c : str.toCharArray()){
res.add(c);
}
return res.stream();
}
4.2.3 排序
- sorted():产生一个新流,其中按自然顺序排序
- sorted(Comparator com):产生一个新流,其中按比较器顺序排序
public void test7(){
List integers = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
integers.stream().sorted().forEach(System.out::println);
integers.stream().sorted((e1, e2) -> e2 - e1).forEach(System.out::println);
}
4.3 终止操作
- 终端操作会从流的流水线生成结果,其结果可以是任何不是流的值。
- 流进行了终止操作后,这个流不能被再次使用
- allMatch(Predicate p):检查是否匹配所有元素
- anyMatch(Predicate p):检查是否至少匹配一个元素
- noneMatch(Predicate p):检查是否没有匹配所有元素
- findFirst():返回第一个元素
- findAny():返回当前流中的任意元素
- count():返回流中元素总数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c):内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API使用内部迭代)
public void test8(){
List integers = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
System.out.println(integers.stream().allMatch(a -> a > 15));
System.out.println(integers.stream().anyMatch(a -> a > 15));
System.out.println(integers.stream().noneMatch(a -> a > 15));
System.out.println(integers.stream().findFirst());
System.out.println(integers.stream().findAny());
System.out.println(integers.parallelStream().findAny());
System.out.println(integers.stream().filter(a -> a > 10).count());
System.out.println(integers.stream().max(Integer :: compare));
System.out.println(integers.stream().min(Integer :: compare));
integers.stream().forEach(System.out::println);
//使用集合遍历
integers.forEach(System.out::println);
}
4.3.2 归约
- reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回T
- reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一个值。返回 Optional
public void test9(){
System.out.println(Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).reduce(0, Integer::sum));
System.out.println(Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).reduce(Integer::sum).get());
}
4.3.3 收集
- collect(Collector c):将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法。
- Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)
-
- 方法:toList 返回类型:List 作用:把流中元素收集到 List
- 方法:toSet 返回类型:Set 作用:把流中元素收集到 Set
- 方法:toCollection 返回类型:Collection 作用:把流中元素收集到创建的集合
public void test10(){
List integers = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7);
List collect = integers.stream().filter(a -> a > 15).collect(Collectors.toList());
collect.forEach(System.out::println);
}
5、Optional 类
-
Optional 类( java.util.Optional ) 是一个容器类,它可以保存类型 T 的值,并且可以避免空指针异常
-
创建 Optional 类对象的方法:
- Optional.of(T t):创建一个 Optional 实例,t 必须非空
- Optional.empty():创建一个空的 Optional 实例
- Optional.ofNullable(T t):t 可以为 null
-
判断 Optional 容器中是否包含对象:
- boolean isPresent():判断是否包含对象
- void ifPresent(Consumer super T> consumer):如果有值,就执行 Consumer 接口的实现代码,并且该值会作为参数传给它
-
获取 Optional 容器的对象:
- T get():如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other):如果有值则将其返回,否则返回指定的 other 对象
public class Girl {
private String name;
public Girl() {
}
public Girl(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Girl{" +
"name='" + name + ''' +
'}';
}
}
public class Boy {
private Girl girl;
public Boy() {
}
public Boy(Girl girl) {
this.girl = girl;
}
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
}
import org.junit.jupiter.api.Test;
import java.util.Optional;
public class OptionalTest {
@Test
public void test01(){
Girl girl = new Girl();
Optional girl1 = Optional.of(girl);
}
@Test
public void test02(){
Girl girl = new Girl();
girl = null;
Optional OptionalGirl = Optional.ofNullable(girl);
System.out.println(girl);
}
@Test
public void test03(){
Boy boy = null;
String girlName = getGirlName(boy);
System.out.println(girlName);
Boy boy1 = new Boy();
System.out.println(getGirlName(boy1));
Boy boy2 = new Boy(new Girl("33"));
System.out.println(getGirlName(boy2));
}
@Test
public String getGirlName(Boy boy){
Optional optionalboy = Optional.ofNullable(boy);
Boy boy1 = optionalboy.orElse(new Boy(new Girl("11")));
Girl girl = boy1.getGirl();
Optional optiongirl = Optional.ofNullable(girl);
Girl girl1 = optiongirl.orElse(new Girl("22"));
return girl1.getName();
}
}



