目录
1.3.1 定义函数接口并使用Lambda表达式实现函数接口
一、Lambda表达式
1.1 Lambda表达式介绍
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
在Java语言中,可以为变量赋予一个值:
能否把一个代码块赋给一变量吗?
在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。
甚至我们可以让语法变得更简洁。
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:
这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。只要”函数式接口“才能使用Lambda表达式。为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成"非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了。
1.2 Lambda作用
最直观的作用就是使得代码变得异常简洁。
接口要求
虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。也就是说一个接口中有且仅有一个抽象方法,还有default实现的方法也能使用Lambda表达式。
@FunctionalInterface注解作用:
@FunctionalInterface标记在接口上,“函数式接口”是指仅仅只包含一个抽象方法的接口。
1.3 Lambda表达式语法
1.3.1 Lambda表达式结构
语法结构:
(parameters) -> expression
或
(parameters) ->{ statements;}
语法形式为 () -> {}:
() 用来描述参数列表,如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可;
-> 读作(goes to),为 lambda运算符 ,固定写法,代表指向动作;
{} 代码块,具体要做的事情,也就是方法体内容;
1.3.2 Lambda表达式的重要特征
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号”()“:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号”{ }“:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
// 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.3 Lambda表达式使用
1.3.1 定义函数接口并使用Lambda表达式实现函数接口
函数接口指的是在该接口中只能含有一个抽象方法。可以有多个default修饰的方法或者是static方法。
package cn.it.bz.Lambda;
//无返回值无参数的函数接口
@FunctionalInterface
interface NoReturnNoParam{
public void method();
}
//无返回值有一个参数的函数接口
@FunctionalInterface
interface NoReturnOneParam {
public void method(int a);
}
//无返回值有两个参数的函数接口
@FunctionalInterface
interface NoReturnManyParam {
public void method(int a, int b);
}
//有返回值的无参数函数接口
@FunctionalInterface
interface ReturnNoParam {
public int method();
}
//有返回值有一个参数的函数接口
@FunctionalInterface
interface ReturnOneParam {
public int method(int a);
}
//有返回值有两个参数的函数接口
@FunctionalInterface
interface ReturnManyParam {
public int method(int a, int b);
}
public class Test {
public static void main(String[] args) {
//无返回值无参数的函数接口实现,类型就是接口名称,{}表示对抽象方法的具体实现
NoReturnNoParam noReturnNoParam = () -> {
System.out.println("noReturnNoParam");
};
//调用该方法
noReturnNoParam.method();
System.out.println("----------------------");
//无返回值一个参数的接口实现
NoReturnOneParam noReturnOneParam = (int a) -> {
System.out.println("noReturnOneParam"+a);
};
//调用该方法
noReturnOneParam.method(1);
System.out.println("---------------------");
//无返回值的两个参数的接口实现
NoReturnManyParam noReturnManyParam = (int a, int b) -> {
System.out.println("noReturnManyParam"+a+","+b);
};
//调用该方法
noReturnManyParam.method(1, 2);
System.out.println("--------------------");
//有返回值无参数的函数接口实现
ReturnNoParam returnNoParam = () -> {
System.out.println("returnNoParam");
return 123;
};
//调用该方法
int a = returnNoParam.method();
System.out.println("a="+a);
System.out.println("---------------------");
//有返回值有一个参数的函数接口实现
ReturnOneParam returnOneParam = (int a1) -> {
System.out.println("returnOneParam"+a1);
return a1;
};
//调用该方法
int b = returnOneParam.method(1);
System.out.println("b="+b);
System.out.println("--------------------");
//有返回值有两个参数的函数接口实现
ReturnManyParam returnManyParam = (int a1, int a2) -> a1 + a2;
// 调用该方法
int c = returnManyParam.method(1, 2);
System.out.println("c="+c);
}
}
1.3.2 简化Lambda表达式
1、只有一个参数时小括号可以省略。
2、参数列表中的参数可以写,可以不写。要写都写,
3、当方法体之哟一行代码时,大括号可以省略。
4、方法体中只有一行return时,return可以省略。
package cn.it.bz.Lambda;
//无返回值无参数的函数接口
@FunctionalInterface
interface NoReturnNoParam{
public void method();
}
//无返回值有一个参数的函数接口
@FunctionalInterface
interface NoReturnOneParam {
public void method(int a);
}
//无返回值有两个参数的函数接口
@FunctionalInterface
interface NoReturnManyParam {
public void method(int a, int b);
}
//有返回值的无参数函数接口
@FunctionalInterface
interface ReturnNoParam {
public int method();
}
//有返回值有一个参数的函数接口
@FunctionalInterface
interface ReturnOneParam {
public int method(int a);
}
//有返回值有两个参数的函数接口
@FunctionalInterface
interface ReturnManyParam {
public int method(int a, int b);
}
public class Test {
public static void main(String[] args) {
//无返回值无参数的函数接口实现
//方法体只有一行代码时,{}可以不写。
NoReturnNoParam noReturnNoParam = () -> System.out.println("noReturnNoParam");;
//调用该方法
noReturnNoParam.method();
System.out.println("----------------------");
//无返回值一个参数的接口实现
//当参数只有一个时,()可以不写;方法体只有一行代码时,{}可以不写。
NoReturnOneParam noReturnOneParam = a-> System.out.println("noReturnOneParam"+a);;
//调用该方法
noReturnOneParam.method(1);
System.out.println("---------------------");
//无返回值的两个参数的接口实现
//方法体只有一行代码时,{}可以不写。
NoReturnManyParam noReturnManyParam = (int a, int b) -> System.out.println("noReturnManyParam"+a+","+b);;
//调用该方法
noReturnManyParam.method(1, 2);
System.out.println("--------------------");
//有返回值无参数的函数接口实现
ReturnNoParam returnNoParam = () -> {
System.out.println("returnNoParam");
return 123;
};
//调用该方法
int a = returnNoParam.method();
System.out.println("a="+a);
System.out.println("---------------------");
//有返回值有一个参数的函数接口实现
//当参数只有一个时,()可以不写;
ReturnOneParam returnOneParam = (int a1) -> {
System.out.println("returnOneParam"+a1);
return a1;
};
//调用该方法
int b = returnOneParam.method(1);
System.out.println("b="+b);
System.out.println("--------------------");
//当方法体只有return一行代码时,return可以不写。
ReturnManyParam returnManyParam = (int a1, int a2) -> a1 + a2;
// 调用该方法
int c = returnManyParam.method(1, 2);
System.out.println("c="+c);
}
}
1.3.3 Lambda表达式引用方法
有时候我们不是必须使用Lambda的函数体定义实现,我们可以利用 lambda表达式指向一个已经存在的方法作为抽象方法的实现。
要求
-
参数的个数以及类型需要与函数接口中的抽象方法一致。
-
返回值类型要与函数接口中的抽象方法的返回值类型一致。
语法
方法归属者::方法名 静态方法的归属者为类名,非静态方法归属者为该对象的引用。
package cn.it.bz.Lambda;
@FunctionalInterface
interface ReturnOne {
public int method(int a);
}
public class Test2 {
//静态方法
public static int doubleNumber(int a){ return a*2; }
//非静态方法
public int doubleNumber2(int a) {return a * 2;}
public static void main(String[] args) {
//将静态方法作为接口中抽象方法的实现方法(前提是参数个数和参数类型必须相同)
//Test2::doubleNumber表示抽象方法的实现方法是Test类下的doubleNumber方法
ReturnOne returnOne1 = Test2::doubleNumber;
//调用
int method = returnOne1.method(10);
System.out.println(method);
//将非静态方法作为接口中抽象方法的实现方法
//先实例化非静态方法的归属者,通过对象引用实现
Test2 test2 = new Test2();
ReturnOne returnOne2 = test2::doubleNumber2;
int method1 = returnOne1.method(20);
System.out.println(method1);
}
}
1.3.4 创建线程
package cn.it.bz.Lambda;
//Runnable接口中只有一个抽象方法run,也就是说Runnable是个函数接口。
public class Test3 {
public static void main(String[] args) {
System.out.println("主线程"+ Thread.currentThread().getName()+"启动!");
//Lambda表达式实现run 方法。
Runnable runnable = () -> {
for (int i = 0; i < 10; i++ ) {
System.out.println(Thread.currentThread().getName() + ", "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
};
//线程包装
Thread thread = new Thread(runnable, "Lambda线程");
//线程启动
thread.start();
System.out.println("主线程"+ Thread.currentThread().getName()+"结束!");
}
}
//或者直接将run方法的实现放在Thread构造方法中。
new Thread(() -> {
for (int i = 0; i < 10; i++ ) {
System.out.println(Thread.currentThread().getName() + ", "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}, "Lambda线程").start();
1.3.5 Lambda 表达式中的闭包问题
什么是闭包
闭包的本质就是代码片断。所以闭包可以理解成一个代码片断的引用。在Java中匿名内部类也是闭包的一种实现方式。
在闭包中访问外部的变量时,外部变量必须是final类型,虚拟机会帮我们加上 final 修饰关键字。
二、常用函数接口
2.1 Consumer接口的使用
Consumer 接口是JDK为我们提供的一个函数式接口,该接口也被称为消费型接口。
遍历集合
我们可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是 Consumer 接口的方法以及遍历集合的操作。
实现方式一:
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
class ConsumerImpl implements Consumer {
//Object就是要消费的数据。
@Override
public void accept(Object o) {
System.out.println(o+"O(∩_∩)O");
}
}
public class TestConsumer {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
/* forEach可以实现取出集合中的元素但是不知道怎么处理取出的元素。
forEach方法需要的参数是Consumer接口实现类的对象,也就是说我需要给forEach
一个能消费它遍历出来的元素的对象。*/
list.forEach(new ConsumerImpl());
}
}
方式二:
如果仅仅是为了输出遍历的数据的话,这样写未免有些麻烦。我们平时使用的输出语句是System.out.println,打开API文档
我们可以发现,out实际上是System类下的静态成员变量,返回的是一个PrintStream类型的数据,该类下的println方法是个无返回值的含有一个Object类型参数的非静态方法。这和Consumer接口中的抽象方法accept的结构一毛一样,因此我们可以使用Lambda表达式将printlin方法作为accept的实现方法。
package cn.it.bz.Lambda;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
public class TestConsumer {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//将PrintStream类下的println方法作为Consumer接口的抽象方法accept方法的实现方法。
PrintStream out = System.out;
list.forEach(out::println);
}
}
2.2 Predicate接口的使用
Predicate 是 JDK 为我们提供的一个函数式接口,可以简化程序的编写。
删除集合中的元素
我们通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
class PredicateImpl implements Predicate {
@Override
public boolean test(Object o) {
return "b".equals(o);
}
}
public class TestPredicate {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//实例化PredicateImpl
PredicateImpl predicateImpl = new PredicateImpl();
list.removeIf(predicateImpl);
// //或者使用Lambda表达式
// list.removeIf((o)-> o.equals("b"));
//查看集合中是否还存在b元素
Consumer consumer = (a) -> System.out.println(a);
list.forEach(consumer);
}
}
2.3 Comparator接口的使用
Comparator是 JDK 为我们提供的一个函数式接口,该接口为比较器接口。
元素排序
之前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器重写 compare 方法的比较器对象,现在我们还可以使用 lambda 表达式来简化代码。
排序方式一:
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class ComparatorImpl implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
public class TestComparator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("c");
list.add("d");
list.add("b");
list.add("a");
//元素排序
ComparatorImpl comparatorImpl = new ComparatorImpl();
list.sort(comparatorImpl);
//输出
list.forEach(System.out::println);
}
}
方式二使用Lambda表达式、
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.List;
public class TestComparator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("c");
list.add("d");
list.add("b");
list.add("a");
//元素排序
list.sort((o1,o2) ->o1.compareTo(o2));
//输出
list.forEach(System.out::println);
}
}
三、Stream流
3.1 Stream流简介
Stream是数据渠道,用于操作数据源所生成的元素序列,它可以实现对集合的复杂操作,例如过滤、排序和映射等。Stream不会改变源对象,而是返回一个新的结果集。
Stream流的生成方式
- 生成流:通过数据源(集合、数组等)创建一个流。
- 中间操作:一个流后面可以跟随零个或者多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用。
- 终结操作:一旦执行终止操作,就执行中间的链式操作,并产生结果。
collection接口中有一个Stream类型的方法,该接口下的实现类实现了该抽象方法,也就是说Collection接口下的List、set等单例集合都存在一个Stream方法返回一个对应的Streatm对象。
3.2 Stream流的常见方法
3.2.1 数据过滤
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStream {
public static void main(String[] args) {
//生成Stream流对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("O(∩_∩)O");
list.add("张三");
list.add("张无形");
Stream<String> stream = list.stream();
//操作流对象
//数据过滤,实现多条件and关系,以张开头三结尾的元素
List<String> collect = stream.filter(o -> o.startsWith("张")).filter(o -> o.endsWith("三")).collect(Collectors.toList());
//遍历过滤后的结合
collect.forEach(System.out::println);
System.out.println("--------------------");
Stream<String> stream1 = list.stream();
//多条件的or关系
//先创建or关系
Predicate<String> predicate = (o) ->o.startsWith("张");
Predicate<String> predicate2 = (o) -> o.startsWith("O");
List<String> collect1 = stream1.filter(predicate.or(predicate2)).collect(Collectors.toList());
//遍历过滤后的结合
collect1.forEach(System.out::println);
}
}
3.2.2 数量限制
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStream2 {
public static void main(String[] args) {
//生成Stream流对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("O(∩_∩)O");
list.add("张三");
list.add("张无形");
Stream<String> stream = list.stream();
//获取前两个元素
List<String> collect = stream.limit(2).collect(Collectors.toList());
//遍历
collect.forEach(System.out:: println);
}
}
3.2.3 元素排序
package cn.it.bz.Lambda;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStream3 {
public static void main(String[] args) {
//生成Stream流对象
List<String> list = new ArrayList<>();
list.add("a");
list.add("O(∩_∩)O");
list.add("张三");
list.add("张无形");
//按照升序排序
List<String> collect = list.stream().sorted().collect(Collectors.toList());
//遍历
collect.forEach(System.out::println);
System.out.println("--------------------");
//降序
list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
}
}