日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

java8 lambda python_【学习笔记】java8 Lambda表达式语法及应用

發布時間:2024/10/12 python 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java8 lambda python_【学习笔记】java8 Lambda表达式语法及应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文是慕課網大牧莫邪老師的視頻教程一課掌握Lambda表達式語法及應用的學習筆記。如果覺得內容對你有用,可以購買老師的課程支持一下,課程價格1元,十分良心了。

1. 課程介紹

2. 為什么引入Lambda表達式

2.1 什么是Lambda表達式

Lambda表達式也稱箭頭函數、匿名函數、閉包

Lambda表達式體現的是輕量級函數式編程思想

-> 符號是Lambda表達式核心操作符號,符號左側是操作參數,符號右側是操作表達式

Lambda表達式是 jdk1.8 提供的新特性

2.2 Model Code as Data(MCAD模式)

Model Code as Data,編碼即數據,盡可能輕量級的將代碼封裝為數據

解決方案:接口&實現類(匿名內部類)

存在問題:語法冗余、thIs關鍵字、變量捕獲、數據控制等

傳統模式下,新線程的創建:

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("threading ..." + Thread.currentThread().getName());

}

}).start();

使用jdk8新特性,lambda表達式優化線程模型

new Thread(() -> {

System.out.println("threading ..." + Thread.currentThread().getName());

}).start();

2.3 項目問題:功能接口的設計及優化

需求環境:線程類的創建

解決方案:匿名內部類的實現

解決方法:lambda表達式實現

2.4 為什么要使用lambda表達式

它不是解決未知問題的新技術

對現有解決方案的語義化優化

需要根據實際需求考慮性能問題

3. Lambda表達式的基礎知識

3.1 函數式接口概述和定義

函數式接口,就是java類型系統中的接口

函數式接口,是只包含一個接口方法的特殊接口

語義化檢測注解:@FunctionalInterface

可以像定義普通接口一樣定義函數式接口,并且接口內只有一個抽象方法:

@FunctionalInterface

public interface IUserCredential {

/**

* 通過用戶賬號,驗證用戶身份信息的接口

* @param username 要驗證的用戶賬號

* @return 返回身份信息[系統管理員、用戶管理員、普通用戶]

*/

String verifyUser(String username);

// 添加這個方法后,會報錯

// boolean test();

由于接口添加了@FunctionalInterface注解,表明是一個函數式接口,內部只能有一個抽象方法,如果再添加一個抽象方法boolean test(),就會報錯:Multiple non-overriding abstract methods found in interface com.imooc.IUserCredential

注意:@FunctionalInterface 注解也可以不加,函數式接口只需要滿足以下兩個條件即可:

定義一個接口

接口中只有一個抽象方法

3.2 默認方法和靜態方法

1. 接口的默認方法:default關鍵字修飾

@FunctionalInterface

public interface IUserCredential {

/**

* 通過用戶賬號,驗證用戶身份信息的接口

* @param username 要驗證的用戶賬號

* @return 返回身份信息[系統管理員、用戶管理員、普通用戶]

*/

String verifyUser(String username);

/**

* 接口的默認方法

*/

default String getCredential(String username) {

// 模擬方法

if ("admin".equals(username)) {

return "admin + 系統管理員用戶";

} else if("manager".equals(username)){

return "manager + 用戶管理員用戶";

} else {

return "commons + 普通會員用戶";

}

}

}

接口的默認方法使用default關鍵字修飾,調用時,需要用接口的實例調用:

IUserCredential instance = new xxx(); // 實例化接口中的實現類

instance.getCredential("admin");

2. 接口的靜態方法:static關鍵字修飾

@FunctionalInterface

public interface IUserCredential {

/**

* 通過用戶賬號,驗證用戶身份信息的接口

* @param username 要驗證的用戶賬號

* @return 返回身份信息[系統管理員、用戶管理員、普通用戶]

*/

String verifyUser(String username);

/**

* 接口的靜態方法

*/

static String getCredential(String username) {

// 模擬方法

if ("admin".equals(username)) {

return "admin + 系統管理員用戶";

} else if("manager".equals(username)){

return "manager + 用戶管理員用戶";

} else {

return "commons + 普通會員用戶";

}

}

}

同普通類的靜態方法一樣,接口的靜態方法在調用時,直接調用即可,不需要實例化接口實例:

// 直接使用 接口名.方法名 調用

IUserCredential.getCredential("admin");

3. 來自Object繼承的方法

由于接口或類都是Object的子類,如果我們在接口中增加一個由Object類繼承過來的抽象方法,接口依然不會報錯:

@FunctionalInterface

public interface IUserCredential {

/**

* 通過用戶賬號,驗證用戶身份信息的接口

* @param username 要驗證的用戶賬號

* @return 返回身份信息[系統管理員、用戶管理員、普通用戶]

*/

String verifyUser(String username);

/**

* 這里的toString()方法是Object類繼承的,添加后并不會報錯

* @return

*/

@Override

String toString();

}

以上接口雖然有兩個抽象類,但由于toString()方法是從Object類繼承的,因此并不會報錯,該接口依然是一個函數式接口。

3.3 Lambda表達式和函數式接口的關系

在jdk1.8之前,我們使用匿名內部類,實現接口的抽象方法:

IUserCredential ic2 = new IUserCredential() {

@Override

public String verifyUser(String username) {

return "admin".equals(username)?"管理員":"會員";

}

};

在jdk1.8,使用lambda表達式,針對函數式接口的簡單實現

IUserCredential ic3 = (String username) -> {

return "admin".equals(username) ? "lbd管理員" : "lbd會員";

};

lambda表達式 是 函數式接口的一種實現.

3.4 jdk中常見的函數式接口

在java.util.function提供了大量的函數式接口:

Predicate 接收參數T對象,返回一個boolean類型結果

@FunctionalInterface

public interface Predicate {

boolean test(T t);

// 省略靜態方法和默認方法

}

Consumer 接收參數T對象,沒有返回值

@FunctionalInterface

public interface Consumer {

void accept(T t);

// 省略靜態方法和默認方法

}

Function 接收參數T對象,返回R對象

@FunctionalInterface

public interface Function {

R apply(T t);

// 省略靜態方法和默認方法

}

Supplier 不接受任何參數,直接通過get()獲取指定類型的對象

@FunctionalInterface

public interface Supplier {

T get();

}

UnaryOperator 接口參數T對象,執行業務處理后,返回更新后的T對象

@FunctionalInterface

public interface UnaryOperator extends Function {

static UnaryOperator identity() {

return t -> t;

}

}

BinaryOperator 接口接收兩個T對象,執行業務處理后,返回一個T對象

public interface BiFunction {

R apply(T t, U u);

// 省略靜態方法和默認方法

}

@FunctionalInterface

public interface BinaryOperator extends BiFunction {

// 省略靜態方法和默認方法

}

示例:

// 1. Predicate 接收參數T對象,返回一個boolean類型結果

Predicate pre = (String username) -> {

return "admin".equals(username);

};

System.out.println(pre.test("manager"));

// 2. Consumer 接收參數T對象,沒有返回值

Consumer con = (String message) -> {

System.out.println("要發送的消息:" + message);

System.out.println("消息發送完成");

};

con.accept("hello 慕課網的學員們..");

// 3. Function 接收參數T對象,返回R對象

Function fun = (String gender) -> {

return "male".equals(gender) ? 1 : 0;

};

System.out.println(fun.apply("male"));

// 4. Supplier 不接受任何參數,直接通過get()獲取指定類型的對象

Supplier sup = () -> {

return UUID.randomUUID().toString();

};

System.out.println(sup.get());

// 5. UnaryOperator 接口參數T對象,執行業務處理后,返回更新后的T對象

UnaryOperator uo = (String img)-> {

img += "[100x200]";

return img;

};

System.out.println(uo.apply("原圖--"));

// 6. BinaryOperator 接口接收兩個T對象,執行業務處理后,返回一個T對象

BinaryOperator bo = (Integer i1, Integer i2) -> {

return i1 > i2? i1: i2;

};

System.out.println(bo.apply(12, 13));

3.5 Lambda表達式基本語法

聲明:就是和lambda表達式綁定的接口類型

參數:包含在一對圓括號中,和綁定的接口中的抽象方法中的參數個數及順序一致。

操作符:->

執行代碼塊:包含在一對大括號中,出現在操作符號的右側

示例如下:

首先定義3個接口:

// 1. 沒有參數,沒有返回值的lambda表達式綁定的接口

interface ILambda1{

void test();

}

// 2. 帶有參數,沒有返回值的lambda表達式

interface ILambda2{

void test(String name, int age);

}

// 3. 帶有參數,帶有返回值的lambda表達式

interface ILambda3 {

int test(int x, int y);

}

編寫Lambda表達式示例:

// 1. [接口聲明] = (參數) -> {執行代碼塊};

ILambda1 i1 = () -> {

System.out.println("hello imooc!");

System.out.println("welcome to imooc!");

};

i1.test();

// 2. lambda表達式的返回值,如果代碼塊只有一行,并且沒有大括號,不用寫return關鍵字,單行代碼的執行結果,會自動返回

ILambda1 i2 = () -> System.out.println("hello imooc");

i2.test();

// 3. 帶有多個參數的Lambda表達式

ILambda2 i21 = (String n, int a) -> {

System.out.println(n + "say: my year's old is " + a);

};

i21.test("jerry", 18);

// 4. 不寫參數類型,由jvm自動推導

ILambda2 i22 = (n, a) -> {

System.out.println(n + " 說:我今年" + a + "歲了.");

};

i22.test("tom", 22);

// 5. 帶有返回值的Lambda表達式

ILambda3 i3 = (x, y) -> {

int z = x + y;

return z;

};

System.out.println(i3.test(11, 22));

// 6. 只有一行時,可以省略大括號和return字段

ILambda3 i31 = (x, y) -> x + y;

System.out.println(i31.test(100, 200));

小結:

lambda表達式,必須和接口進行綁定。

lambda表達式的參數,可以附帶0個到n個參數,括號中的參數類型可以不用指定,jvm在運行時,會自動根據綁定的抽象方法中參數進行推導。

lambda表達式的返回值,如果代碼塊只有一行,并且沒有大括號,不用寫return關鍵字,單行代碼的執行結果,會自動返回。如果添加了大括號,或者有多行代碼,必須通過return關鍵字返回執行結果。

3.6 變量捕獲——變量的訪問操作

1. 匿名內部類型中對于變量的訪問

public void testInnerClass() {

String s2 = "局部變量";

new Thread(new Runnable() {

String s3 = "內部變量";

@Override

public void run() {

// 訪問全局變量

// System.out.println(this.s1); // 無法訪問s1,這里的this關鍵字表示是當前內部類型的對象

System.out.println(s1);

System.out.println(s2); // 局部變量的訪問

// s2 = "hello"; // 不能對局部變量進行數據的修改[final]

System.out.println(s3);

System.out.println(this.s3);

}

}).start();

}

在匿名內部類中,

this關鍵字表示的是當前內部類型的對象

局部變量的訪問時,不能對局部變量進行數據的修改(默認為final類型)

2. lambda表達式變量捕獲

public void testLambda() {

String s2 = "局部變量lambda";

new Thread(() -> {

String s3 = "內部變量lambda";

// 訪問全局變量

System.out.println(this.s1);// this關鍵字,表示的就是所屬方法所在類型的對象

// 訪問局部變量

System.out.println(s2);

// s2 = "hello";// 不能進行數據修改,默認推導變量的修飾符:final

System.out.println(s3);

s3 = "labmda 內部變量直接修改";

System.out.println(s3);

}).start();

}

在Lambda表達式中,

this關鍵字,表示的就是所屬方法所在類型的對象

修改局部時,同樣會報錯:默認推導變量的修飾符為final

3.7 Lambda表達式類型檢查

首先定義個函數式接口:

@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);

}

匿名內部類調用:

test(new MyInterface() {

@Override

public List strategy(String s, List list) {

list.add(s);

return list;

}

});

Lambda表達式調用:

test((x, y) -> {

y.add(x);

return y;

});

Lambda表達式方法推導:

(x,y)->{..} --> test(param) --> param==MyInterface --> lambda表達式-> MyInterface類型

這個就是對于lambda表達式的類型檢查,MyInterface接口就是lambda表達式的目標類型(target typing)

Lambda表達式方法參數推導:

(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表達式參數的類型檢查

3.8 方法重載和Lambda表達式

先定義兩個函數式接口:

interface Param1 {

void outInfo(String info);

}

interface Param2 {

void outInfo(String info);

}

再定義兩個重載方法:

// 定義重載的方法

public void lambdaMethod(Param1 param) {

param.outInfo("hello param1 imooc!");

}

public void lambdaMethod(Param2 param) {

param.outInfo("hello param2 imooc");

}

使用匿名內部類調用:

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);

}

});

這里能正常運行,但是在使用Lambda表達式時,會有問題:

app.lambdaMethod( (String info) -> {

System.out.println(info);

});

異常信息:

Ambiguous method call. Both

lambdaMethod(Param1) in Test?and

lambdaMethod(Param2) in Test?match

jvm對Lambda表達式調用的方法推導如下:

lambda表達式存在類型檢查-> 自動推導lambda表達式的目標類型

lambdaMethod() -> 方法 -> 重載方法

-> Param1 函數式接口

-> Param2 函數式接口

調用方法-> 傳遞Lambda表達式-> 自動推導->

-> Param1 | Param2

因此,在調用時,需要人為地告訴jvm我們要調用的方法參數是啥:

app.lambdaMethod((Param1) (String info) -> {

System.out.println(info);

});

3.9 深入理解lambda表達式

Lambda表達式在jvm謹慎解析在私有靜態方法和匿名內部類型,通過實現接口的匿名內部類型中接口方法調用靜態實現方法,完成Lambda表達式的執行。

先準備一個App.java類:

// 函數式接口

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);

}

可以看到,自動生成了私有靜態方法private static void lambda$main$0(java.lang.String),Lambda在實際運行時,也是生成了一個私有靜態方法:

private static void lambda$main$0(String message) {

System.out.println(message);

}

為了查看編譯過程,我們使用參數-Djdk.internal.lambda.dumpProxyClasses重新處理:

$ java -Djdk.internal.lambda.dumpProxyClasses App

lambda!

運行后,發現多生成了一個類:App$$Lambda$1.class,使用javap -p App$$Lambda$1查看類的信息:

$ 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)方法實際調用的是 lambda$main$0:

public void markUp(String message) {

App.lambda$main$0(message);

}

總結下lambda表達式的底層執行過程:

在編譯時,會自動生成私有靜態方法 private static void lambda$main$0(java.lang.String)

在編譯時,會自動生成實現類:final class App$$Lambda$1 implements IMarkUp

調用mu.markUp("lambda!"),實際上進行的操作是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. 實際調用: new App$$Lambda$1().markUp("lambda!");

}

// 1. 自動生成的私有靜態方法

/*

private static void lambda$main$0(String message) {

System.out.println(message);

}

*/

// 2. 自動生成的內部類

/*

final class App$$Lambda$1 implements IMarkUp {

private App$$Lambda$1() {

}

public void markUp(String message) {

App.lambda$main$0(message);

}

}

*/

}

4. Lambda表達式在集合中的運用

4.1 方法引用

方法引用是結合Lambda表達式的一種語法特性,主要分為靜態方法引用、實例方法引用和構造方法引用。

先準備一個POJO:

@Data

@AllArgsConstructor

@NoArgsConstructor

class Person {

private String name;

private String gender;

private int age;

// 靜態方法引用

public static int compareByAge(Person p1, Person p2) {

return p1.getAge() - p2.getAge();

}

}

再準備一些數據:

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. 靜態方法引用

匿名內部類實現排序:

Collections.sort(list, new Comparator() {

@Override

public int compare(Person o1, Person o2) {

return o1.getAge() - o2.getAge();

}

});

lambda表達式實現排序

Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());

方法引用實現排序

// 使用::操作符引用Person類的靜態方法compareByAge()

Collections.sort(list, Person::compareByAge);

2. 實例方法引用

添加一個類,準備實例方法:

class PersonUtil {

// 實例方法引用

public int comprareByName(Person p1, Person p2) {

return p1.getName().hashCode() - p2.getName().hashCode();

}

}

實例方法引用:

PersonUtil pu = new PersonUtil();

Collections.sort(list, pu::comprareByName);

3. 構造方法引用

準備一個函數式接口:

interface IPerson {

Person getPerson(String name, String gender, int age);

}

使用方式如下:

/*

// 匿名內部類方式

IPerson p1 = new IPerson() {

@Override

public Person getPerson(String name, String gender, int age) {

return new Person(name, gender, age);

}

}

// lambda表達式方式

IPerson p1 = (name, gender, age) -> new Person(name, gender, age);

*/

// 綁定構造方法,實際調用的構造方法是 Person(String, String, int)

IPerson p1 = Person::new;

// 調用接口方法

Person person = p1.getPerson("tom", "男", 18);

4.2 Stream概述

首先準備數據:

List list = new ArrayList();

list.add("tom");

list.add("jerry");

list.add("shuke");

list.add("beita");

list.add("damu");

現在有這樣的處理要求:找出升序大于5的有效賬號

第一種方式:增強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 實現

List listc = list.stream().filter(s->s.length()>3).collect(Collectors.toList());

System.out.println(listc);

4.3 Stream常見操作API介紹

4.3.1 聚合操作

4.3.2 stream的處理流程

數據源

數據轉換

獲取結果

4.3.3 獲取Stream對象

從集合或者數組中獲取[**]

Collection.stream(),如accounts.stream()

Collection.parallelStream()

Arrays.stream(T t)

BufferReader

BufferReader.lines()-> stream()

靜態工廠

java.util.stream.IntStream.range()..

java.nio.file.Files.walk()..

自定構建

java.util.Spliterator

更多的方式..

Random.ints()

Pattern.splitAsStream()..

4.3.4 中間操作API{intermediate}

操作結果是一個Stream,中間操作可以有一個或者多個連續的中間操作,需要注意的是,中間操作只記錄操作方式,不做具體執行,直到結束操作發生時,才做數據的最終執行。

中間操作:就是業務邏輯處理。

中間操作過程:

無狀態:數據處理時,不受前置中間操作的影響,如:map/filter/peek/parallel/sequential/unordered

有狀態:數據處理時,受到前置中間操作的影響,如:distinct/sorted/limit/skip

4.3.5 終結操作|結束操作{Terminal}

需要注意的是,一個Stream對象,只能有一個Terminal操作,這個操作一旦發生,就會真實處理數據,生成對應的處理結果。

終結操作又可區分為非短路操作和短路操作,

非短路操作:當前的Stream對象必須處理完集合中所有 數據,才能得到處理結果,如:forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator

短路操作:當前的Stream對象在處理過程中,一旦滿足某個條件,就可以得到結果,如:anyMatch/allMatch/noneMatch/findFirst/findAny等,Short-circuiting,無限大的Stream-> 有限大的Stream。

4.4 Stream操作集合數中的數據-上

4.4.1 獲取stream

多個數據

Stream stream = Stream.of("admin", "tom", "damu");

數組

String [] strArrays = new String[] {"xueqi", "biyao"};

Stream stream2 = Arrays.stream(strArrays);

列表

List list = new ArrayList<>();

list.add("少林");

list.add("武當");

list.add("青城");

list.add("崆峒");

list.add("峨眉");

Stream stream3 = list.stream();

集合

Set set = new HashSet<>();

set.add("少林羅漢拳");

set.add("武當長拳");

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對象對于基本數據類型的功能封裝

// int / long / double

IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println);

// range方法:[1, 5),左閉右開

IntStream.range(1, 5).forEach(System.out::println);

// rangeClosed:[1, 5],左半右閉

IntStream.rangeClosed(1, 5).forEach(System.out::println);

4.4.3 Stream對象 --> 轉換得到指定的數據類型

// 數組

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操作集合數中的數據-下

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()方法接收一個Functional接口

accountList = accountList.stream().map(x->"梁山好漢:" + x).collect(Collectors.toList());

// filter() 添加過濾條件,過濾符合條件的用戶

accountList = accountList.stream().filter(x-> x.length() > 5).collect(Collectors.toList());

// forEach 增強型循環

accountList.forEach(x-> System.out.println("forEach->" + x));

// peek() 中間操作,迭代數據完成數據的依次處理過程

accountList.stream()

.peek(x -> System.out.println("peek 1: " + x))

.peek(x -> System.out.println("peek 2:" + x))

.forEach(System.out::println);

2. Stream中對于數字運算的支持

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() 中間操作,有狀態,跳過部分數據

intList.stream().skip(3).forEach(System.out::println);

// limit() 中間操作,有狀態,限制輸出數據量

intList.stream().skip(3).limit(2).forEach(System.out::println);

// distinct() 中間操作,有狀態,剔除重復的數據

intList.stream().distinct().forEach(System.out::println);

// sorted() 中間操作,有狀態,排序

// max() 獲取最大值

Optional optional = intList.stream().max((x, y)-> x-y);

System.out.println(optional.get());

// min() 獲取最小值

// reduce() 合并處理數據

Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);

System.out.println(optional2.get());

5. Lambda表達式在實際生產中的應用

5.1 Lambda表達式重構項目

可以使用Lambda表達式簡化項目中的代碼。

5.2 Lambda表達式和Stream性能問題

我們主要從兩個方面進行性能比較:基本數據類型與復雜數據類型。

5.2.1 基本數據類型的性能比較

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. 基本數據類型:整數

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) 增強型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");

}

}

運行結果如下:

2147480897

testStream:88ms

2147480897

testParallelStream:28ms

2147480897

testForLoop:9ms

2147480897

testStrongForLoop:11ms

2147480897

testIterator:15ms

5.2.2 復雜數據類型的性能

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)));

}

// 調用執行

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; // 庫存

Integer hot; // 熱度

public Product(String name, Integer stock, Integer hot) {

this.name = name;

this.stock = stock;

this.hot = hot;

}

}

運行結果:

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 結論

jvm相關人員也對stream進行了一系列測,結果如下:

可以看到,隨著核心數增加,并行Stream帶來的性能提升是非常明顯的。

最終,我們可以得到這樣一個結論:對于簡單數據的迭代處理,可以直接通過外部迭代進行操作,如果在性能上有一定的要求,可以使用并行stream進行操作;對于復雜對象的處理操作,stream的串行操作在性能上已經和普通的迭代相差無幾,甚至超過了普通的迭代方式,完全可以用簡潔的stream的語法來替換普通的迭代操作,如果在性能上有要求,可以直接選擇并行stream操作以提升性能,并行stream在多核條件下,更能發揮其性能優勢。

5.3 線程安全問題

這一節我們來看看并行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) {

// 整數列表

List lists = new ArrayList();

// 增加數據

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());

}

}

運行結果如下:

1000

1000

994

1000

可以看到,lists.parallelStream().forEach(x-> list3.add(x)) 會引發線程安全問題,而lists.parallelStream().collect(Collectors.toList())不會引起線程安全問題。

關于stream的collect操作,官方文檔有云:當并行 執行時,可以實例化,填充和合并多個中間結果,以便保持可變結構的隔離。因此,即使與非線程安全的數據結構(例如ArrayList)并行執行,并行還原也不需要額外的同步。

結論:并行stream的線程安全問題,在業務處理的過程中,主要通過自定義編碼添加線程鎖的方式,或者使用stream api中提供的線程安全的終端操作來完成執行過程。不過,在更多的場景中,如果我們遇到多線程問題,會直接使用線程安全的集合來規范數據源。

總結

以上是生活随笔為你收集整理的java8 lambda python_【学习笔记】java8 Lambda表达式语法及应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。