java8 lambda python_【学习笔记】java8 Lambda表达式语法及应用
本文是慕課網(wǎng)大牧莫邪老師的視頻教程一課掌握Lambda表達(dá)式語(yǔ)法及應(yīng)用的學(xué)習(xí)筆記。如果覺(jué)得內(nèi)容對(duì)你有用,可以購(gòu)買(mǎi)老師的課程支持一下,課程價(jià)格1元,十分良心了。
1. 課程介紹
2. 為什么引入Lambda表達(dá)式
2.1 什么是Lambda表達(dá)式
Lambda表達(dá)式也稱箭頭函數(shù)、匿名函數(shù)、閉包
Lambda表達(dá)式體現(xiàn)的是輕量級(jí)函數(shù)式編程思想
-> 符號(hào)是Lambda表達(dá)式核心操作符號(hào),符號(hào)左側(cè)是操作參數(shù),符號(hào)右側(cè)是操作表達(dá)式
Lambda表達(dá)式是 jdk1.8 提供的新特性
2.2 Model Code as Data(MCAD模式)
Model Code as Data,編碼即數(shù)據(jù),盡可能輕量級(jí)的將代碼封裝為數(shù)據(jù)
解決方案:接口&實(shí)現(xiàn)類(lèi)(匿名內(nèi)部類(lèi))
存在問(wèn)題:語(yǔ)法冗余、thIs關(guān)鍵字、變量捕獲、數(shù)據(jù)控制等
傳統(tǒng)模式下,新線程的創(chuàng)建:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("threading ..." + Thread.currentThread().getName());
}
}).start();
使用jdk8新特性,lambda表達(dá)式優(yōu)化線程模型
new Thread(() -> {
System.out.println("threading ..." + Thread.currentThread().getName());
}).start();
2.3 項(xiàng)目問(wèn)題:功能接口的設(shè)計(jì)及優(yōu)化
需求環(huán)境:線程類(lèi)的創(chuàng)建
解決方案:匿名內(nèi)部類(lèi)的實(shí)現(xiàn)
解決方法:lambda表達(dá)式實(shí)現(xiàn)
2.4 為什么要使用lambda表達(dá)式
它不是解決未知問(wèn)題的新技術(shù)
對(duì)現(xiàn)有解決方案的語(yǔ)義化優(yōu)化
需要根據(jù)實(shí)際需求考慮性能問(wèn)題
3. Lambda表達(dá)式的基礎(chǔ)知識(shí)
3.1 函數(shù)式接口概述和定義
函數(shù)式接口,就是java類(lèi)型系統(tǒng)中的接口
函數(shù)式接口,是只包含一個(gè)接口方法的特殊接口
語(yǔ)義化檢測(cè)注解:@FunctionalInterface
可以像定義普通接口一樣定義函數(shù)式接口,并且接口內(nèi)只有一個(gè)抽象方法:
@FunctionalInterface
public interface IUserCredential {
/**
* 通過(guò)用戶賬號(hào),驗(yàn)證用戶身份信息的接口
* @param username 要驗(yàn)證的用戶賬號(hào)
* @return 返回身份信息[系統(tǒng)管理員、用戶管理員、普通用戶]
*/
String verifyUser(String username);
// 添加這個(gè)方法后,會(huì)報(bào)錯(cuò)
// boolean test();
由于接口添加了@FunctionalInterface注解,表明是一個(gè)函數(shù)式接口,內(nèi)部只能有一個(gè)抽象方法,如果再添加一個(gè)抽象方法boolean test(),就會(huì)報(bào)錯(cuò):Multiple non-overriding abstract methods found in interface com.imooc.IUserCredential
注意:@FunctionalInterface 注解也可以不加,函數(shù)式接口只需要滿足以下兩個(gè)條件即可:
定義一個(gè)接口
接口中只有一個(gè)抽象方法
3.2 默認(rèn)方法和靜態(tài)方法
1. 接口的默認(rèn)方法:default關(guān)鍵字修飾
@FunctionalInterface
public interface IUserCredential {
/**
* 通過(guò)用戶賬號(hào),驗(yàn)證用戶身份信息的接口
* @param username 要驗(yàn)證的用戶賬號(hào)
* @return 返回身份信息[系統(tǒng)管理員、用戶管理員、普通用戶]
*/
String verifyUser(String username);
/**
* 接口的默認(rèn)方法
*/
default String getCredential(String username) {
// 模擬方法
if ("admin".equals(username)) {
return "admin + 系統(tǒng)管理員用戶";
} else if("manager".equals(username)){
return "manager + 用戶管理員用戶";
} else {
return "commons + 普通會(huì)員用戶";
}
}
}
接口的默認(rèn)方法使用default關(guān)鍵字修飾,調(diào)用時(shí),需要用接口的實(shí)例調(diào)用:
IUserCredential instance = new xxx(); // 實(shí)例化接口中的實(shí)現(xiàn)類(lèi)
instance.getCredential("admin");
2. 接口的靜態(tài)方法:static關(guān)鍵字修飾
@FunctionalInterface
public interface IUserCredential {
/**
* 通過(guò)用戶賬號(hào),驗(yàn)證用戶身份信息的接口
* @param username 要驗(yàn)證的用戶賬號(hào)
* @return 返回身份信息[系統(tǒng)管理員、用戶管理員、普通用戶]
*/
String verifyUser(String username);
/**
* 接口的靜態(tài)方法
*/
static String getCredential(String username) {
// 模擬方法
if ("admin".equals(username)) {
return "admin + 系統(tǒng)管理員用戶";
} else if("manager".equals(username)){
return "manager + 用戶管理員用戶";
} else {
return "commons + 普通會(huì)員用戶";
}
}
}
同普通類(lèi)的靜態(tài)方法一樣,接口的靜態(tài)方法在調(diào)用時(shí),直接調(diào)用即可,不需要實(shí)例化接口實(shí)例:
// 直接使用 接口名.方法名 調(diào)用
IUserCredential.getCredential("admin");
3. 來(lái)自O(shè)bject繼承的方法
由于接口或類(lèi)都是Object的子類(lèi),如果我們?cè)诮涌谥性黾右粋€(gè)由Object類(lèi)繼承過(guò)來(lái)的抽象方法,接口依然不會(huì)報(bào)錯(cuò):
@FunctionalInterface
public interface IUserCredential {
/**
* 通過(guò)用戶賬號(hào),驗(yàn)證用戶身份信息的接口
* @param username 要驗(yàn)證的用戶賬號(hào)
* @return 返回身份信息[系統(tǒng)管理員、用戶管理員、普通用戶]
*/
String verifyUser(String username);
/**
* 這里的toString()方法是Object類(lèi)繼承的,添加后并不會(huì)報(bào)錯(cuò)
* @return
*/
@Override
String toString();
}
以上接口雖然有兩個(gè)抽象類(lèi),但由于toString()方法是從Object類(lèi)繼承的,因此并不會(huì)報(bào)錯(cuò),該接口依然是一個(gè)函數(shù)式接口。
3.3 Lambda表達(dá)式和函數(shù)式接口的關(guān)系
在jdk1.8之前,我們使用匿名內(nèi)部類(lèi),實(shí)現(xiàn)接口的抽象方法:
IUserCredential ic2 = new IUserCredential() {
@Override
public String verifyUser(String username) {
return "admin".equals(username)?"管理員":"會(huì)員";
}
};
在jdk1.8,使用lambda表達(dá)式,針對(duì)函數(shù)式接口的簡(jiǎn)單實(shí)現(xiàn)
IUserCredential ic3 = (String username) -> {
return "admin".equals(username) ? "lbd管理員" : "lbd會(huì)員";
};
lambda表達(dá)式 是 函數(shù)式接口的一種實(shí)現(xiàn).
3.4 jdk中常見(jiàn)的函數(shù)式接口
在java.util.function提供了大量的函數(shù)式接口:
Predicate 接收參數(shù)T對(duì)象,返回一個(gè)boolean類(lèi)型結(jié)果
@FunctionalInterface
public interface Predicate {
boolean test(T t);
// 省略靜態(tài)方法和默認(rèn)方法
}
Consumer 接收參數(shù)T對(duì)象,沒(méi)有返回值
@FunctionalInterface
public interface Consumer {
void accept(T t);
// 省略靜態(tài)方法和默認(rèn)方法
}
Function 接收參數(shù)T對(duì)象,返回R對(duì)象
@FunctionalInterface
public interface Function {
R apply(T t);
// 省略靜態(tài)方法和默認(rèn)方法
}
Supplier 不接受任何參數(shù),直接通過(guò)get()獲取指定類(lèi)型的對(duì)象
@FunctionalInterface
public interface Supplier {
T get();
}
UnaryOperator 接口參數(shù)T對(duì)象,執(zhí)行業(yè)務(wù)處理后,返回更新后的T對(duì)象
@FunctionalInterface
public interface UnaryOperator extends Function {
static UnaryOperator identity() {
return t -> t;
}
}
BinaryOperator 接口接收兩個(gè)T對(duì)象,執(zhí)行業(yè)務(wù)處理后,返回一個(gè)T對(duì)象
public interface BiFunction {
R apply(T t, U u);
// 省略靜態(tài)方法和默認(rèn)方法
}
@FunctionalInterface
public interface BinaryOperator extends BiFunction {
// 省略靜態(tài)方法和默認(rèn)方法
}
示例:
// 1. Predicate 接收參數(shù)T對(duì)象,返回一個(gè)boolean類(lèi)型結(jié)果
Predicate pre = (String username) -> {
return "admin".equals(username);
};
System.out.println(pre.test("manager"));
// 2. Consumer 接收參數(shù)T對(duì)象,沒(méi)有返回值
Consumer con = (String message) -> {
System.out.println("要發(fā)送的消息:" + message);
System.out.println("消息發(fā)送完成");
};
con.accept("hello 慕課網(wǎng)的學(xué)員們..");
// 3. Function 接收參數(shù)T對(duì)象,返回R對(duì)象
Function fun = (String gender) -> {
return "male".equals(gender) ? 1 : 0;
};
System.out.println(fun.apply("male"));
// 4. Supplier 不接受任何參數(shù),直接通過(guò)get()獲取指定類(lèi)型的對(duì)象
Supplier sup = () -> {
return UUID.randomUUID().toString();
};
System.out.println(sup.get());
// 5. UnaryOperator 接口參數(shù)T對(duì)象,執(zhí)行業(yè)務(wù)處理后,返回更新后的T對(duì)象
UnaryOperator uo = (String img)-> {
img += "[100x200]";
return img;
};
System.out.println(uo.apply("原圖--"));
// 6. BinaryOperator 接口接收兩個(gè)T對(duì)象,執(zhí)行業(yè)務(wù)處理后,返回一個(gè)T對(duì)象
BinaryOperator bo = (Integer i1, Integer i2) -> {
return i1 > i2? i1: i2;
};
System.out.println(bo.apply(12, 13));
3.5 Lambda表達(dá)式基本語(yǔ)法
聲明:就是和lambda表達(dá)式綁定的接口類(lèi)型
參數(shù):包含在一對(duì)圓括號(hào)中,和綁定的接口中的抽象方法中的參數(shù)個(gè)數(shù)及順序一致。
操作符:->
執(zhí)行代碼塊:包含在一對(duì)大括號(hào)中,出現(xiàn)在操作符號(hào)的右側(cè)
示例如下:
首先定義3個(gè)接口:
// 1. 沒(méi)有參數(shù),沒(méi)有返回值的lambda表達(dá)式綁定的接口
interface ILambda1{
void test();
}
// 2. 帶有參數(shù),沒(méi)有返回值的lambda表達(dá)式
interface ILambda2{
void test(String name, int age);
}
// 3. 帶有參數(shù),帶有返回值的lambda表達(dá)式
interface ILambda3 {
int test(int x, int y);
}
編寫(xiě)Lambda表達(dá)式示例:
// 1. [接口聲明] = (參數(shù)) -> {執(zhí)行代碼塊};
ILambda1 i1 = () -> {
System.out.println("hello imooc!");
System.out.println("welcome to imooc!");
};
i1.test();
// 2. lambda表達(dá)式的返回值,如果代碼塊只有一行,并且沒(méi)有大括號(hào),不用寫(xiě)return關(guān)鍵字,單行代碼的執(zhí)行結(jié)果,會(huì)自動(dòng)返回
ILambda1 i2 = () -> System.out.println("hello imooc");
i2.test();
// 3. 帶有多個(gè)參數(shù)的Lambda表達(dá)式
ILambda2 i21 = (String n, int a) -> {
System.out.println(n + "say: my year's old is " + a);
};
i21.test("jerry", 18);
// 4. 不寫(xiě)參數(shù)類(lèi)型,由jvm自動(dòng)推導(dǎo)
ILambda2 i22 = (n, a) -> {
System.out.println(n + " 說(shuō):我今年" + a + "歲了.");
};
i22.test("tom", 22);
// 5. 帶有返回值的Lambda表達(dá)式
ILambda3 i3 = (x, y) -> {
int z = x + y;
return z;
};
System.out.println(i3.test(11, 22));
// 6. 只有一行時(shí),可以省略大括號(hào)和return字段
ILambda3 i31 = (x, y) -> x + y;
System.out.println(i31.test(100, 200));
小結(jié):
lambda表達(dá)式,必須和接口進(jìn)行綁定。
lambda表達(dá)式的參數(shù),可以附帶0個(gè)到n個(gè)參數(shù),括號(hào)中的參數(shù)類(lèi)型可以不用指定,jvm在運(yùn)行時(shí),會(huì)自動(dòng)根據(jù)綁定的抽象方法中參數(shù)進(jìn)行推導(dǎo)。
lambda表達(dá)式的返回值,如果代碼塊只有一行,并且沒(méi)有大括號(hào),不用寫(xiě)return關(guān)鍵字,單行代碼的執(zhí)行結(jié)果,會(huì)自動(dòng)返回。如果添加了大括號(hào),或者有多行代碼,必須通過(guò)return關(guān)鍵字返回執(zhí)行結(jié)果。
3.6 變量捕獲——變量的訪問(wèn)操作
1. 匿名內(nèi)部類(lèi)型中對(duì)于變量的訪問(wèn)
public void testInnerClass() {
String s2 = "局部變量";
new Thread(new Runnable() {
String s3 = "內(nèi)部變量";
@Override
public void run() {
// 訪問(wèn)全局變量
// System.out.println(this.s1); // 無(wú)法訪問(wèn)s1,這里的this關(guān)鍵字表示是當(dāng)前內(nèi)部類(lèi)型的對(duì)象
System.out.println(s1);
System.out.println(s2); // 局部變量的訪問(wèn)
// s2 = "hello"; // 不能對(duì)局部變量進(jìn)行數(shù)據(jù)的修改[final]
System.out.println(s3);
System.out.println(this.s3);
}
}).start();
}
在匿名內(nèi)部類(lèi)中,
this關(guān)鍵字表示的是當(dāng)前內(nèi)部類(lèi)型的對(duì)象
局部變量的訪問(wèn)時(shí),不能對(duì)局部變量進(jìn)行數(shù)據(jù)的修改(默認(rèn)為final類(lèi)型)
2. lambda表達(dá)式變量捕獲
public void testLambda() {
String s2 = "局部變量lambda";
new Thread(() -> {
String s3 = "內(nèi)部變量lambda";
// 訪問(wèn)全局變量
System.out.println(this.s1);// this關(guān)鍵字,表示的就是所屬方法所在類(lèi)型的對(duì)象
// 訪問(wèn)局部變量
System.out.println(s2);
// s2 = "hello";// 不能進(jìn)行數(shù)據(jù)修改,默認(rèn)推導(dǎo)變量的修飾符:final
System.out.println(s3);
s3 = "labmda 內(nèi)部變量直接修改";
System.out.println(s3);
}).start();
}
在Lambda表達(dá)式中,
this關(guān)鍵字,表示的就是所屬方法所在類(lèi)型的對(duì)象
修改局部時(shí),同樣會(huì)報(bào)錯(cuò):默認(rèn)推導(dǎo)變量的修飾符為final
3.7 Lambda表達(dá)式類(lèi)型檢查
首先定義個(gè)函數(shù)式接口:
@FunctionalInterface
interface MyInterface {
R strategy (T t, R r);
}
定義方法:
public static void test(MyInterface inter) {
List list = inter.strategy("hello", new ArrayList());
System.out.println(list);
}
匿名內(nèi)部類(lèi)調(diào)用:
test(new MyInterface() {
@Override
public List strategy(String s, List list) {
list.add(s);
return list;
}
});
Lambda表達(dá)式調(diào)用:
test((x, y) -> {
y.add(x);
return y;
});
Lambda表達(dá)式方法推導(dǎo):
(x,y)->{..} --> test(param) --> param==MyInterface --> lambda表達(dá)式-> MyInterface類(lèi)型
這個(gè)就是對(duì)于lambda表達(dá)式的類(lèi)型檢查,MyInterface接口就是lambda表達(dá)式的目標(biāo)類(lèi)型(target typing)
Lambda表達(dá)式方法參數(shù)推導(dǎo):
(x,y)->{..} --> MyInterface.strategy(T r, R r)--> MyInterface inter
--> T==String R==List --> lambda--> (x, y) == strategy(T t , R r)--> x==T==String y==R==List
lambda表達(dá)式參數(shù)的類(lèi)型檢查
3.8 方法重載和Lambda表達(dá)式
先定義兩個(gè)函數(shù)式接口:
interface Param1 {
void outInfo(String info);
}
interface Param2 {
void outInfo(String info);
}
再定義兩個(gè)重載方法:
// 定義重載的方法
public void lambdaMethod(Param1 param) {
param.outInfo("hello param1 imooc!");
}
public void lambdaMethod(Param2 param) {
param.outInfo("hello param2 imooc");
}
使用匿名內(nèi)部類(lèi)調(diào)用:
app.lambdaMethod(new Param1() {
@Override
public void outInfo(String info) {
System.out.println(info);
}
});
app.lambdaMethod(new Param2() {
@Override
public void outInfo(String info) {
System.out.println("------");
System.out.println(info);
}
});
這里能正常運(yùn)行,但是在使用Lambda表達(dá)式時(shí),會(huì)有問(wèn)題:
app.lambdaMethod( (String info) -> {
System.out.println(info);
});
異常信息:
Ambiguous method call. Both
lambdaMethod(Param1) in Test?and
lambdaMethod(Param2) in Test?match
jvm對(duì)Lambda表達(dá)式調(diào)用的方法推導(dǎo)如下:
lambda表達(dá)式存在類(lèi)型檢查-> 自動(dòng)推導(dǎo)lambda表達(dá)式的目標(biāo)類(lèi)型
lambdaMethod() -> 方法 -> 重載方法
-> Param1 函數(shù)式接口
-> Param2 函數(shù)式接口
調(diào)用方法-> 傳遞Lambda表達(dá)式-> 自動(dòng)推導(dǎo)->
-> Param1 | Param2
因此,在調(diào)用時(shí),需要人為地告訴jvm我們要調(diào)用的方法參數(shù)是啥:
app.lambdaMethod((Param1) (String info) -> {
System.out.println(info);
});
3.9 深入理解lambda表達(dá)式
Lambda表達(dá)式在jvm謹(jǐn)慎解析在私有靜態(tài)方法和匿名內(nèi)部類(lèi)型,通過(guò)實(shí)現(xiàn)接口的匿名內(nèi)部類(lèi)型中接口方法調(diào)用靜態(tài)實(shí)現(xiàn)方法,完成Lambda表達(dá)式的執(zhí)行。
先準(zhǔn)備一個(gè)App.java類(lèi):
// 函數(shù)式接口
interface IMarkUp {
void markUp(String msg);
}
public class App {
public static void main(String [] args) {
IMarkUp mu = (message) -> System.out.println(message);
mu.markUp("lambda!");
}
}
使用javac 編譯下,再使用javap查看:
$ javac App.java
$ javap -p App.class
Compiled from "App.java"
public class App {
public App();
public static void main(java.lang.String[]);
private static void lambda$main$0(java.lang.String);
}
可以看到,自動(dòng)生成了私有靜態(tài)方法private static void lambda$main$0(java.lang.String),Lambda在實(shí)際運(yùn)行時(shí),也是生成了一個(gè)私有靜態(tài)方法:
private static void lambda$main$0(String message) {
System.out.println(message);
}
為了查看編譯過(guò)程,我們使用參數(shù)-Djdk.internal.lambda.dumpProxyClasses重新處理:
$ java -Djdk.internal.lambda.dumpProxyClasses App
lambda!
運(yùn)行后,發(fā)現(xiàn)多生成了一個(gè)類(lèi):App$$Lambda$1.class,使用javap -p App$$Lambda$1查看類(lèi)的信息:
$ javap -p App$$Lambda$1
final class App$$Lambda$1 implements IMarkUp {
private App$$Lambda$1();
public void markUp(java.lang.String);
}
這里的markUp(java.lang.String)方法實(shí)際調(diào)用的是 lambda$main$0:
public void markUp(String message) {
App.lambda$main$0(message);
}
總結(jié)下lambda表達(dá)式的底層執(zhí)行過(guò)程:
在編譯時(shí),會(huì)自動(dòng)生成私有靜態(tài)方法 private static void lambda$main$0(java.lang.String)
在編譯時(shí),會(huì)自動(dòng)生成實(shí)現(xiàn)類(lèi):final class App$$Lambda$1 implements IMarkUp
調(diào)用mu.markUp("lambda!"),實(shí)際上進(jìn)行的操作是new App$$Lambda$1().markUp("lambda!")
代碼如下:
interface IMarkUp {
void markUp(String msg);
}
public class App {
public static void main(String [] args) {
IMarkUp mu = (message) -> System.out.println(message);
mu.markUp("lambda!");
// 3. 實(shí)際調(diào)用: new App$$Lambda$1().markUp("lambda!");
}
// 1. 自動(dòng)生成的私有靜態(tài)方法
/*
private static void lambda$main$0(String message) {
System.out.println(message);
}
*/
// 2. 自動(dòng)生成的內(nèi)部類(lèi)
/*
final class App$$Lambda$1 implements IMarkUp {
private App$$Lambda$1() {
}
public void markUp(String message) {
App.lambda$main$0(message);
}
}
*/
}
4. Lambda表達(dá)式在集合中的運(yùn)用
4.1 方法引用
方法引用是結(jié)合Lambda表達(dá)式的一種語(yǔ)法特性,主要分為靜態(tài)方法引用、實(shí)例方法引用和構(gòu)造方法引用。
先準(zhǔn)備一個(gè)POJO:
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private String gender;
private int age;
// 靜態(tài)方法引用
public static int compareByAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
再準(zhǔn)備一些數(shù)據(jù):
List list = new ArrayList();
list.add(new Person("shuke", "男", 29));
list.add(new Person("tom", "男", 16));
list.add(new Person("jerry", "男", 20));
list.add(new Person("beita", "女", 30));
1. 靜態(tài)方法引用
匿名內(nèi)部類(lèi)實(shí)現(xiàn)排序:
Collections.sort(list, new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
lambda表達(dá)式實(shí)現(xiàn)排序
Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());
方法引用實(shí)現(xiàn)排序
// 使用::操作符引用Person類(lèi)的靜態(tài)方法compareByAge()
Collections.sort(list, Person::compareByAge);
2. 實(shí)例方法引用
添加一個(gè)類(lèi),準(zhǔn)備實(shí)例方法:
class PersonUtil {
// 實(shí)例方法引用
public int comprareByName(Person p1, Person p2) {
return p1.getName().hashCode() - p2.getName().hashCode();
}
}
實(shí)例方法引用:
PersonUtil pu = new PersonUtil();
Collections.sort(list, pu::comprareByName);
3. 構(gòu)造方法引用
準(zhǔn)備一個(gè)函數(shù)式接口:
interface IPerson {
Person getPerson(String name, String gender, int age);
}
使用方式如下:
/*
// 匿名內(nèi)部類(lèi)方式
IPerson p1 = new IPerson() {
@Override
public Person getPerson(String name, String gender, int age) {
return new Person(name, gender, age);
}
}
// lambda表達(dá)式方式
IPerson p1 = (name, gender, age) -> new Person(name, gender, age);
*/
// 綁定構(gòu)造方法,實(shí)際調(diào)用的構(gòu)造方法是 Person(String, String, int)
IPerson p1 = Person::new;
// 調(diào)用接口方法
Person person = p1.getPerson("tom", "男", 18);
4.2 Stream概述
首先準(zhǔn)備數(shù)據(jù):
List list = new ArrayList();
list.add("tom");
list.add("jerry");
list.add("shuke");
list.add("beita");
list.add("damu");
現(xiàn)在有這樣的處理要求:找出升序大于5的有效賬號(hào)
第一種方式:增強(qiáng)for遍歷
List lista = new ArrayList();
for (String s : list) {
if (s.length() > 3) {
lista.add(s);
}
}
System.out.println(lista);
第二種方式:Iterator遍歷
List listb = new ArrayList<>();
Iterator it = list.iterator();
while(it.hasNext()) {
String s = it.next();
if(s.length() > 3) {
listb.add(s);
}
}
System.out.println(listb);
第三種方式:使用 stream 實(shí)現(xiàn)
List listc = list.stream().filter(s->s.length()>3).collect(Collectors.toList());
System.out.println(listc);
4.3 Stream常見(jiàn)操作API介紹
4.3.1 聚合操作
4.3.2 stream的處理流程
數(shù)據(jù)源
數(shù)據(jù)轉(zhuǎn)換
獲取結(jié)果
4.3.3 獲取Stream對(duì)象
從集合或者數(shù)組中獲取[**]
Collection.stream(),如accounts.stream()
Collection.parallelStream()
Arrays.stream(T t)
BufferReader
BufferReader.lines()-> stream()
靜態(tài)工廠
java.util.stream.IntStream.range()..
java.nio.file.Files.walk()..
自定構(gòu)建
java.util.Spliterator
更多的方式..
Random.ints()
Pattern.splitAsStream()..
4.3.4 中間操作API{intermediate}
操作結(jié)果是一個(gè)Stream,中間操作可以有一個(gè)或者多個(gè)連續(xù)的中間操作,需要注意的是,中間操作只記錄操作方式,不做具體執(zhí)行,直到結(jié)束操作發(fā)生時(shí),才做數(shù)據(jù)的最終執(zhí)行。
中間操作:就是業(yè)務(wù)邏輯處理。
中間操作過(guò)程:
無(wú)狀態(tài):數(shù)據(jù)處理時(shí),不受前置中間操作的影響,如:map/filter/peek/parallel/sequential/unordered
有狀態(tài):數(shù)據(jù)處理時(shí),受到前置中間操作的影響,如:distinct/sorted/limit/skip
4.3.5 終結(jié)操作|結(jié)束操作{Terminal}
需要注意的是,一個(gè)Stream對(duì)象,只能有一個(gè)Terminal操作,這個(gè)操作一旦發(fā)生,就會(huì)真實(shí)處理數(shù)據(jù),生成對(duì)應(yīng)的處理結(jié)果。
終結(jié)操作又可區(qū)分為非短路操作和短路操作,
非短路操作:當(dāng)前的Stream對(duì)象必須處理完集合中所有 數(shù)據(jù),才能得到處理結(jié)果,如:forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
短路操作:當(dāng)前的Stream對(duì)象在處理過(guò)程中,一旦滿足某個(gè)條件,就可以得到結(jié)果,如:anyMatch/allMatch/noneMatch/findFirst/findAny等,Short-circuiting,無(wú)限大的Stream-> 有限大的Stream。
4.4 Stream操作集合數(shù)中的數(shù)據(jù)-上
4.4.1 獲取stream
多個(gè)數(shù)據(jù)
Stream stream = Stream.of("admin", "tom", "damu");
數(shù)組
String [] strArrays = new String[] {"xueqi", "biyao"};
Stream stream2 = Arrays.stream(strArrays);
列表
List list = new ArrayList<>();
list.add("少林");
list.add("武當(dāng)");
list.add("青城");
list.add("崆峒");
list.add("峨眉");
Stream stream3 = list.stream();
集合
Set set = new HashSet<>();
set.add("少林羅漢拳");
set.add("武當(dāng)長(zhǎng)拳");
set.add("青城劍法");
Stream stream4 = set.stream();
Map
Map map = new HashMap<>();
map.put("tom", 1000);
map.put("jerry", 1200);
map.put("shuke", 1000);
Stream stream5 = map.entrySet().stream();
4.4.2 Stream對(duì)象對(duì)于基本數(shù)據(jù)類(lèi)型的功能封裝
// int / long / double
IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println);
// range方法:[1, 5),左閉右開(kāi)
IntStream.range(1, 5).forEach(System.out::println);
// rangeClosed:[1, 5],左半右閉
IntStream.rangeClosed(1, 5).forEach(System.out::println);
4.4.3 Stream對(duì)象 --> 轉(zhuǎn)換得到指定的數(shù)據(jù)類(lèi)型
// 數(shù)組
Object [] objx = stream.toArray(String[]::new);
// 字符串
String str = stream.collect(Collectors.joining()).toString();
// 列表
List listx = (List) stream.collect(Collectors.toList());
// 集合
Set setx = (Set) stream.collect(Collectors.toSet());
// Map
Map mapx = (Map) stream.collect(Collectors.toMap(x->x, y->"value:"+y));
4.5 Stream操作集合數(shù)中的數(shù)據(jù)-下
1. Stream中間操作
List accountList = new ArrayList<>();
accountList.add("xongjiang");
accountList.add("lujunyi");
accountList.add("wuyong");
accountList.add("linchong");
accountList.add("luzhishen");
accountList.add("likui");
accountList.add("wusong");
// map() 中間操作,map()方法接收一個(gè)Functional接口
accountList = accountList.stream().map(x->"梁山好漢:" + x).collect(Collectors.toList());
// filter() 添加過(guò)濾條件,過(guò)濾符合條件的用戶
accountList = accountList.stream().filter(x-> x.length() > 5).collect(Collectors.toList());
// forEach 增強(qiáng)型循環(huán)
accountList.forEach(x-> System.out.println("forEach->" + x));
// peek() 中間操作,迭代數(shù)據(jù)完成數(shù)據(jù)的依次處理過(guò)程
accountList.stream()
.peek(x -> System.out.println("peek 1: " + x))
.peek(x -> System.out.println("peek 2:" + x))
.forEach(System.out::println);
2. Stream中對(duì)于數(shù)字運(yùn)算的支持
List intList = new ArrayList<>();
intList.add(20);
intList.add(19);
intList.add(7);
intList.add(8);
intList.add(86);
intList.add(11);
intList.add(3);
intList.add(20);
// skip() 中間操作,有狀態(tài),跳過(guò)部分?jǐn)?shù)據(jù)
intList.stream().skip(3).forEach(System.out::println);
// limit() 中間操作,有狀態(tài),限制輸出數(shù)據(jù)量
intList.stream().skip(3).limit(2).forEach(System.out::println);
// distinct() 中間操作,有狀態(tài),剔除重復(fù)的數(shù)據(jù)
intList.stream().distinct().forEach(System.out::println);
// sorted() 中間操作,有狀態(tài),排序
// max() 獲取最大值
Optional optional = intList.stream().max((x, y)-> x-y);
System.out.println(optional.get());
// min() 獲取最小值
// reduce() 合并處理數(shù)據(jù)
Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);
System.out.println(optional2.get());
5. Lambda表達(dá)式在實(shí)際生產(chǎn)中的應(yīng)用
5.1 Lambda表達(dá)式重構(gòu)項(xiàng)目
可以使用Lambda表達(dá)式簡(jiǎn)化項(xiàng)目中的代碼。
5.2 Lambda表達(dá)式和Stream性能問(wèn)題
我們主要從兩個(gè)方面進(jìn)行性能比較:基本數(shù)據(jù)類(lèi)型與復(fù)雜數(shù)據(jù)類(lèi)型。
5.2.1 基本數(shù)據(jù)類(lèi)型的性能比較
package java8;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
/**
* {這里添加描述}
*
* @author funcy
* @date 2020-01-18 8:57 下午
*/
public class Test02 {
public static void main(String[] args) {
Random random = new Random();
// 1. 基本數(shù)據(jù)類(lèi)型:整數(shù)
List integerList = new ArrayList();
for (int i = 0; i < 1000000; i++) {
integerList.add(random.nextInt(Integer.MAX_VALUE));
}
// 1) stream
testStream(integerList);
// 2) parallelStream
testParallelStream(integerList);
// 3) 普通for
testForLoop(integerList);
// 4) 增強(qiáng)型for
testStrongForLoop(integerList);
// 5) 迭代器
testIterator(integerList);
}
public static void testStream(List list) {
long start = System.currentTimeMillis();
Optional optional = list.stream().max(Integer::compare);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testStream:" + (end - start) + "ms");
}
public static void testParallelStream(List list) {
long start = System.currentTimeMillis();
Optional optional = list.parallelStream().max(Integer::compare);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testParallelStream:" + (end - start) + "ms");
}
public static void testForLoop(List list) {
long start = System.currentTimeMillis();
int max = Integer.MIN_VALUE;
for (int i = 0; i < list.size(); i++) {
int current = list.get(i);
if (current > max) {
max = current;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testForLoop:" + (end - start) + "ms");
}
public static void testStrongForLoop(List list) {
long start = System.currentTimeMillis();
int max = Integer.MIN_VALUE;
for (Integer integer : list) {
if (integer > max) {
max = integer;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testStrongForLoop:" + (end - start) + "ms");
}
public static void testIterator(List list) {
long start = System.currentTimeMillis();
Iterator it = list.iterator();
int max = it.next();
while (it.hasNext()) {
int current = it.next();
if (current > max) {
max = current;
}
}
System.out.println(max);
long end = System.currentTimeMillis();
System.out.println("testIterator:" + (end - start) + "ms");
}
}
運(yùn)行結(jié)果如下:
2147480897
testStream:88ms
2147480897
testParallelStream:28ms
2147480897
testForLoop:9ms
2147480897
testStrongForLoop:11ms
2147480897
testIterator:15ms
5.2.2 復(fù)雜數(shù)據(jù)類(lèi)型的性能
package java8;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
/**
* {這里添加描述}
*
* @author funcy
* @date 2020-01-18 9:11 下午
*/
public class Test03 {
public static void main(String[] args) {
Random random = new Random();
List productList = new ArrayList<>();
for(int i = 0; i < 1000000; i++) {
productList.add(new Product("pro" + i, i, random.nextInt(Integer.MAX_VALUE)));
}
// 調(diào)用執(zhí)行
testProductStream(productList);
testProductParallelStream(productList);
testProductForloop(productList);
testProductStrongForloop(productList);
testProductIterator(productList);
}
public static void testProductStream(List list) {
long start = System.currentTimeMillis();
Optional optional = list.stream().max((p1, p2)-> p1.hot - p2.hot);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testProductStream:" + (end - start) + "ms");
}
public static void testProductParallelStream(List list) {
long start = System.currentTimeMillis();
Optional optional = list.stream().max((p1, p2)-> p1.hot - p2.hot);
System.out.println(optional.get());
long end = System.currentTimeMillis();
System.out.println("testProductParallelStream:" + (end - start) + "ms");
}
public static void testProductForloop(List list) {
long start = System.currentTimeMillis();
Product maxHot = list.get(0);
for(int i = 0; i < list.size(); i++) {
Product current = list.get(i);
if (current.hot > maxHot.hot) {
maxHot = current;
}
}
System.out.println(maxHot);
long end = System.currentTimeMillis();
System.out.println("testProductForloop:" + (end - start) + "ms");
}
public static void testProductStrongForloop(List list) {
long start = System.currentTimeMillis();
Product maxHot = list.get(0);
for (Product product : list) {
if(product.hot > maxHot.hot) {
maxHot = product;
}
}
System.out.println(maxHot);
long end = System.currentTimeMillis();
System.out.println("testProductStrongForloop:" + (end - start) + "ms");
}
public static void testProductIterator(List list) {
long start = System.currentTimeMillis();
Iterator it = list.iterator();
Product maxHot = it.next();
while(it.hasNext()) {
Product current = it.next();
if (current.hot > maxHot.hot) {
maxHot = current;
}
}
System.out.println(maxHot);
long end = System.currentTimeMillis();
System.out.println("testProductIterator:" + (end - start) + "ms");
}
}
class Product {
String name; // 名稱
Integer stock; // 庫(kù)存
Integer hot; // 熱度
public Product(String name, Integer stock, Integer hot) {
this.name = name;
this.stock = stock;
this.hot = hot;
}
}
運(yùn)行結(jié)果:
java8.Product@5f184fc6
testProductStream:63ms
java8.Product@5f184fc6
testProductParallelStream:15ms
java8.Product@5f184fc6
testProductForloop:16ms
java8.Product@5f184fc6
testProductStrongForloop:16ms
java8.Product@5f184fc6
testProductIterator:17ms
5.2.3 結(jié)論
jvm相關(guān)人員也對(duì)stream進(jìn)行了一系列測(cè),結(jié)果如下:
可以看到,隨著核心數(shù)增加,并行Stream帶來(lái)的性能提升是非常明顯的。
最終,我們可以得到這樣一個(gè)結(jié)論:對(duì)于簡(jiǎn)單數(shù)據(jù)的迭代處理,可以直接通過(guò)外部迭代進(jìn)行操作,如果在性能上有一定的要求,可以使用并行stream進(jìn)行操作;對(duì)于復(fù)雜對(duì)象的處理操作,stream的串行操作在性能上已經(jīng)和普通的迭代相差無(wú)幾,甚至超過(guò)了普通的迭代方式,完全可以用簡(jiǎn)潔的stream的語(yǔ)法來(lái)替換普通的迭代操作,如果在性能上有要求,可以直接選擇并行stream操作以提升性能,并行stream在多核條件下,更能發(fā)揮其性能優(yōu)勢(shì)。
5.3 線程安全問(wèn)題
這一節(jié)我們來(lái)看看并行stream(parallelStream)的線程安全:
package java8;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* {這里添加描述}
*
* @author funcy
* @date 2020-01-18 9:30 下午
*/
public class Test04 {
public static void main(String[] args) {
// 整數(shù)列表
List lists = new ArrayList();
// 增加數(shù)據(jù)
for (int i = 0; i < 1000; i++){
lists.add(i);
}
// 串行Stream
List list2 = new ArrayList<>();
lists.stream().forEach(x->list2.add(x));
System.out.println(lists.size());
System.out.println(list2.size());
// 并行Stream
List list3 = new ArrayList<>();
lists.parallelStream().forEach(x-> list3.add(x));
System.out.println(list3.size());
// stream的collect操作
List list4 = lists.parallelStream().collect(Collectors.toList());
System.out.println(list4.size());
}
}
運(yùn)行結(jié)果如下:
1000
1000
994
1000
可以看到,lists.parallelStream().forEach(x-> list3.add(x)) 會(huì)引發(fā)線程安全問(wèn)題,而lists.parallelStream().collect(Collectors.toList())不會(huì)引起線程安全問(wèn)題。
關(guān)于stream的collect操作,官方文檔有云:當(dāng)并行 執(zhí)行時(shí),可以實(shí)例化,填充和合并多個(gè)中間結(jié)果,以便保持可變結(jié)構(gòu)的隔離。因此,即使與非線程安全的數(shù)據(jù)結(jié)構(gòu)(例如ArrayList)并行執(zhí)行,并行還原也不需要額外的同步。
結(jié)論:并行stream的線程安全問(wèn)題,在業(yè)務(wù)處理的過(guò)程中,主要通過(guò)自定義編碼添加線程鎖的方式,或者使用stream api中提供的線程安全的終端操作來(lái)完成執(zhí)行過(guò)程。不過(guò),在更多的場(chǎng)景中,如果我們遇到多線程問(wèn)題,會(huì)直接使用線程安全的集合來(lái)規(guī)范數(shù)據(jù)源。
總結(jié)
以上是生活随笔為你收集整理的java8 lambda python_【学习笔记】java8 Lambda表达式语法及应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 通风帽采取防止凝结水滴落的措施有哪些
- 下一篇: 湖南省株洲市攸县新市镇大桥头村湘东组00