![](https://cdn.nlark.com/yuque/0/2023/jpeg/28753938/1691067189459-f51a48da-0da6-4e6e-aeee-75b39662cd20.jpeg) ## 一、Lambda表達式 > Lambda 是一個匿名函數,我們可以把 La ...
一、Lambda表達式
Lambda 是一個匿名函數,我們可以把 Lambda表達式理解為是一段可以傳遞的代碼(將代碼像數據一樣進行傳遞)。可以寫出更簡潔、更靈活的代碼。作為一種更緊湊的代碼風格,使Java的語言表達能力得到了提升
1、舉例
( o1 , o2 ) -> Integer.compare( o1 , o2 )
2、格式
- -> : lambda 操作符或箭頭操作符
- -> 左邊 :lambda 形參列表(其實就是抽象中的抽象方法的形參列表)
- -> 右邊 :lambda 體(其實就是重寫的抽象方法的方法體)
3、lambda 表達式的使用(6種情況)
Runnable r = () -> System.out.println("hello");
Consumer<String> consumer = (args) -> System.out.println(args);
Consumer<String> consumer2 = args -> System.out.println(args);
BinaryOperator<Long> bo = (Long x,Long y) -> {
System.out.println("實現函數介面方法!");
return x + y;
};
BinaryOperator<Long> bio = (Long x,Long y) -> x + y;
BinaryOperator<Long> bio = (x, y) -> x + y;
4、lambda 表達式的本質:作為介面的實例
二、函數式介面
1、什麼是函數式介面
- 只包含一個抽象方法的介面,稱為函數式介面。
- 你可以通過 Lambda 表達式來創建該介面的對象。(若 Lambda 表達式拋出一個受檢異常,那麼該異常需要在目標介面的抽象方法上進行聲明)。
- 我們可以在任意函數式介面上使用 @FunctionalInterface 註解,這樣做可以檢查它是否是一個函數式介面,同時 javadoc 也會包含一條聲明,說明這個介面是一個函數式介面。
2、自定義函數式介面
@FunctionalInterface
public interface MyInterface {
public String getValue();
}
@FunctionalInterface
public interface MyInterface<T> {
public T getValue(T t);
}
3、Lambda 表達式作為參數傳遞
註意:為了將 Lambda 表達式作為參數傳遞,接收Lambda 表達式的參數類型必須是與該 Lambda 表達式相容的函數式介面的類型。
4、Java 內置四大核心函數式介面
消費型介面Consumer void accept(T t)
@Test
public void testConsumer() {
// 消費型介面Consumer<T> void accept(T t)
buyCar(265000.00, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("買車花費了:" + aDouble);
}
});
System.out.println("使用lambda表達式如下");
buyCar(99999.99, money -> System.out.println("買車花費了:" + money));
}
/**
* 利用Consumer 實現消費
* @param money 金額
* @param con 所消費金額
*/
public void buyCar(Double money, Consumer<Double> con) {
con.accept(money);
}
斷定型介面Predicate boolean test(T t)
@Test
public void testPredicate() {
// 斷定型介面Predicate<T> boolean test(T t)
List<String> list = Arrays.asList("aabb", "bbcc", "ccdd", "aadd");
List<String> res = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("aa");
}
});
System.out.println("過濾後:" + res);
System.out.println("使用lambda表達式如下");
List<String> res2 = filterString(list, s -> s.contains("aa"));
System.out.println("過濾後:" + res2);
}
/**
* 利用Predicate 實現過濾
* @param list 原數組
* @param pre 約束條件
* @return 過濾後數組
*/
public List<String> filterString(List<String> list, Predicate<String> pre) {
List<String> result = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
result.add(s);
}
}
return result;
}
供給型介面Supplier T get()
函數型介面Function<T,R> R apply(T t)
5、其他介面
三、函數式引用(方法引用與構造器引用)
使用場景
當要傳遞給 Lambda 體的操作,已經有實現的方法了,可以使用方法引用!
方法引用,本質上也是 Lambda 表達式,而 Lambda 表達式作為函數式介面的實例。所以,方法引用也是函數式介面的實例。
使用格式
方法引用使用的要求:要求介面中的抽象方法的形參列表和返回值類型與方法引用的方法的形參列表和返回值類型相同!(情況三特殊)
情況一 對象 :: 非靜態方法
情況二 類 :: 靜態方法
情況三 類 :: 非靜態方法
示例代碼
package com.mv.java8.basic;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法引用
* 1.使用情景:當要傳遞給 Lambda 體的操作,已經有實現的方法了,可以使用方法引用!
* 2.方法引用,本質上也是 Lambda 表達式,而 Lambda 表達式作為函數式介面的實例。所以,方法引用也是函數式介面的實例。
* 3.使用格式: 類 (或對象) :: 方法名
* 4.具體分為以下三種情況
* 5.方法引用使用的要求:要求介面中的抽象方法的形參列表和返回值類型與方法引用的方法的形參列表和返回值類型相同!
* 對象 :: 非靜態方法
* 類 :: 靜態方法
* 類 :: 非靜態方法
*
* @author wv
* @version V1.0
* @date 2023/8/3 19:45
*/
public class MethodRefTest {
@Test
public void test01() {
// 情況一 : 對象 :: 實例方法
// Consumer 中的 void accept(T t)
// PrintStream 中的 void println(T t)
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("hello");
System.out.println("使用方法引用如下:");
// PrintStream printStream = System.out;
// Consumer<String> con2 = printStream::println;
Consumer<String> con2 = System.out::println;
con2.accept("world");
}
@Test
public void test02() {
// 情況二 : 類 :: 靜態方法
// Comparator 中的 int compare(T t1, T t2)
// Integer 中的 int compare(T t1, T t2)
Comparator<Integer> com1 = (a, b) -> Integer.compare(a, b);
System.out.println(com1.compare(10, 5));
System.out.println("使用方法引用如下:");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com1.compare(5, 10));
// Function 中的 apply(T t)
// Math 中的 Long round(Double d)
Function<Double, Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(func.apply(3.14));
System.out.println("使用方法引用如下:");
Function<Double, Long> func1 = Math::round;
System.out.println(func1.apply(3.14));
}
@Test
public void test03() {
// 情況二 : 類 :: 非靜態方法
// Comparator 中的 int compare(T t1, T t2)
// String 中的 int t1.compareTo(t2)
Comparator<String> com1 = (a, b) -> a.compareTo(b);
System.out.println(com1.compare("a", "b"));
System.out.println("使用方法引用如下:");
Comparator<String> com2 = String::compareTo;
System.out.println(com1.compare("a", "b"));
}
@Test
public void test04() {
// 構造器引用
// Supplier 中的 T get()
Supplier<Object> supplier1 = new Supplier<Object>() {
@Override
public Object get() {
return new Object();
}
};
System.out.println(supplier1.get());
System.out.println("使用方法引用如下:");
Supplier<Object> supplier2 = Object::new;
System.out.println(supplier2.get());
// 數組引用
// Function 中的 R apply(T t)
Function<Integer, String[]> func1 = length -> new String[length];
String[] strings = func1.apply(6);
System.out.println(Arrays.toString(strings));
System.out.println("使用方法引用如下:");
Function<Integer, String[]> func2 = String[] ::new;
String[] strings2 = func1.apply(6);
System.out.println(Arrays.toString(strings2));
}
}
四、Stream 流
1、概念
Java8中有兩大最為重要的改變。第一個是 Lambda 表達式;另外一個則是 Stream API(java.util.stream.*)。
Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查找、過濾和映射數據等操作。使用Stream API 對集合數據進行操作,就類似於使用 SQL 執行的資料庫查詢。也可以使用 Stream API 來並行執行操作。簡而言之,Stream API 提供了一種高效且易於使用的處理數據的方式。
stream 流是數據渠道,用於操作數據源(集合、數組等)所生成的元素序列。
集合講究的是數據,流講究的是計算!
2、操作步驟
- 創建 Stream :一個數據源(如:集合、數組),獲取一個流
- 中間操作:一個中間操作鏈,對數據源的數據進行處理
- 終止操作(終端操作)
3、特點
- Stream 關註的是對數據的運算,CPU 打交道;集合關註的是數據的存儲,與記憶體打交道。
- Stream 流
- 不會自己存儲元素
- 不會改變數據源。相反,他們會返回一個持有結果的 Stream 流
- 操作是延遲執行的,這意味著他們會等到需要結果的時候執行
- Stream 執行流程
- stream 的實例化
- 一系列的中間操作(過濾、映射、......)
- 終止操作
- 說明
- 一個中間操作鏈,對數據源的數據進行處理
- 一旦執行終止操作,就執行中間操作鏈,並產生結果。之後,不會再被使用。(需重新執行)
4、Stream 流創建方式
創建方式一:通過集合創建 Stream 流
創建方式一:通過數組創建 Stream 流
創建方式一:通過 Stream 流的靜態方法 of( ) 創建
創建方式一:由函數創建流:創建無限流
/**
* 通過集合創建 Stream 流
*/
@Test
public void createStreamByCollection() {
List<String> list = Arrays.asList("aaa","bbb","ccc");
// 1.串列流
Stream<String> stream = list.stream();
// 2.並行流
Stream<String> parallelStream = list.parallelStream();
}
/**
* 通過數組創建 Stream 流
*/
@Test
public void createStreamByArray() {
int[] arr = new int[]{1,2,3,4,5,6};
// 調用 Arrays 類的靜態方法 stream
IntStream stream = Arrays.stream(arr);
}
/**
* 通過 Stream 流的 靜態方法of() 創建
*/
@Test
public void createStreamByOf() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
/**
* 由函數創建流:創建無限流
*/
@Test
public void createStream() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
// 迭代
//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
Stream.iterate(0,t -> t + 2).limit(10).forEach(System.out::println);
// 生成
// public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
5、Stream 流的中間操作
篩選與切片
- filter(Predicate p) 接收Lambda,從流中排除某些元素。
- limit(n) 截斷流,使其元素不超過給定數量。
- skip(n) 跳過元素,返回一個扔掉了前n個元素的流。若流中元素不足n個,則返回一個空流。與limit()互補。
- distinct() 篩選,通過流所生成元素的hashCode()和equals()去除重覆元素。
public class StreamAPITest2 {
/**
* 篩選與切片
*/
@Test
public void test() {
List<String> list = Arrays.asList("李華","小明","小紅","小王","李華");
System.out.println(list);
System.out.println("*************************");
// filter(Predicate p) 接收Lambda,從流中排除某些元素。
List<String> filterList = list.stream().filter(e -> !e.equals("李華")).collect(Collectors.toList());
System.out.println(filterList);
System.out.println("*************************");
// limit(n) 截斷流,使其元素不超過給定數量。
System.out.println(list.stream().limit(2).collect(Collectors.toList()));
System.out.println("*************************");
// skip(n) 跳過元素,返回一個扔掉了前n個元素的流。若流中元素不足n個,則返回一個空流。與limit()互補.
System.out.println(list.stream().skip(2).collect(Collectors.toList()));
System.out.println("*************************");
// distinct() 篩選,通過流所生成元素的hashCode()和equals()去除重覆元素
System.out.println(list);
System.out.println(list.stream().distinct().collect(Collectors.toList()));
System.out.println("*************************");
}
}
映射
- **map(Function f) **:接收一個函數作為參數,將元素轉換成其他形式或提取信息,該函數會被應用到每個元素上,並將其映射成一個新的元素。
- flatMap(Function f) :接收一個函數作為參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流。
- mapToDouble(ToDoubleFunction f):接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 DoubleStream。
- mapToInt(ToIntFunction f):接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 IntStream。
- mapToLong(ToLongFunction f):接收一個函數作為參數,該函數會被應用到每個元素上,產生一個新的 LongStream。
/**
* map 與 flatMap映射
*/
@Test
public void testMap() {
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
// map(Function f) 接收一個函數作為參數,將元素轉換成其他形式或提取信息,
// 該函數會被應用到每個元素上,並將其映射成一個新的元素。
// System.out.println(list.stream().map(str -> str.toUpperCase()).collect(Collectors.toList()));
System.out.println(list.stream().map(String::toUpperCase).collect(Collectors.toList()));
// flatMap(Function f) 接收一個函數作為參數,將流中的每個值都換成另一個流,然後把所有流連接成一個流。
// 未使用 flatMap
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::formStringToStream);
streamStream.forEach(s -> {
s.forEach(System.out::println);
});
System.out.println("****************************");
// 使用 flatMap
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::formStringToStream);
characterStream.collect(Collectors.toList()).forEach(System.out::println);
}
public static Stream<Character> formStringToStream(String str) {
List<Character> list = new ArrayList<>();
char[] chars = str.toCharArray();
for (char c : chars) {
list.add(c);
}
return list.stream();
}
排序
- sorted() 自然排序
- sorted(Comparator com) 定製排序
/**
* 排序
*/
@Test
public void testSort() {
// sorted() 自然排序
List<Integer> integerList = Arrays.asList(12, 23, -12, 32, 55, 66, 11);
integerList.stream().sorted().forEach(System.out::println);
System.out.println("***************************");
// sorted(Comparator com) 定製排序
integerList.stream().sorted( (x,y) -> Integer.compare(y,x)).forEach(System.out::println);
}
6、Stream 的終止操作
查找與匹配
- allMatch(Predicate p) :檢查是否匹配所有元素
- anyMatch(Predicate p):檢查是否至少匹配一個元素
- noneMatch(Predicate p):檢查是否沒有匹配所有元素
- findFirst():返回第一個元素
- findAny():返回當前流中的任意元素
- count():返迴流中元素總數
- max(Comparator c):返迴流中最大值
- min(Comparator c):返迴流中最小值
- forEach(Consumer c) 內部迭代(使用 Collection 介面需要用戶去做迭代,稱為外部迭代。相反,Stream API 使用內部迭代——它幫你把迭代做了)
歸約
- reduce(T t, BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。返回 T
- reduce(BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。返回 Optional
/**
* 終止操作 : 歸約
*/
@Test
public void testReduce() {
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// reduce(T t, BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。返回 T
System.out.println(integerList.stream().reduce(0, Integer::sum));
// reduce(BinaryOperator b) 可以將流中元素反覆結合起來,得到一個值。返回 Optional<T>
// System.out.println(integerList.stream().reduce((a, b) -> a + b));
System.out.println(integerList.stream().reduce(Integer::sum));
}
收集
- collect(Collector c):將流轉換為其他形式。接收一個 Collector介面的實現,用於給Stream中元素做彙總的方法
/**
* 終止操作 : 收集
*/
@Test
public void testCollect() {
List<Integer> integerList = Arrays.asList(1, 2, 2, 2, 3, 4);
// toList
System.out.println(integerList.stream().skip(1).collect(Collectors.toList()));
// toSet
System.out.println(integerList.stream().skip(1).collect(Collectors.toSet()));
}
五、Optional 類
Optional
常用方法
Optional.of(T t) : 創建一個 Optional 實例
Optional.empty() : 創建一個空的 Optional 實例
Optional.ofNullable(T t):若 t 不為 null,創建 Optional 實例,否則創建空實例
isPresent() : 判斷是否包含值
orElse(T t) : 如果調用對象包含值,返回該值,否則返回t
orElseGet(Supplier s) :如果調用對象包含值,返回該值,否則返回 s 獲取的值
map(Function f): 如果有值對其處理,並返回處理後的Optional,否則返回 Optional.empty()
flatMap(Function mapper):與 map 類似,要求返回值必須是Optional
本文來自博客園,作者:自律即自由-,轉載請註明原文鏈接:https://www.cnblogs.com/deyo/p/17638586.html