Bài 7: Thực Hành với Functional Interface và Streams API
1. Giới Thiệu về Thực Hành với Functional Interface và Streams API
Để thực sự làm chủ Functional Interface và Streams API trong Java, chúng ta cần thực hành với các bài toán cụ thể. Bài học này sẽ hướng dẫn bạn thông qua nhiều ví dụ thực tế và bài tập để củng cố kiến thức về cách sử dụng Functional Interface và Streams API.
2. Chuẩn Bị Môi Trường và Dữ Liệu
Để bắt đầu thực hành, bạn cần chuẩn bị một môi trường phát triển Java và một tập hợp dữ liệu để làm việc. Dưới đây là một vài bước chuẩn bị:
Cài đặt Java Development Kit (JDK): Đảm bảo rằng bạn đã cài đặt JDK 8 hoặc mới hơn.
Sử dụng một IDE như IntelliJ IDEA, Eclipse, hoặc VS Code: Giúp tăng hiệu quả viết và kiểm tra mã nguồn.
Tạo một lớp Product để sử dụng trong các ví dụ:
public class Product { private String name; private double price; public Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }
3. Ví Dụ Thực Hành với Streams API
Ví Dụ 1: Lọc Dữ Liệu
Mục tiêu: Lọc ra các sản phẩm có giá lớn hơn 100.
List<Product> products = Arrays.asList(
new Product("Laptop", 1500),
new Product("Mouse", 50),
new Product("Keyboard", 75),
new Product("Monitor", 200)
);
List<Product> expensiveProducts = products.stream()
.filter(product -> product.getPrice() > 100)
.collect(Collectors.toList());
expensiveProducts.forEach(System.out::println);
// Output:
// Laptop ($1500.0)
// Monitor ($200.0)
Giải thích: Sử dụng filter
để lọc các sản phẩm có giá lớn hơn 100 và thu thập kết quả vào một danh sách mới.
Ví Dụ 2: Ánh Xạ Dữ Liệu
Mục tiêu: Chuyển đổi danh sách các sản phẩm thành danh sách tên sản phẩm.
List<String> productNames = products.stream()
.map(Product::getName)
.collect(Collectors.toList());
productNames.forEach(System.out::println);
// Output:
// Laptop
// Mouse
// Keyboard
// Monitor
Giải thích: Sử dụng map
để chuyển đổi mỗi sản phẩm thành tên của nó và thu thập kết quả vào một danh sách mới.
Ví Dụ 3: Sắp Xếp Dữ Liệu
Mục tiêu: Sắp xếp danh sách sản phẩm theo giá.
List<Product> sortedProducts = products.stream()
.sorted(Comparator.comparingDouble(Product::getPrice))
.collect(Collectors.toList());
sortedProducts.forEach(System.out::println);
// Output:
// Mouse ($50.0)
// Keyboard ($75.0)
// Monitor ($200.0)
// Laptop ($1500.0)
Giải thích: Sử dụng sorted
với Comparator
để sắp xếp các sản phẩm theo giá từ thấp đến cao.
Ví Dụ 4: Giảm Thiểu Dữ Liệu
Mục tiêu: Tính tổng giá trị của tất cả các sản phẩm.
double totalValue = products.stream()
.map(Product::getPrice)
.reduce(0.0, Double::sum);
System.out.println("Total value: " + totalValue);
// Output: Total value: 1825.0
Giải thích: Sử dụng map
để chuyển đổi mỗi sản phẩm thành giá của nó và sau đó sử dụng reduce
để tính tổng giá trị.
4. Ví Dụ Thực Hành với Functional Interface
Ví Dụ 1: Sử Dụng Predicate
để Kiểm Tra Điều Kiện
Mục tiêu: Kiểm tra xem một chuỗi có trống hay không.
Predicate<String> isEmpty = String::isEmpty;
System.out.println(isEmpty.test("")); // Output: true
System.out.println(isEmpty.test("Hello")); // Output: false
Giải thích: Sử dụng Predicate
để kiểm tra điều kiện và trả về kết quả boolean.
Ví Dụ 2: Sử Dụng Function
để Chuyển Đổi Dữ Liệu
Mục tiêu: Chuyển đổi chuỗi thành chiều dài của nó.
Function<String, Integer> stringLength = String::length;
System.out.println(stringLength.apply("Hello")); // Output: 5
System.out.println(stringLength.apply("Java")); // Output: 4
Giải thích: Sử dụng Function
để chuyển đổi mỗi chuỗi thành chiều dài của nó.
Ví Dụ 3: Sử Dụng Consumer
để Thực Hiện Hành Động
Mục tiêu: In ra mỗi phần tử trong danh sách.
Consumer<String> print = System.out::println;
List<String> names = Arrays.asList("John", "Jane", "Adam", "Eve");
names.forEach(print);
// Output:
// John
// Jane
// Adam
// Eve
Giải thích: Sử dụng Consumer
để thực hiện một hành động với mỗi phần tử trong danh sách.
Ví Dụ 4: Sử Dụng Supplier
để Cung Cấp Dữ Liệu
Mục tiêu: Cung cấp ngày hiện tại.
Supplier<LocalDate> currentDate = LocalDate::now;
System.out.println(currentDate.get()); // Output: Current Date
Giải thích: Sử dụng Supplier
để cung cấp dữ liệu mà không cần đầu vào.
5. Bài Tập Thực Hành
Bài Tập 1: Lọc và Sắp Xếp Dữ Liệu
- Mô tả: Cho một danh sách các sản phẩm, lọc ra các sản phẩm có giá lớn hơn 50 và sắp xếp theo giá giảm dần.
Ví dụ mã nguồn:
List<Product> products = Arrays.asList(
new Product("Laptop", 1500),
new Product("Mouse", 50),
new Product("Keyboard", 75),
new Product("Monitor", 200)
);
List<Product> filteredAndSortedProducts = products.stream()
.filter(product -> product.getPrice() > 50)
.sorted(Comparator.comparingDouble(Product::getPrice).reversed())
.collect(Collectors.toList());
filteredAndSortedProducts.forEach(System.out::println);
// Output:
// Laptop ($1500.0)
// Monitor ($200.0)
// Keyboard ($75.0)
Bài Tập 2: Tính Giá Trị Trung Bình của Sản Phẩm
- Mô tả: Cho một danh sách các sản phẩm, tính giá trị trung bình của tất cả các sản phẩm.
Ví dụ mã nguồn:
double averagePrice = products.stream()
.mapToDouble(Product::getPrice)
.average()
.orElse(0.0);
System.out.println("Average price: " + averagePrice);
// Output: Average price: 456.25
Bài Tập 3: Sử Dụng Predicate
và Consumer
- Mô tả: Kiểm tra xem trong danh sách sản phẩm có sản phẩm nào giá dưới 100 không và in ra tên sản phẩm đó.
Ví dụ mã nguồn:
Predicate<Product> isCheap = product -> product.getPrice() < 100;
Consumer<Product> printName = product -> System.out.println(product.getName());
products.stream()
.filter(isCheap)
.forEach(printName);
// Output:
// Mouse
// Keyboard
6. Thực Hành Kết Hợp Functional Interface và Streams API
Bài Tập 4: Kết Hợp Function
, Predicate
, và Streams API
- Mô tả: Cho một danh sách các sản phẩm, lọc ra các sản phẩm có giá lớn hơn 50, chuyển đổi thành tên sản phẩm và in ra.
Ví dụ mã nguồn:
List<Product> products = Arrays.asList(
new Product("Laptop", 1500),
new Product("Mouse", 50),
new Product("Keyboard", 75),
new Product("Monitor", 200)
);
products.stream()
.filter(product -> product.getPrice() > 50)
.map(Product::getName)
.forEach(System.out::println);
// Output:
// Laptop
// Keyboard
// Monitor
7. Kết Luận
Thực hành là cách tốt nhất để nắm vững các khái niệm về Functional Interface và Streams API. Qua các ví dụ và bài tập thực hành, bạn sẽ hiểu rõ hơn về cách áp dụng các kỹ thuật này vào các tình huống thực tế.
Câu hỏi củng cố kiến thức
Streams API cho phép xử lý dữ liệu theo cách nào?
- Streams
API cho phép xử lý dữ liệu theo cách khai báo (declarative) thay vì cách mệnh lệnh (imperative).
Toán tử
map
trong Streams API dùng để làm gì?map
dùng để chuyển đổi mỗi phần tử trong Stream thành một phần tử mới.
Sự khác nhau giữa
filter
vàreduce
trong Streams API là gì?filter
dùng để lọc các phần tử dựa trên một điều kiện, cònreduce
dùng để tổng hợp các phần tử thành một giá trị duy nhất.
Predicate là gì?
Predicate
là Functional Interface với một phương thức duy nhấttest
trả về boolean.
Supplier cung cấp gì?
Supplier
cung cấp một giá trị mà không cần đầu vào.
Tóm tắt
Functional Interface và Streams API là hai công cụ mạnh mẽ trong Java, giúp viết mã nguồn ngắn gọn, dễ bảo trì và hiệu quả. Thực hành với các ví dụ cụ thể sẽ giúp bạn nắm vững các kỹ thuật này và áp dụng vào các bài toán thực tế.
Subscribe to my newsletter
Read articles from hoangkim directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by