Lambda 表達(dá)式,即函數(shù)式編程是 JDK8 的一個(gè)新特性,也被稱為閉包,Lambda表達(dá)式允許把函數(shù)作為一個(gè)方法的參數(shù),即行為參數(shù)化,函數(shù)作為參數(shù)傳遞進(jìn)方法中。
Lambda表達(dá)式可以取代大部分的匿名內(nèi)部類,寫出更優(yōu)雅的 Java 代碼,尤其在集合的遍歷和其他集合操作中,可以極大地優(yōu)化代碼結(jié)構(gòu)。
總結(jié)就是:使用不可變值與函數(shù),函數(shù)對不可變值進(jìn)行處理,映射成另一個(gè)值。
一、什么是函數(shù)式接口
函數(shù)接口是只有一個(gè)抽象方法的接口,用作 Lambda 表達(dá)式的類型。使用@FunctionalInterface注解修飾的類,編譯器會檢測該類是否只有一個(gè)抽象方法或接口,否則,會報(bào)錯??梢杂卸鄠€(gè)默認(rèn)方法,靜態(tài)方法。
lambda 表達(dá)式是一小段代碼,它接受參數(shù)并返回一個(gè)值。Lambda 表達(dá)式類似于方法,但它們不需要名稱,并且可以直接在方法體中實(shí)現(xiàn)。
語法:最簡單的 lambda 表達(dá)式包含一個(gè)參數(shù)和一個(gè)表達(dá)式:
零參數(shù):
() -> System.out.println("零參數(shù) lambda");
一個(gè)參數(shù):
p -> System.out.println("一個(gè)參數(shù):" + param);
多個(gè)參數(shù):
(p1 [,p2,p3,....pn]) -> System.out.println("多個(gè)參數(shù):" + p1 + ", " + p2 + ... + pn);
上面的表達(dá)式有一定的限制。它們要么返回一個(gè)值要么執(zhí)行一段方法,并且它們不能包含變量、賦值或語句,例如if or for 。為了進(jìn)行更復(fù)雜的操作,可以使用帶有花括號的代碼塊。如果 lambda 表達(dá)式需要返回一個(gè)值,那么代碼塊應(yīng)該有一個(gè)return語句。
(parameter1, parameter2) -> { code block [return] }
1、方法引用
類 :: 靜態(tài)方法
Consumer<String> c = [ (s) -> System.out.println(s); <=> System.out::println; ]
對象 :: 實(shí)例方法
List<String> list = Lists.newArrayList();
Consumer<String> c = [ (e) => list.add(e); <=> list::add; ]
構(gòu)造器 :: new
Supplier<List<String>> s = [ () -> new ArrayList<>(); <=> ArrayList::new; ]
2、@FunctionalInterface注解
有且只有一個(gè)抽象方法的接口被稱為函數(shù)式接口,函數(shù)式接口適用于函數(shù)式編程的場景,Lambda就是Java中函數(shù)式編程的體現(xiàn),可以使用Lambda表達(dá)式創(chuàng)建一個(gè)函數(shù)式接口的對象,一定要確保接口中有且只有一個(gè)抽象方法,這樣Lambda才能順利的進(jìn)行推導(dǎo)。
與@Override 注解的作用類似,Java 8中專門為函數(shù)式接口引入了一個(gè)新的注解:@FunctionalInterface 。該注解可用于一個(gè)接口的定義上,一旦使用該注解來定義接口,編譯器將會強(qiáng)制檢查該接口是否確實(shí)有且僅有一個(gè)抽象方法(equal和hashcode方法不算),否則將會報(bào)錯。但是這個(gè)注解不是必須的,只要符合函數(shù)式接口的定義,那么這個(gè)接口就是函數(shù)式接口。
3、 java8自帶的常用函數(shù)式接口。

4、簡單的例子
package com.biyu.study.lambda;
import com.biyu.study.lambda.bean.Employee;
import java.math.BigDecimal;
import java.util.function.*;
public class Test {
public static void main(String[] args) {
Predicate<Integer> predicate = x -> x > 20;
Employee employee = new Employee("Lily", "女", 18);
System.out.println("Lily的大于18歲嗎?:" + predicate.test(employee.getAge()));
Consumer<String> consumer = System.out::println;
consumer.accept("Hello World");
Function<Employee, String> function = Employee::getName;
String name = function.apply(employee);
System.out.println(name);
Supplier<Integer> supplier = () -> Integer.valueOf(BigDecimal.TEN.toString());
System.out.println(supplier.get());
UnaryOperator<Boolean> unaryOperator = uglily -> !uglily;
Boolean apply2 = unaryOperator.apply(true);
System.out.println(apply2);
BinaryOperator<Integer> operator = (x, y) -> x * y;
Integer integer = operator.apply(2, 3);
System.out.println(integer);
test(() -> "函數(shù)式接口");
}
/**
* 自定義函數(shù)式接口使用
*
* @param worker
*/
public static void test(Worker worker) {
String work = worker.work();
System.out.println(work);
}
public interface Worker {
String work();
}
}

以上演示了lambda接口的使用及自定義一個(gè)函數(shù)式接口并使用。
二、Lambda 流的常用方法
1、ForEach
集合的遍歷 forEach 方法:
public static void testForEach(){
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("3");
}};
list.forEach(s-> System.out.println(s));
}

2、Collect
將操作后的對象轉(zhuǎn)化為新的對象:
public static void testCollect(){
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("2");
}};
//轉(zhuǎn)換為新的list
List newList = list.stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
System.out.println(newList);
}
3、Filter
Filter 為過濾的意思,只要滿足 Filter 表達(dá)式的數(shù)據(jù)就可以留下來,不滿足的數(shù)據(jù)被過濾掉。
public static void testFilter() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("3");
}};
List<String> filtered = list.stream()
// 過濾掉我們希望留下來的值
// 表示我們希望字符串是 1 能留下來
// 其他的過濾掉
.filter(str -> "1".equals(str))
.collect(Collectors.toList());
System.out.println(filtered);
}

4、Map
map 方法可以讓我們進(jìn)行一些流的轉(zhuǎn)化,比如原來流中的元素是 A,通過 map 操作,可以使返回的流中的元素是 B。
public static void testMap() {
List<String> list = new ArrayList<String>() {{
add("A");
add("B");
add("C");
}};
//通過 map 方法list中元素轉(zhuǎn)化成 小寫
List<String> strLowerList = list.stream()
.map(str -> str.toLowerCase())
.collect(Collectors.toList());
System.out.println(strLowerList);
}

5、MapToInt
mapToInt 方法的功能和 map 方法一樣,只不過 mapToInt 返回的結(jié)果已經(jīng)沒有泛型,已經(jīng)明確是 int 類型的流了。
public static void testMapToInt() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("3");
}};
List<Integer> intList=list.stream()
.mapToInt(s->Integer.valueOf(s))
// 一定要有 mapToObj,因?yàn)?mapToInt 返回的是 IntStream,因?yàn)橐呀?jīng)確定是 int 類型了,所以沒有泛型的,
// 而 Collectors.toList() 強(qiáng)制要求有泛型的流,所以需要使用 mapToObj方法返回有泛型的流
.mapToObj(s->s)
.collect(Collectors.toList());
System.out.println(intList);
Double sum = list.stream()
.mapToDouble(s->Double.valueOf(s))
// DoubleStream/IntStream 有許多 sum(求和)、min(求最小值)、max(求最大值)、average(求平均值)等方法
.sum();
System.out.println(sum);
}

6、Distinct
distinct 方法有去重的功能:
public static void testDistinct() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("2");
add("5");
add("3");
}};
List<Integer> intList = list.stream()
.map(s -> Integer.valueOf(s))
.distinct()
.collect(Collectors.toList());
System.out.println(intList);
}

7、Sorted
Sorted 方法提供了排序的功能,并且允許我們自定義排序。
public static void testSorted() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("-1");
add("9");
add("11");
add("27");
add("0");
}};
List<Integer> intList = list.stream()
.map(s -> Integer.valueOf(s))
// 等同于 .sorted(Comparator.naturalOrder()) 自然排序
.sorted()
.collect(Collectors.toList());
System.out.println(intList);
// 自定義排序器
intList =list.stream()
.map(s -> Integer.valueOf(s))
// 反自然排序
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
System.out.println(intList);
}

8、groupingBy
groupingBy 是能夠根據(jù)字段進(jìn)行分組,toMap 是把 List 的數(shù)據(jù)格式轉(zhuǎn)化成 Map 的格式。
public static void testGroupBy() {
List<String> list = new ArrayList<String>() {{
add("iphone 4s");
add("iphone 6");
add("iphone 12");
add("iphone 14");
add("xiaomi");
add("huawei");
add("oppo");
}};
Map<String, List<String>> strList = list.stream().collect(Collectors.groupingBy(s -> {
if (s.startsWith("iphone")){
return "iphone";
} else {
return "other";
}
}));
System.out.println(strList);
}

9、FindFirst
findFirst 表示匹配到第一個(gè)滿足條件的值就返回:
public static void testFindFirst() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("2");
}};
list.stream()
.filter(s -> "2".equals(s))
.findFirst()
.get();
// 防止空指針
list.stream()
.filter(s -> "2".equals(s))
.findFirst()
// orElse 表示如果 findFirst 返回 null 的話,就返回 orElse 里的內(nèi)容
.orElse("3");
Optional<String> str = list.stream()
.filter(s -> "2".equals(s))
.findFirst();
// isPresent 為 true 的話,表示 value != null
if (str.isPresent()) {
return;
}
}
10、Reduce
reduce 方法允許我們在循環(huán)里面疊加計(jì)算值:
public static void testReduce() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("3");
}};
int sum = list.stream()
.map(s -> Integer.valueOf(s))
// s1 和 s2 表示循環(huán)中的前后兩個(gè)數(shù)
.reduce((s1, s2) -> s1 + s2)
.orElse(0);
System.out.println(sum);
sum =list.stream()
.map(s -> Integer.valueOf(s))
// 第一個(gè)參數(shù)表示基數(shù),會從 100 開始加
.reduce(100, (s1, s2) -> s1 + s2);
System.out.println(sum);
}

11、Peek
peek 方法很簡單,我們在 peek 方法里面做任意沒有返回值的事情,比如打印日志:
public static void testPeek() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("3");
}};
list.stream().map(s -> Integer.valueOf(s))
.peek(s -> System.out.println(s))
.collect(Collectors.toList());
}

12、Limit
limit 方法會限制輸出值個(gè)數(shù),入?yún)⑹窍拗频膫€(gè)數(shù)大小:
public static void testLimit() {
List<String> list = new ArrayList<String>() {{
add("1");
add("2");
add("3");
add("4");
add("5");
}};
List<Integer> integers= list.stream()
.map(s -> Integer.valueOf(s))
.limit(3L)
.collect(Collectors.toList());
System.out.println(integers);
}

13、Max,Min
通過 max、min 方法,可以獲取集合中最大、最小的對象。文章來源:http://www.zghlxwxcb.cn/news/detail-762703.html
public static void testMaxMin() {
List<String> list = new ArrayList<String>() {{
add("1");
add("3");
add("2");
add("-12");
add("99");
}};
String maxNum = list.stream().max(Comparator.comparing(s -> Integer.valueOf(s))).get();
String minNum = list.stream().min(Comparator.comparing(s -> Integer.valueOf(s))).get();
System.out.println(maxNum);
System.out.println(minNum);
}

三、寫在最后
本章節(jié)主要從實(shí)際使用講述了常用的方法及流,文中例子主要是為了講解較為簡單,大家可以去使用java8重構(gòu)自己現(xiàn)有的代碼,自行領(lǐng)會lambda的奧妙使用。java8可以很清晰表達(dá)你要做什么,代碼也很簡潔。文章來源地址http://www.zghlxwxcb.cn/news/detail-762703.html
到了這里,關(guān)于【Java基礎(chǔ)】Java Lambda表達(dá)式詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!