Bài 7: Thực Hành với Functional Interface và Streams API

hoangkimhoangkim
6 min read

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 PredicateConsumer
  • 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

  1. 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).

  1. 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.
  2. Sự khác nhau giữa filterreduce 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òn reduce dùng để tổng hợp các phần tử thành một giá trị duy nhất.
  3. Predicate là gì?

    • Predicate là Functional Interface với một phương thức duy nhất test trả về boolean.
  4. 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ế.

0
Subscribe to my newsletter

Read articles from hoangkim directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

hoangkim
hoangkim