Bài 7: Sử dụng Predicate và Function trong Stream
Mục tiêu:
Hướng dẫn chi tiết cách sử dụng Predicate và Function trong Stream API của Java 8.
Giải thích cách kết hợp
PredicatevàFunctionđể lọc và biến đổi dữ liệu trong Stream.Cung cấp ví dụ minh họa chi tiết để áp dụng vào thực tế.
Dàn ý chi tiết:
1. Predicate trong Stream
Predicate<T> là một Functional Interface được sử dụng để kiểm tra một điều kiện trên đối tượng kiểu T. Nó thường được sử dụng trong các thao tác lọc dữ liệu (filtering) trong Stream.
Cú pháp cơ bản:
Phương thức
test(T t)củaPredicatetrả về giá trị boolean.Ví dụ:
Predicate<Integer> isEven = num -> num % 2 == 0; System.out.println(isEven.test(4)); // true
Sử dụng
Predicatetrong Stream:Phương thức
filter(Predicate<T>)trong Stream dùng để lọc các phần tử thỏa mãn điều kiện.Ví dụ: Lọc các số chẵn từ một danh sách:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) // Sử dụng Predicate .collect(Collectors.toList()); System.out.println(evenNumbers); // [2, 4, 6]
Kết hợp nhiều Predicate:
Sử dụng các phương thức
and(),or(), vànegate()để kết hợp nhiều điều kiện.Ví dụ: Lọc các số chẵn và lớn hơn 3:
Predicate<Integer> isEven = num -> num % 2 == 0; Predicate<Integer> isGreaterThan3 = num -> num > 3; List<Integer> result = numbers.stream() .filter(isEven.and(isGreaterThan3)) .collect(Collectors.toList()); System.out.println(result); // [4, 6]
2. Function trong Stream
Function<T, R> là một Functional Interface được sử dụng để biến đổi dữ liệu từ kiểu T sang kiểu R. Nó thường được sử dụng trong các thao tác biến đổi dữ liệu (mapping) trong Stream.
Cú pháp cơ bản:
Phương thức
apply(T t)củaFunctiontrả về giá trị kiểuR.Ví dụ:
Function<String, Integer> stringLength = str -> str.length(); System.out.println(stringLength.apply("Java")); // 4
Sử dụng
Functiontrong Stream:Phương thức
map(Function<T, R>)trong Stream dùng để biến đổi từng phần tử.Ví dụ: Chuyển đổi các chuỗi thành chữ in hoa:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<String> upperCaseNames = names.stream() .map(String::toUpperCase) // Sử dụng Function .collect(Collectors.toList()); System.out.println(upperCaseNames); // [ALICE, BOB, CHARLIE]
Kết hợp nhiều Function:
Sử dụng phương thức
andThen()hoặccompose()để kết hợp nhiềuFunction.Ví dụ: Chuyển đổi chuỗi thành chữ in hoa và sau đó lấy độ dài:
Function<String, String> toUpperCase = String::toUpperCase; Function<String, Integer> getLength = String::length; Function<String, Integer> transform = toUpperCase.andThen(getLength); List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); List<Integer> lengths = names.stream() .map(transform) .collect(Collectors.toList()); System.out.println(lengths); // [5, 3, 7]
3. Kết hợp Predicate và Function trong Stream
Predicate và Function có thể được kết hợp để thực hiện các thao tác phức tạp hơn, chẳng hạn như lọc dữ liệu trước khi biến đổi.
Ví dụ: Lọc và biến đổi dữ liệu:
Lọc các số chẵn và sau đó tính bình phương của chúng:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); List<Integer> result = numbers.stream() .filter(n -> n % 2 == 0) // Lọc số chẵn .map(n -> n * n) // Tính bình phương .collect(Collectors.toList()); System.out.println(result); // [4, 16, 36]
Ví dụ: Lọc và chuyển đổi chuỗi:
Lọc các chuỗi có độ dài lớn hơn 3 và chuyển đổi thành chữ in hoa:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); List<String> result = names.stream() .filter(name -> name.length() > 3) // Lọc chuỗi .map(String::toUpperCase) // Chuyển đổi .collect(Collectors.toList()); System.out.println(result); // [ALICE, CHARLIE, DAVID]
Ví dụ tổng hợp
Sử dụng
PredicatevàFunctiontrong Stream:List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); List<String> result = names.stream() .filter(name -> name.length() > 3) // Lọc chuỗi .map(String::toUpperCase) // Chuyển đổi .collect(Collectors.toList()); System.out.println(result); // [ALICE, CHARLIE, DAVID]Kết hợp nhiều
PredicatevàFunction:List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6); Predicate<Integer> isEven = num -> num % 2 == 0; Predicate<Integer> isGreaterThan3 = num -> num > 3; Function<Integer, Integer> square = num -> num * num; List<Integer> result = numbers.stream() .filter(isEven.and(isGreaterThan3)) // Lọc số chẵn và > 3 .map(square) // Tính bình phương .collect(Collectors.toList()); System.out.println(result); // [16, 36]
Tổng kết
Predicate<T>được sử dụng để lọc dữ liệu trong Stream thông qua phương thứcfilter().Function<T, R>được sử dụng để biến đổi dữ liệu trong Stream thông qua phương thứcmap().Kết hợp
PredicatevàFunctiongiúp thực hiện các thao tác phức tạp như lọc và biến đổi dữ liệu một cách hiệu quả.
Bài tập thực hành
Tạo một Stream từ một danh sách các số nguyên và lọc ra các số lớn hơn 10. Sau đó, tính bình phương của các số này và in kết quả ra màn hình.
Tạo một Stream từ một danh sách các chuỗi và lọc ra các chuỗi có độ dài lớn hơn 5. Sau đó, chuyển đổi các chuỗi này thành chữ thường và in kết quả ra màn hình.
Kết hợp nhiều
Predicateđể lọc các số nguyên dương và chia hết cho 3. Sau đó, tính tổng các số này.
Gợi ý đáp án
Lọc và tính bình phương:
List<Integer> numbers = Arrays.asList(5, 12, 8, 15, 3); List<Integer> result = numbers.stream() .filter(n -> n > 10) // Lọc số > 10 .map(n -> n * n) // Tính bình phương .collect(Collectors.toList()); System.out.println(result); // [144, 225]Lọc và chuyển đổi chuỗi:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); List<String> result = names.stream() .filter(name -> name.length() > 5) // Lọc chuỗi > 5 ký tự .map(String::toLowerCase) // Chuyển đổi thành chữ thường .collect(Collectors.toList()); System.out.println(result); // [charlie]Kết hợp nhiều Predicate và tính tổng:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 9, 12); Predicate<Integer> isPositive = num -> num > 0; Predicate<Integer> isDivisibleBy3 = num -> num % 3 == 0; int sum = numbers.stream() .filter(isPositive.and(isDivisibleBy3)) // Lọc số dương và chia hết cho 3 .mapToInt(Integer::intValue) // Chuyển đổi thành IntStream .sum(); // Tính tổng System.out.println(sum); // 30
Kết luận
Bài viết này đã hướng dẫn chi tiết cách sử dụng Predicate và Function trong Stream API của Java 8. Hiểu rõ cách kết hợp các Functional Interface này giúp bạn xử lý dữ liệu một cách hiệu quả và linh hoạt trong các bài toán thực tế.