一、Lambda表達式
Lambda 表達式是一種匿名函數,它可以用來定義函數式接口的實現。Lambda 表達式可以用來簡化代碼,提高代碼的可讀性和可維護性。
1、Lambda 表達式
1.1、語法介紹
Lambda 表達式的語法如下:
(parameters) -> expression or statement或者(parameters) -> { statements; }
Lambda表達式由以下幾個部分組成:
-
參數列表(parameters):Lambda表達式可以有零個或多個參數。如果有多個參數,使用逗號將它們分隔開。參數的類型可以顯式指定,也可以通過上下文推斷得出。
-
箭頭符號(->):箭頭符號用于分隔參數列表和Lambda表達式的主體。
-
表達式(expression)或語句塊(statements):Lambda表達式的主體可以是一個表達式或一個代碼塊。如果主體是一個表達式,則可以直接返回該表達式的結果。如果主體是一個代碼塊,則需要使用大括號將代碼塊括起來,并且可能需要使用return語句來返回結果。
Lambda 表達式是一種匿名函數,它可以用來定義函數式接口的實現。Lambda 表達式可以用來簡化代碼,提高代碼的可讀性和可維護性。
1.2、Lambda表達式特性
除了Java版本的要求外,Lambda表達式只能用于函數式接口。
- 函數式接口:Lambda表達式只能用于函數式接口,即只有一個抽象方法的接口。函數式接口可以使用@FunctionalInterface注解進行標記,以確保只有一個抽象方法。
@FunctionalInterface
interface MyInterface {void myMethod();
}public class Main {public static void main(String[] args) {MyInterface myInterface = () -> System.out.println("Hello, Lambda!");myInterface.myMethod();}
}
在上面的示例中,我們定義了一個函數式接口MyInterface,它只有一個抽象方法myMethod。然后,我們使用Lambda表達式來實現這個接口,并在myMethod方法中打印一條消息。最后,我們創建了一個接口實例并調用myMethod方法。
- 上下文推斷:Lambda表達式的參數類型可以通過上下文推斷得出,無需顯式指定。這是Java 8引入的類型推斷機制的一部分。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
在這個例子中,Lambda表達式name -> System.out.println(name)被傳遞給forEach方法。編譯器會根據forEach方法的參數類型推斷出Lambda表達式的參數類型為String。
- 方法引用:Lambda表達式可以使用方法引用來簡化代碼,雙冒號(:😃。方法引用是指直接引用已有方法的方式,可以通過類名、對象實例或超類來引用方法。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");names.stream().map(String::toUpperCase).forEach(System.out::println);
在這個例子中,
String::toUpperCase
是一個方法引用,它引用了String類的toUpperCase
方法。在map方法中,每個元素都會被轉換為大寫。然后,使用forEach方法將轉換后的結果打印出來。方法引用使代碼更加簡潔,不需要編寫Lambda表達式來調用toUpperCase方法。
1.3、方法引用,雙冒號(::)
在Lambda表達式中,雙冒號(::)是一種特殊的語法,用于引用方法或構造函數。它可以用于以下幾種情況:
- 方法引用:使用雙冒號(::)來引用一個已經存在的方法。例如,假設有一個函數式接口
Function
,它有一個抽象方法apply
,可以使用方法引用來引用一個已經存在的方法作為apply
的實現。例如:
Function<String, Integer> function = Integer::parseInt;
這里,
Integer::parseInt
表示引用了Integer
類的靜態方法parseInt
作為apply
方法的實現。
- 構造函數引用:使用雙冒號(::)來引用一個構造函數。例如,假設有一個函數式接口
Supplier
,它有一個抽象方法get
,可以使用構造函數引用來引用一個構造函數作為get
方法的實現。例如:
Supplier<List<String>> supplier = ArrayList::new;
這里,
ArrayList::new
表示引用了ArrayList
類的構造函數作為get
方法的實現。
- 數組引用:使用雙冒號(::)來引用一個數組。例如,可以使用
int[]::new
來引用一個整型數組的構造函數。
2、Lambda 表達式最佳實踐
2.1、簡單使用示例
以下是一些使用 Lambda 表達式的示例:
// 作為方法的參數
List<String> names = Arrays.asList("Alice", "Bob", "Carol");
names.sort((a, b) -> b.compareTo(a));// 作為構造函數的參數
Button button = new Button(() -> System.out.println("Click"));// 作為成員變量的初始值
Map<String, String> map = new HashMap<>();
map.put("key", "value");// 作為局部變量的初始值
int sum = names.stream().mapToInt(String::length).sum();
Lambda 表達式可以提高代碼的可讀性和可維護性,因此在編寫 Java 代碼時,我們應該盡可能使用 Lambda 表達式。
2.2、Lambda表達式最佳實踐
以下是使用Lambda表達式的一些最佳實踐:
-
使用合適的函數式接口:Lambda表達式需要與函數式接口一起使用。函數式接口是只包含一個抽象方法的接口。在選擇函數式接口時,確保它與Lambda表達式的參數和返回類型匹配。
-
保持Lambda表達式簡潔:Lambda表達式的主要目的是使代碼更加簡潔和易讀。因此,應盡量避免編寫過于復雜的Lambda表達式。如果Lambda表達式變得過于冗長或復雜,可以考慮將其提取為一個獨立的方法或使用方法引用。
-
使用方法引用:方法引用是Lambda表達式的一種簡化形式,可以使代碼更加簡潔和易讀。如果Lambda表達式只是簡單地調用一個已經存在的方法,可以考慮使用方法引用來代替Lambda表達式。
-
避免副作用:Lambda表達式應該是無副作用的,即不會對外部狀態產生影響。這有助于提高代碼的可讀性和可維護性。如果Lambda表達式需要修改外部狀態,應該使用其他方式來處理,例如使用局部變量或實例變量。
-
使用類型推斷:在Lambda表達式中,編譯器可以根據上下文推斷參數類型。因此,可以省略參數類型的顯式聲明,使代碼更加簡潔。但是,如果Lambda表達式的參數類型不明確或不容易理解,最好顯式聲明參數類型,以提高代碼的可讀性。
-
使用Lambda表達式的流式操作:Lambda表達式與流式操作(Stream API)結合使用可以實現更加簡潔和功能強大的代碼。流式操作提供了一種流暢的方式來處理集合數據,可以使用Lambda表達式來定義各種操作,如過濾、映射、排序等。
總的來說,使用Lambda表達式應該注重代碼的簡潔性、可讀性和可維護性。合理選擇函數式接口、使用方法引用、避免副作用以及充分利用類型推斷和流式操作等技巧,可以使Lambda表達式的使用更加高效和優雅。
二、Steam API
Java 8 引入了新的 Stream API,它提供了一種更簡單、更高效的方式來處理集合。Stream API 是一個函數式編程 API,它允許我們對集合中的元素進行各種操作,而無需使用傳統的 for 循環。
Stream API 的核心是 Stream 對象,它表示一個元素序列。Stream 可以由集合創建,也可以由其他 Stream 創建。Stream 可以通過各種方式進行操作,包括過濾、映射、聚合和收集。
1、Stream API 常見使用
Stream API 的使用非常簡單。要創建一個 Stream,我們可以使用集合的 stream() 方法。例如,以下代碼創建了一個包含數字 1 到 10 的 Stream:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = numbers.stream();
Stream 可以通過各種方式進行操作。最常見的操作是過濾和映射。過濾操作用于從 Stream 中選擇滿足某些條件的元素。例如,以下代碼使用 filter()
方法從 Stream 中選擇所有偶數:
Stream<Integer> evenNumbers = stream.filter(n -> n % 2 == 0);
映射操作用于將 Stream 中的元素轉換為另一種類型的元素。例如,以下代碼使用 map()
方法將 Stream 中的數字轉換為字符串:
Stream<String> strings = stream.map(n -> String.valueOf(n));
Stream 還可以通過聚合操作來計算結果。聚合操作用于將 Stream 中的元素匯總到一個單一的值。例如,以下代碼使用 sum()
方法計算 Stream 中所有元素的總和:
int sum = stream.sum();
Stream 還可以通過收集操作將結果保存到集合中。例如,以下代碼使用 collect()
方法將 Stream 中的元素收集到一個列表中:
List<Integer> list = stream.collect(Collectors.toList());
Stream API 提供了一種非常簡單、高效的方式來處理集合。它可以幫助我們編寫更簡潔、更可讀的代碼。
2、Stream API 使用示例
以下是一些使用 Stream API 的示例:
- 計算集合中所有元素的總和:
int sum = numbers.stream().mapToInt(n -> n).sum();
- 計算集合中所有元素的平均值:
double average = numbers.stream().mapToInt(n -> n).average().getAsDouble();
- 查找集合中最大的元素:
Optional<Integer> max = numbers.stream().max(Comparator.comparing(n -> n));
- 查找集合中所有的偶數:
Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);
- 將集合中的元素轉換為字符串:
Stream<String> strings = numbers.stream().map(n -> String.valueOf(n));
- 將集合中的元素收集到一個列表中:
List<Integer> list = numbers.stream().collect(Collectors.toList());
- 將集合中的元素保存到文件中:
numbers.stream().forEach(n -> System.out.println(n));
3、Stream API 核心方法
下面常用的Stream API方法:
方法 | 描述 | 類型 | 使用示例 |
---|---|---|---|
filter(Predicate<T> predicate) | 過濾流中的元素,只保留滿足條件的元素 | 中間操作 | stream.filter(x -> x > 5) |
map(Function<T, R> mapper) | 對流中的每個元素應用一個函數,并將結果映射為一個新的流 | 中間操作 | stream.map(x -> x * 2) |
flatMap(Function<T, Stream<R>> mapper) | 對流中的每個元素應用一個函數,并將結果扁平化為一個新的流 | 中間操作 | stream.flatMap(x -> Stream.of(x, x + 1)) |
distinct() | 去除流中的重復元素 | 中間操作 | stream.distinct() |
sorted() | 對流中的元素進行排序 | 中間操作 | stream.sorted() |
limit(long maxSize) | 限制流中元素的數量不超過指定的最大值 | 中間操作 | stream.limit(10) |
skip(long n) | 跳過流中的前n個元素 | 中間操作 | stream.skip(5) |
forEach(Consumer<T> action) | 對流中的每個元素執行指定的操作 | 終止操作 | stream.forEach(System.out::println) |
collect(Collector<T, A, R> collector) | 將流中的元素收集到一個可變容器中,如List、Set或Map | 終止操作 | stream.collect(Collectors.toList()) |
reduce(BinaryOperator<T> accumulator) | 將流中的元素按照指定的操作進行歸約,得到一個結果 | 終止操作 | stream.reduce(0, (a, b) -> a + b) |
anyMatch(Predicate<T> predicate) | 檢查流中是否存在滿足條件的元素 | 終止操作 | stream.anyMatch(x -> x > 5) |
allMatch(Predicate<T> predicate) | 檢查流中的所有元素是否都滿足條件 | 終止操作 | stream.allMatch(x -> x > 0) |
noneMatch(Predicate<T> predicate) | 檢查流中是否沒有任何元素滿足條件 | 終止操作 | stream.noneMatch(x -> x < 0) |
findFirst() | 返回流中的第一個元素 | 終止操作 | stream.findFirst() |
findAny() | 返回流中的任意一個元素 | 終止操作 | stream.findAny() |
count() | 返回流中的元素數量 | 終止操作 | stream.count() |
min(Comparator<T> comparator) | 返回流中的最小元素 | 終止操作 | stream.min(Comparator.naturalOrder()) |
max(Comparator<T> comparator) | 返回流中的最大元素 | 終止操作 | stream.max(Comparator.naturalOrder()) |
這些方法根據其功能和使用方式可以分為兩種類型:中間操作和終止操作。中間操作返回一個新的流,可以被鏈式調用,而終止操作會產生一個最終結果或副作用。根據具體的需求,可以根據需要組合使用這些方法來處理和操作流中的元素。
4、Stream 可以配合哪些類使用
Stream 可以配合多種類使用,包括但不限于以下類:
- Collection 接口:Stream 可以通過 Collection 接口的
stream()
方法來創建。
List<String> names = Arrays.asList("Alice", "Bob", "Carol");
Stream<String> stream = names.stream();
- Arrays 類:Stream 可以通過 Arrays 類的
stream()
方法來創建。
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
- BufferedReader 類:Stream 可以通過 BufferedReader 類的
lines()
方法來創建。
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
Stream<String> lines = reader.lines();
- IntStream、LongStream、DoubleStream 類:這些類提供了特定類型的 Stream,可以用于處理基本類型的元素。
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
LongStream longStream = LongStream.range(1, 10);
DoubleStream doubleStream = DoubleStream.generate(Math::random);
- Files 類:Stream 可以通過 Files 類的
lines()
方法來讀取文件內容并創建。
Stream<String> lines = Files.lines(Paths.get("file.txt"));
- Pattern 類:Stream 可以通過 Pattern 類的
splitAsStream()
方法來將字符串拆分為 Stream。
Pattern pattern = Pattern.compile("\\s+");
Stream<String> words = pattern.splitAsStream("Hello World");
- Random 類:Stream 可以通過 Random 類的
ints()
、longs()
、doubles()
方法來生成隨機數的 Stream。
Random random = new Random();
IntStream randomInts = random.ints(5, 1, 10);
- StreamSupport 類:Stream 可以通過 StreamSupport 類的
stream()
方法來創建自定義的 Stream。
Iterable<String> iterable = Arrays.asList("Alice", "Bob", "Carol");
Stream<String> stream = StreamSupport.stream(iterable.spliterator(), false);
這些類提供了不同的方式來創建和處理 Stream,可以根據具體的需求選擇合適的類。配合不同的類使用 Stream 可以實現豐富的功能和靈活的操作。
參考
- 官方文檔