十七、JDK8 新特性
17.1 Lambda運算式介紹
17.1.1 Lambda運算式案例
先來看案例:
package com.itheima.lambda;
/**
* @author: Carl Zhang
* @create: 2021-12-31 14:57
* 體驗Lambda運算式和傳統運算式區別
* 實作游泳
*/
public class Lambda01 {
public static void main(String[] args) {
/*
* 結論:1. 傳統的匿名內部類方法解決介面引數問題,需要 創建匿名內部類物件,實作介面方法,兩步 --關注點 怎么做
* 2. Lambda運算式只需要一條式子,代碼簡介,關注點更明確在方法功能和輸出 -- 關注點 做什么
* 3. 這種關注方法能做什么的思想就是函式式編程思想
* */
//傳統方法實作游泳 - 匿名內部類
swim(new Swim() {
@Override
public void swimming() {
System.out.println("匿名內部類的游泳....");
}
});
//Lambda運算式實作
swim(() -> System.out.println("匿名內部類的游泳...."));
}
public static void swim (Swim swim) {
swim.swimming();
}
}
interface Swim {
/**
* 游泳
* */
void swimming();
}
注意:lambda 運算式可以理解為對匿名內部類的一種簡化 , 但是本質是有區別的
17.1.2 引入函式式編程思想
介紹:
- 在數學中,函式就是有輸入量、輸出量的一套計算方案,也就是“拿資料做操作”
- 而
lambda是就是函式式編程思想的一種體現
17.1.3 函式式編程思想和面向物件編程思想的對比
- 面向物件思想 :
- 強調的是用物件去完成某些功能 -- 怎么做
- 函式式編程思想 :
- 強調的是結果 , 而不是怎么去做 -- 做什么
17.2 函式式介面
17.2.1 函式式介面介紹
-
概念:
- 只有一個抽象方法需要重寫的介面就是函式式介面,
- 函式式介面是允許有其他的非抽象方法的存在例如靜態方法,默認方法,私有方法,
-
注解: 為了標識介面是一個函式式介面,可以在介面之上加上一個注解:
@FunctionalInterface -
相關API :
JDK中的java.util.function包里的介面都是函式式介面
我們以前學的 Runnable 介面也是函式式介面

也可以自定義一個函式式介面:
package com.itheima.lambda;
/**
* @author CarlZhang
* 自定義一個函式式介面
*/
@SuppressWarnings("ALL")
@FunctionalInterface
public interface FunctionalInterface01 {
/**
* 只有一個 要重寫的 抽象方法
*/
void method01();
/**
* 繼承Object類的方法
*/
@Override
String toString();
/**
* jdk1.8 介面里可以有靜態方法和默認方法
* */
static void method02() {
System.out.println("FunctionalInterface01介面里的靜態方法");
}
/**
* 默認方法
* */
default void method03() {
System.out.println("FunctionalInterface01介面里的默認方法");
}
}
@FunctionalInterface
interface FunctionalInterface02 extends FunctionalInterface01{
//報錯:因為此處有兩個要重寫的抽象法, 一個父類介面的一個此介面的,
//結論:函式式介面里只能有一個要重寫的抽象方法,父類介面里的也算,而Object類比較特殊,
// 介面里有重寫Object類的抽象方法不影響函式式介面的判定
//void method02();
}
17.2.2 注意事項和使用細節
- 介面里只能有一個要重寫的抽象方法,繼承自
**Object**類的方法除外 - 可以用注解
@FunctionalInterface來表示函式式介面
17.3 Lambda運算式的使用
17.3.1 Lambda運算式語法
標準格式 :(形參串列) - > { //要實作方法的方法體... }
17.3.2 Lambda運算式使用案例
/**
* @author: Carl Zhang
* @create: 2021-12-31 16:32
* 練習1:
* 1 撰寫一個介面(ShowHandler)
* 2 在該介面中存在一個抽象方法(show),該方法是無引數無回傳值
* 3 在測驗類(ShowHandlerDemo)中存在一個方法(useShowHandler)
* 方法的的引數是ShowHandler型別的,在方法內部呼叫了ShowHandler的show方法
*/
public class ShowHandlerDemo {
public static void useShowHandler(ShowHandler showHandler) {
showHandler.show();
}
public static void main(String[] args) {
//呼叫useShowHandler方法
//使用Lambda運算式實作ShowHandler介面作為引數
useShowHandler(() -> {
System.out.println("使用Lambda運算式實作ShowHandler介面作為引數");
});
}
/*
* Lambda運算式格式決議
* 1. () 表示實作的介面里方法的形參串列
* 2. -> 語法規定,指向要實作的方法內容
* 3. {} 要實作的方法的方法體
*
* 注意:Lambda運算式實作的介面必須是函式式介面
* */
}
/**
* @author CarlZhang
*/
@FunctionalInterface
public interface ShowHandler {
/**
* 在該介面中存在一個抽象方法(show),該方法是無引數無回傳值
* */
void show();
}
/**
* @author: Carl Zhang
* @create: 2021-12-31 16:48
* 需求
* 1 首先存在一個介面(StringHandler)
* 2 在該介面中存在一個抽象方法(printMessage),該方法是有引數無回傳值
* 3 在測驗類(StringHandlerDemo)中存在一個方法(useStringHandler),
* 方法的的引數是StringHandler型別的,
* 在方法內部呼叫了StringHandler的printMessage方法
*/
public class StringHandlerDemo {
public static void useStringHandler(StringHandler stringHandler) {
stringHandler.printMessage("Hello, World");
}
public static void main(String[] args) {
//使用lambda運算式實作StringHandler, 作為引數傳遞
//結論:
// 1. () 里的內容對應介面里方法()的內容,是形式引數,lambda運算式看作一個介面實作類
// 2. 只有一個引數情況下,可以省略()
useStringHandler(String s -> {
System.out.println("呼叫Lambda運算式的代碼塊 " + s);
});
//匿名內部類的方式實作
useStringHandler(new StringHandler() {
@Override
public void printMessage(String s) {
System.out.println("匿名內部類的方法 " + s);
}
});
}
}
@FunctionalInterface
public interface StringHandler {
/**
* 在該介面中存在一個抽象方法(printMessage),該方法是有引數無回傳值
* @param s 任意字串
*/
void printMessage(String s);
}
package com.heima.lambda;
import org.omg.CORBA.PUBLIC_MEMBER;
/**
* @author Carl Zhang
* @description
* @date 2022/1/1 20:32
* 1 首先存在一個介面(Calculator)
* 2 在該介面中存在一個抽象方法(calc),該方法是有引數也有回傳值
* 3 在測驗類(CalculatorDemo)中存在一個方法(useCalculator)
* 方法的的引數是Calculator型別的
* 在方法內部呼叫了Calculator的calc方法
*/
public class CalculatorDemo {
public static void useCalculator(Calculator calculator) {
System.out.println(calculator.calc(11, 12));
}
public static void main(String[] args) {
/*
* 1. 有參有回傳值的方法,直接寫(形參串列) -> { return 回傳值; },
* 進一步體現 (輸入) - > {輸出} 的函式式編程思想
* 2. 引數型別可以省略,有多個引數不能只省略一個
* 3. 代碼塊只有一句,則可以省略大括號和分號,甚至return
* */
//useCalculator((int num1, int num2) -> {
// return num1 + num2;
//});
useCalculator((num1, num2) -> num1 + num2);
}
}
/**
* @author CarlZhang
* 1 首先存在一個介面(Calculator)
* 2 在該介面中存在一個抽象方法(calc),該方法是有引數也有回傳值
*/
@FunctionalInterface
public interface Calculator {
/**
* 計算兩數之和
* @param num1 第一個數
* @param num2 第二個數
* @return 兩數之和
*/
int calc(int num1, int num2);
}
17.3.2 注意事項和使用細節
使用前提 :Lambda 運算式實作的介面必須是函式式介面
格式決議:
Lambda運算式可看做函式式介面的一個實作類物件()表示實作的介面里方法的形參串列,沒有可以空著,- 引數型別可以省略,有多個引數不能只省略一個
- 只有一個引數可以省略
()
->語法規定,指向要實作的方法內容{}要實作的方法的方法體- 代碼塊里只有一句,則可以省略
{}和;,甚至return
- 代碼塊里只有一句,則可以省略
(形參) -> {回傳值}的格式體現了(輸入) -> {輸出}的函式式編程思想
17.4 Lambda運算式和匿名內部類的區別
- 作用物件不同 :
- 匿名內部類:可以是介面,也可以是抽象類,還可以是具體類
Lambda運算式:只能是函式式介面
- 使用場景不同 :
- 如果介面中有且僅有一個抽象方法,可以使用
Lambda運算式,也可以使用匿名內部類 - 如果介面中多于一個抽象方法,只能使用匿名內部類,而不能使用
Lambda運算式
- 如果介面中有且僅有一個抽象方法,可以使用
- 實作原理不同 :
- 匿名內部類:編譯之后,產生一個單獨的
.class位元組碼檔案 Lambda運算式:編譯之后,沒有一個單獨的.class位元組碼檔案,對應的位元組碼會在運行的時候動態生成
- 匿名內部類:編譯之后,產生一個單獨的
/**
* @author Carl Zhang
* @description Lambda運算式和匿名內部類的區別
* @date 2022/1/1 21:36
*/
public class LambdaVsAnonymous {
public static void main(String[] args) {
//Lambda運算式呼叫show方法 -- 編譯后沒有.class檔案
Test test = () -> System.out.println("Hello, World");
test.show();
//匿名內部類呼叫show方法 -- 有LambdaVsAnonymous$1.class檔案
new Test() {
@Override
public void show() {
System.out.println("Hello, World");
}
}.show();
}
}
@FunctionalInterface
interface Test {
/**
* 列印方法
*/
void show();
}
17.5 Stream 流
17.5.1 Stream的體驗
import java.util.ArrayList;
/**
* @author Carl Zhang
* @description 體驗Stream流的好處
* @date 2022/1/2 17:28
* 需求:按照下面的要求完成集合的創建和遍歷
* <p>
* 1 創建一個集合,存盤多個字串元素
* "張無忌" , "張翠山" , "張三豐" , "謝廣坤" , "趙四" , "劉能" , "小沈陽" , "張良"
* 2 把集合中所有以"張"開頭的元素存盤到一個新的集合
* 3 把"張"開頭的集合中的長度為3的元素存盤到一個新的集合
* 4 遍歷上一步得到的集合
*/
public class Stream01 {
public static void main(String[] args) {
//集合的方式
//1.創建集合,添加資料
ArrayList<String> list = new ArrayList<>();
list.add("張無忌");
list.add("張翠山");
list.add("張三豐");
list.add("謝廣坤");
list.add("趙四");
list.add("劉能");
list.add("小沈陽");
list.add("張良");
ArrayList<String> newList = new ArrayList<>();
ArrayList<String> newList2 = new ArrayList<>();
//"張"開頭的元素添加到新集合
for (String s : list) {
if (s.startsWith("張")) {
//3."張"開頭的元素添加到新集合
newList.add(s);
}
}
//"張"開頭的且長度為3的添加到另一個元素
for (String s : newList) {
if (s.startsWith("張") && s.length() == 3) {
newList2.add(s);
}
}
//4.列印
System.out.println(newList);
System.out.println(newList2);
System.out.println("===================");
//Stream流的方式 獲取并列印"張"開頭的且長度為3的元素 -- 使對容器里資料的操作進行了簡化
list.stream().filter(s -> s.startsWith("張") && s.length()
== 3).forEach(s -> System.out.println(s));
}
}
17.5.2 Stream流介紹


17.6 Stream流三類方法
17.6.1 Stream流三類方法介紹
- 獲取
Stream流- 創建一條流水線,并把資料放到流水線上準備進行操作
- 中間方法
- 流水線上的操作,
- 一次操作完畢之后,還可以繼續進行其他操作
- 終結方法
- 一個
Stream流只能有一個終結方法 - 是流水線上的最后一個操作
- 一個
17.6.2 Stream流 - 獲取方法
- 單列集合
- 可以使用
Collection介面中的默認方法stream()生成流 default Stream<E> stream()
- 可以使用
- 雙列集合
- 雙列集合不能直接獲取 , 需要間接的生成流
- 可以先通過
keySet() 或者entrySet()獲取一個Set集合,再獲取Stream流
- 陣列
Arrays中的靜態方法stream生成流
import java.util.*;
import java.util.stream.Stream;
/**
* @author Carl Zhang
* @description Stream流的獲取方法
* @date 2022/1/2 17:59
*/
@SuppressWarnings("ALL")
public class StreamGetMethod {
public static void main(String[] args) {
//獲取單列集合的Stream流
singleSetStream();
//獲取雙列集合的Stream流
doubleSetStream();
//獲取陣列的Stream流
arrayStream();
//獲取任意元素的stream流 --了解
int[] array = {1, 2, 3, 4, 5, 6};
Stream.of(array).forEach(i -> System.out.println(i)); //[I@7ba4f24f
Stream.of(1, 2, 3, 4, 5, 6).forEach(i -> System.out.println(i));
}
private static void arrayStream() {
System.out.println("獲取陣列的Stream流");
int[] arr = {1, 2, 3, 4, 5, 6};
Arrays.stream(arr).forEach(i -> System.out.println(i));
}
private static void doubleSetStream() {
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("it001", "曹植");
hashMap.put("it002", "曹丕");
hashMap.put("it003", "曹熊");
hashMap.put("it004", "曹沖");
hashMap.put("it005", "曹昂");
// 雙列集合不能直接獲取 , 需要間接的生成流
// 可以先通過keySet或者entrySet獲取一個Set集合,再獲取Stream流
System.out.println("獲取雙列集合的Stream流");
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
entries.stream().forEach(entry -> System.out.println(entry.getKey() +
"-" + entry.getValue()));
}
private static void singleSetStream() {
ArrayList<String> list = new ArrayList<>();
list.add("迪麗熱巴");
list.add("古力娜扎");
list.add("馬爾扎哈");
list.add("歐陽娜娜");
// 可以使用Collection介面中的默認方法stream()生成流
// default Stream<E> stream()
System.out.println("獲取單列集合的Stream流");
list.stream().forEach((String s) -> {
System.out.println(s);
});
}
}
17.6.3 Stream流 - 中間方法
特點:回傳了 Stream 流物件,用以繼續呼叫方法進行操作流物件
Stream<T> filter(Predicate predicate):用于對流中的資料進行過濾Predicate介面中的方法 :boolean test(T t):對給定的引數進行判斷,回傳一個布林值
Stream<T> limit(long maxSize):截取指定引數個數的資料Stream<T> skip(long n):跳過指定引數個數的資料static <T> Stream<T> concat(Stream a, Stream b):合并a和b兩個流為一個流Stream<T> distinct():去除流中重復的元素,依賴(hashCode和equals方法)Stream<T> sorted (): 將流中元素按照自然排序的規則排序Stream<T> sorted (Comparator<? super T> comparator): 將流中元素按照自定義比較器規則排序
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* @author Carl Zhang
* @description Stream流的中間方法
* @date 2022/1/2 19:12
*/
@SuppressWarnings("ALL")
public class StreamCentreMethod {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張無忌");
list.add("張翠山");
list.add("張三豐");
list.add("謝廣坤");
list.add("趙四");
list.add("劉能");
list.add("小沈陽");
list.add("張良");
list.add("張良");
list.add("張良");
list.add(new String("張良"));
//1 Stream<T> filter(Predicate predicate):用于對流中的資料進行過濾
// Predicate函式式介面的方法 : boolean test(T t):對給定的引數進行判斷,回傳一個布林值
// T 是泛型,Stream流里的元素型別
// 回傳true就留下,false就過濾掉
//列印集合中三個字名字的元素
//list.stream().filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// return s.length() == 3;
// }
//}).forEach(s -> System.out.println(s));
list.stream().filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
//2 Stream<T> limit(long maxSize):截取指定引數個數的資料
//獲取前兩個元素
Stream<String> stream = list.stream();
stream.limit(2).forEach(s -> System.out.println(s));
//3 Stream<T> skip(long n):跳過指定引數個數的資料
//跳過前兩個元素,列印后面的元素
//例外IllegalStateException:stream has already been operated upon or closed
//stream.skip(2).forEach(s -> System.out.println(s));
list.stream().skip(2).forEach(s -> System.out.println(s));
//4 static <T> Stream<T> concat(Stream a, Stream b):合并a和b兩個流為一個流
ArrayList<String> list2 = new ArrayList<>();
list2.add("迪麗熱巴");
list2.add("古力娜扎");
list2.add("歐陽娜娜");
list2.add("馬爾扎哈");
Stream.concat(list.stream(), list2.stream()).forEach(s -> System.out.println(s));
//5 Stream<T> distinct():去除流中重復的元素,依賴(hashCode()和equals())
list.stream().distinct().forEach(s -> System.out.println(s));
//這里只剩一個"張良",因為String里的方法根據value的值來回傳hashCode
//new String("張良").hashCode();
//6 Stream<T> sorted () : 將流中元素按照自然排序的規則排序
list.stream().sorted().forEach(s -> System.out.println(s));
//7 Stream<T> sorted (Comparator<? super T> comparator) : 將流中元素按照自定義比較器規則排序
list.stream().sorted(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();
}
}).forEach(s -> System.out.println(s));
System.out.println();
list.stream().sorted((s1, s2) -> s2.length() - s1.length()).forEach(
s -> System.out.println(s)
);
}
}
17.6.4 Stream流 - 終結方法
特點:慷訓傳值,不能繼續呼叫方法進行操作流物件
void forEach(Consumer action):對此流的每個元素執行操作Consumer介面中的方法void accept(T t):對給定的引數執行此操作
long count():回傳此流中的元素數
import java.util.ArrayList;
import java.util.function.Consumer;
/**
* @author Carl Zhang
* @description Stream流的終結方法
* @date 2022/1/2 19:48
*/
public class StreamEndMethod {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("張無忌");
list.add("張翠山");
list.add("張三豐");
list.add("謝廣坤");
//1 void forEach(Consumer action):對此流的每個元素執行操作
// Consumer介面中的方法 void accept(T t):對給定的引數執行此操作
// 把list集合中的元素放在stream流中
// forEach方法會回圈遍歷流中的資料
// 并回圈呼叫accept方法 , 把資料傳給s
// 所以s就代表的是流中的每一個資料
// 我們只要在accept方法中對資料做業務邏輯處理即可
list.stream().forEach(s -> System.out.println(s));
//2. long count():回傳此流中的元素數
//結果:4
System.out.println(list.stream().count());
}
}
17.7 Stream流的收集方法
17.7.1 使用收集方法的原因
問題:使用 Stream 流的方式操作完畢之后,我想把流中的資料起來,該怎么辦呢?
引出收集方法
package com.heima.stream;
import java.util.ArrayList;
/**
* @author Carl Zhang
* @description 使用收集方法的原因
* @date 2022/1/2 20:04
* 需求:過濾元素并遍歷集合
* 定義一個集合,并添加一些整數1,2,3,4,5,6,7,8,9,10
* 將集合中的奇數洗掉,只保留偶數,
* 遍歷集合得到2,4,6,8,10
*/
public class CollectionMethod01 {
public static void main(String[] args) {
//JDK9 新特性 直接傳入一個不可變集合的元素,來創建新集合
//ArrayList<Integer> list = new ArrayList<>(List.of(1,2,3,4,5,6,7,8,9,10));
ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
//使用Stream流過濾掉集合中的奇數,獲取偶數
list.stream().filter(i -> i % 2 == 0).forEach(i -> System.out.println(i));
//結論:list集合里的數未改變
//如果需要保留流里面過濾后的元素 -> 使用收集方法
System.out.println(list);
}
}
17.7.2 收集方法介紹
Stream 流的收集方法
R collect(Collector collector) : 此方法只負責收集流中的資料 , 創建集合添加資料動作需要依賴于引數
收集方法也可以看作一種終結方法,呼叫完 collect() 不回傳 Stream 物件,不可再對流進行操作
17.7.2 三種收集方式
工具類 Collectors 提供了具體的收集方式
public static <T> Collector toList() :把元素收集到List集合中
public static <T> Collector toSet() :把元素收集到Set集合中
package com.heima.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
/**
* @author Carl Zhang
* @description Stream流的收集方法
* @date 2022/1/2 20:22
* 需求 :
* 定義一個集合,并添加一些整數1,2,3,4,5,6,7,8,9,10
* 將集合中的奇數洗掉,只保留偶數,
* 遍歷集合得到2,4,6,8,10
*/
public class CollectionMethod02 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
//通過收集方法獲取過濾后的元素
//決議:
//1. R collect(Collector collector) : 此方法只負責收集流中的資料 , 創建集合添加資料動作需要依賴于引數
//2. 通過工具類Collector里的規則來將收集到的元素保存到集合里
// public static <T> Collector toList():把元素收集到List集合中
List<Integer> list1 = list.stream().filter(i -> i % 2 == 0).collect(
Collectors.toList());
System.out.println(list1); //[2, 4, 6, 8, 10]
//將過濾好的元素收集到Set集合里
//public static <T> Collector toSet():把元素收集到Set集合中
Set<Integer> set = list.stream().filter(i -> i % 2 == 0).collect(
Collectors.toSet()
);
System.out.println(set); //[2, 4, 6, 8, 10]
}
}
public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Carl Zhang
* @description 將過濾好的元素收集到Map集合里
* @date 2022/1/2 20:33
* public static Collector toMap(Function keyMapper,Function valueMapper):
* 把元素收集到Map集合中
* <p>
* 1 創建一個ArrayList集合,并添加以下字串,字串中前面是姓名,后面是年齡
* "zhangsan,23"
* "lisi,24"
* "wangwu,25"
* 2 保留年齡大于等于24歲的人,并將結果收集到Map集合中,姓名為鍵,年齡為值
*/
public class CollectionMethod03 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("zhangsan,23");
list.add("lisi,24");
list.add("wangwu,25");
//篩選出24歲以上的元素
Stream<String> stream = list.stream().filter(s -> {
String[] split = s.split(",");
//獲取年齡
int age = Integer.parseInt(split[1]);
//篩選出大于等于24歲的
return age >= 24;
});
//將過濾好的元素收集到Map集合
//public static Collector toMap(Function keyMapper,Function valueMapper):
Map<String, String> collect = stream.collect(Collectors.toMap(
// 獲取鍵:Function keyMapper -- 傳入一個函式式介面的實作類
// s 表示流里的元素
// 獲取第一個元素,做為鍵
s -> s.split(",")[0],
// 獲取值Function valueMapper)
s -> s.split(",")[1]
));
//遍歷
Set<Map.Entry<String, String>> entries = collect.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey() + "-" + entry.getValue());
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/403538.html
標籤:其他
上一篇:InnoDB學習(七)之索引結構
