Lambda 表達式
Lambda 表達式是 JDK8 的一個新特性,可以取代大部分的匿名內部類,寫出更優雅的 Java 代碼,尤其在集合的遍歷和其他集合操作中,可以極大地優化代碼結構。
JDK 也提供了大量的內置函數式接口供我們使用,使得 Lambda 表達式的運用更加方便、高效。
可以對某些匿名內部類的寫法進行簡化,它是函數式編程思想的一個重要體現,不用關注是什么對象,而是更關注對數據進行了什么操作。
基本格式
(參數列表)->{代碼}
范例
范例一:
在創建線程並啟動時可以使用匿名內部類的寫法;
- 匿名內部類方式:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread());
}
}).start();
- Lambda方式:
new Thread(() -> {
System.out.println(Thread.currentThread());
}).start();
范例二:
IntBinaryOperator是一個接口,使用匿名內部類的寫法調用該方法;
- 匿名內部類方式:
public static int calculateNum(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}
@Test
public void testLambda2() {
int i = calculateNum(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
});
System.out.println(i);
}
- Lambda方式:
public static int calculateNum(IntBinaryOperator operator) {
int a = 10;
int b = 20;
return operator.applyAsInt(a, b);
}
@Test
public void testLambda2() {
int i = calculateNum((int left, int right) -> {
return left + right;
});
System.out.println(i);
}
范例三:
IntPredicate是一個接口。先使用匿名內部類的寫法調用該方法;
- 匿名內部類方式:
public static void printNum(IntPredicate predicate) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
if (predicate.test(i)) {
System.out.println(i);
}
}
}
@Test
public void testLambda3() {
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
return value % 3 == 0;
}
});
}
- Lambda方式:
public static void printNum(IntPredicate predicate) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
if (predicate.test(i)) {
System.out.println(i);
}
}
}
@Test
public void testLambda3() {
printNum((int value) -> {
return value % 3 == 0;
});
}
范例四:
Function是一個接口,先使用匿名內部類的寫法調用該方法;
- 匿名內部類方式:
public static <R> R typeConver(Function<String, R> function) {
String str = "1235";
R result = function.apply(str);
return result;
}
@Test
public void testLambda4() {
Integer result = typeConver(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
System.out.println(result);
}
- Lambda方式:
public static <R> R typeConver(Function<String, R> function) {
String str = "1235";
R result = function.apply(str);
return result;
}
@Test
public void testLambda4() {
Integer result = typeConver((String s) -> {
return Integer.valueOf(s);
});
System.out.println(result);
}
范例五:
IntConsumer是一個接口,先使用匿名內部類的寫法調用該方法;
- 匿名內部類方式:
public static void foreachArr(IntConsumer consumer) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
consumer.accept(i);
}
}
@Test
public void testLambda5() {
foreachArr(new IntConsumer() {
@Override
public void accept(int value) {
System.out.println(value);
}
});
- Lambda方式:
public static void foreachArr(IntConsumer consumer) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
consumer.accept(i);
}
}
@Test
public void testLambda5() {
foreachArr((int value) -> {
System.out.println(value);
});
}
省略規則
- 參數類型可以省略;
public static void foreachArr(IntConsumer consumer) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
consumer.accept(i);
}
}
@Test
public void testLambda5() {
foreachArr((value) -> {
System.out.println(value);
});
}
- 方法體只有一句代碼時大括號return和唯一一句代碼的分號可以省略;
public static void foreachArr(IntConsumer consumer) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
consumer.accept(i);
}
}
@Test
public void testLambda5() {
foreachArr((value) -> System.out.println(value));
}
- 方法只有一個參數時小括號可以省略;
public static void foreachArr(IntConsumer consumer) {
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i : arr) {
consumer.accept(i);
}
}
@Test
public void testLambda5() {
foreachArr(value -> System.out.println(value));
}
- 以上這些規則都記不住也可以省略不記,可通過idea的replaceLambda表達式快速生成lambda表達式;
Stream 流
Stream將要處理的元素集合看作一種流,在流的過程中,借助Stream API對流中的元素進行操作。
Stream - 特性
Stream可以由數組或集合創建,對流的操作分為兩種:
- 中間操作,每次返回一個新的流,可以有多個;
- 終端操作,每個流只能進行一次終端操作,終端操作結束后流無法再次使用。終端操作會產生一個新的集合或值。
Stream特性:
-
stream不存儲數據,而是按照特定的規則對數據進行計算,一般會輸出結果;
-
stream不會改變數據源,通常情況下會產生一個新的集合或一個值;
-
stream具有延遲執行特性,只有調用終端操作時,中間操作才會執行。
Stream - 創建方式
Stream創建方式有三種:
-
通過 java.util.Collection.stream() 方法用集合創建流;
-
使用java.util.Arrays.stream(T[] array)方法用數組創建流;
-
使用Stream的靜態方法:of()、iterate()、generate()。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @author hos
* @Createdate 2022/3/21 14:40
*/
public class StreamCreateType {
public static void main(String[] args) {
/**
* Stream 流的創建有3種方式
* 1. Collection.stream()方法用集合創建
* 2. Arrays.stream(T[] array) 方法用數組創建
* 3. 使用Stream的靜態方法:of()、iterate()、generate()
*/
//方式一: Collection.stream()方法用集合創建
List<String> list = Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9");
// 創建一個順序流
Stream<String> stream = list.stream();
// 創建一個並行流
Stream<String> stringStream = list.parallelStream();
List<String> collect = stringStream.collect(Collectors.toList());
//方式二: Arrays.stream(T[] array) 方法用數組創建
int[] array = {1, 2, 3, 4, 5};
IntStream stream1 = Arrays.stream(array);
System.out.println(stream1.max().getAsInt());
//方式三: 使用Stream的靜態方法:of()、iterate()、generate()
Stream<Integer> intStream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
// 0 3 6 9
stream2.forEach(System.out::println);
AtomicInteger m = new AtomicInteger(10);
Stream<Integer> stream3 = Stream.generate(()-> m.getAndIncrement()).limit(3);
//10 11 12
stream3.forEach(System.out::println);
/**
* 創建流:
* - 單列集合: 集合對象.stream()
* - 數組: Arrays.stream(數組) 或 Stream.of
* - 雙列集合: 轉換成單列集合后再創建
*/
//單列集合: 集合對象.stream()
List<String> listStr = new ArrayList<>();
Stream<String> stream4 = listStr.stream();
//數組: Arrays.stream(數組) 或 Stream.of
Integer[] arr = {1,2,3,4,5};
Stream<Integer> stream5 = Arrays.stream(arr);
Stream<Integer> stream6 = Stream.of(arr);
//雙列集合: 轉換成單列集合后再創建
Map<String,Integer> map = new HashMap<>();
map.put("hos",19);
map.put("hosys",17);
map.put("hosystem",16);
Stream<Map.Entry<String, Integer>> stream7 = map.entrySet().stream();
}
}
Stream - 使用
中間操作
map
map,可以將一個流的元素按照一定的映射規則映射到另一個流中;
map,接收一個函數作為參數,該函數會被應用到每個元素上,並將其映射成一個新的元素。
//Stream - map: 可以將一個流的元素按照一定的映射規則映射到另一個流中
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
Arrays.stream(arr).map(element -> element + 10).forEach(System.out::println);
filter
filter,對流中的元素進行條件過濾,符合過濾條件的才能繼續留在流中;
filter,按照一定的規則校驗流中的元素,將符合條件的元素提取到新的流中的操作。
//Stream - filter: 對流中的元素進行條件過濾
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
//打印數組中大於3的元素
Arrays.stream(arr).filter(arrElement -> arrElement > 3).forEach(System.out::println);
distinct
distinct,去除流中的重復元素;
注意: distinct方法是依賴Object的equals方法來判斷是否是相同對象的,所以需要注意重寫equals方法;
//Stream - distinct: 去除重復元素
Integer[] arr = {1, 2, 2, 3, 3, 4, 4, 5};
//Arrays.stream創建流
//去除重復元素
Arrays.stream(arr).distinct().forEach(System.out::println);
sorted
sorted(),自然排序,流中元素需實現Comparable接口;
sorted(Comparator com),Comparator排序器自定義排序。
注意: 如果調用空參的sorted()方法,需要流中的元素是實現了Comparable。
//Stream - sorted: 自然排序
Integer[] arr = {5,4, 3, 2, 1};
//Arrays.stream創建流
Arrays.stream(arr).sorted().forEach(System.out::println);
limit
limit,可以設置流的最大長度,超出的部分將被拋棄;
//Stream - limit: 設置流的最大長度
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
Arrays.stream(arr).limit(2).forEach(System.out::println);
skip
skip,跳過流中的前n個元素,返回剩下的元素;
//Stream - skip: 跳過流中的前n個元素
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
Arrays.stream(arr).skip(2).forEach(System.out::println);
flatMap
flatMap,接收一個函數作為參數,將流中的每個值都換成另一個流,然后把所有流連接成一個流;
map只能把一個對象轉換成另一個對象來作為流中的元素。而flatMap可以把一個對象轉換成多個對象作為流中的元素。
//Stream - flatMap: 接收一個函數作為參數,將流中的每個值都換成另一個流
ArrayList<Object> list = new ArrayList<>();
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<Object> list2 = new ArrayList<>();
ArrayList<Object> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
ArrayList<Object> list5 = new ArrayList<>();
list1.add("str1");
list2.add("str2");
list3.add("str3");
list4.add("str4");
list5.add("str5");
list.add(list1);
list.add(list2);
list.add(list3);
list.add(list4);
list.add(list5);
list.stream().flatMap(o -> Stream.of(o)).forEach(System.out::println);
終結操作
forEach
forEach方法,通過 lambda 表達式的方式遍歷集合中的元素;
forEach,對流中的元素進行遍歷操作,通過傳入的參數去指定對遍歷到的元素進行什么具體操作。
//Stream - forEach: 通過 lambda 表達式的方式遍歷集合中的元素
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
Arrays.stream(arr).forEach(System.out::println);
count
count,用來獲取當前流中元素的個數;
//Stream - count: 用來獲取當前流中元素的個數
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
System.out.println(Arrays.stream(arr).count());
max&min
max&min,可以用來或者流中的最值。
//Stream - max&min: 可以用來或者流中的最值
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
System.out.println(Arrays.stream(arr).max((o1, o2) -> o1 - o2).get());
System.out.println(Arrays.stream(arr).min((o1, o2) -> o1 - o2).get());
collect
collect,把當前流轉換成一個集合;
collect,把一個流收集起來,最終可以是收集成一個值也可以收集成一個新的集合;流不存儲數據,那么在流中的數據完成處理后,需要將流中的數據重新歸集到新的集合里。
//Stream - collect: 把當前流轉換成一個集合
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
//收集大於3且放到list集合中
List<Integer> collect = Arrays.stream(arr).filter(ele -> ele > 3).collect(Collectors.toList());
System.out.println(collect);
reduce
reduce,把一個流縮減成一個值,能實現對集合求和、求乘積和求最值操作;
reduce,對流中的數據按照你指定的計算方式計算出一個結果。
//Stream - reduce: 對流中的數據按照你指定的計算方式計算出一個結果
Integer[] arr = {1, 2, 3, 4, 5};
//Arrays.stream創建流
Integer addSum = Arrays.stream(arr).reduce(1, (result, element) -> result + element);
Integer subSum = Arrays.stream(arr).reduce(1, (result, element) -> result - element);
Integer mulSum = Arrays.stream(arr).reduce(1, (result, element) -> result * element);
Integer divSum = Arrays.stream(arr).reduce(120, (result, element) -> result / element);
System.out.println(addSum);
System.out.println(subSum);
System.out.println(mulSum);
System.out.println(divSum);