0
0
Lập trình
Hưng Nguyễn Xuân 1
Hưng Nguyễn Xuân 1xuanhungptithcm

Hướng Dẫn Sử Dụng Gatherer Trong Java Stream

Đăng vào 4 ngày trước

• 6 phút đọc

Giới thiệu

Trong lập trình Java, việc xử lý dữ liệu bằng các stream đã trở nên phổ biến nhờ vào tính linh hoạt và khả năng tối ưu hóa. Một trong những khái niệm mới và thú vị trong Java là Gatherer, một dạng trừu tượng giống như collector cho phép bạn thực hiện các phép biến đổi phức tạp hơn trên các stream. Bài viết này sẽ giúp bạn hiểu rõ về Gatherer, cách sử dụng và những lưu ý khi triển khai nó.

1. Gatherer là gì?

Gatherer là một trừu tượng mới trong Java, cho phép bạn biến đổi một stream theo cách cần duy trì trạng thái hoặc xem xét nhiều phần tử cùng lúc, vượt ra ngoài khả năng của map, filter, và flatMap. Hãy tưởng tượng rằng Gatherer giống như một phép toán trung gian tùy chỉnh, có sức mạnh tương đương với việc viết collector của riêng bạn, nhưng dành cho các phép toán trung gian thay vì các phép toán cuối.

Các ứng dụng của Gatherer

  • Chuyển đổi trạng thái (như "nhóm các phần tử liên tiếp", "chia thành các cửa sổ", "chỉ phát hành các thay đổi").
  • Khi các phép toán cơ bản như map, filter, và flatMap không đủ.
  • Khi bạn cần các phép biến đổi có thể tái sử dụng và kết hợp mà không cần phải sử dụng vòng lặp mệnh lệnh.

2. Khi nào nên và không nên sử dụng Gatherer

Nên sử dụng Gatherer khi:

  • Bạn cần thực hiện các phép biến đổi có trạng thái.
  • Các phép toán cơ bản không đáp ứng đủ nhu cầu.
  • Bạn muốn các phép biến đổi có thể tái sử dụng và kết hợp.

Không nên sử dụng Gatherer khi:

  • Một phép toán đơn giản như map, filter, hay flatMap đã đủ.
  • Bạn chỉ cần một phép giảm cuối (như collect, reduce) → thì nên sử dụng một Collector.
  • Hiệu suất là điều quan trọng và bạn không cần hành vi trạng thái phức tạp → Gatherer có thể gây overhead về mặt trừu tượng.

3. Cách sử dụng Gatherer

API của Gatherer tập trung vào phương thức Gatherer.of(...). Một Gatherer định nghĩa cách xử lý các phần tử và phát hành không có, một, hoặc nhiều đầu ra cho mỗi đầu vào.

Một Gatherer bao gồm:

  • Initializer → tạo trạng thái có thể thay đổi (tùy chọn).
  • Integrator → xử lý mỗi phần tử đầu vào cùng với trạng thái.
  • Finisher → bước cuối cùng khi stream kết thúc (tùy chọn).

Cách định nghĩa Gatherer

java Copy
static <T, R, S> Gatherer<T, S, R> of(
    Supplier<S> initializer,
    Integrator<T, S, R> integrator,
    BiConsumer<S, Downstream<? super R>> finisher
)

4. Ví dụ cụ thể

Ví dụ 1: Xóa các phần tử trùng lặp liên tiếp

java Copy
import java.util.stream.*;

public class GathererExample1 {
    public static void main(String[] args) {
        var result = Stream.of("A", "A", "B", "B", "C", "A", "A")
            .gather(Gatherers.distinctAdjacent()) // gatherer tích hợp sẵn
            .toList();

        System.out.println(result); // [A, B, C, A]
    }
}

Gatherers.distinctAdjacent() là một gatherer tích hợp sẵn giúp xóa các phần tử trùng lặp liên tiếp.

Ví dụ 2: Chia thành các nhóm (Cửa sổ)

java Copy
import java.util.stream.*;
import java.util.*;

public class GathererExample2 {
    public static void main(String[] args) {
        var result = Stream.iterate(1, n -> n + 1).limit(10)
            .gather(Gatherers.windowFixed(3)) // cửa sổ kích thước 3
            .toList();

        System.out.println(result);
        // [[1,2,3], [4,5,6], [7,8,9], [10]]
    }
}

Gatherers.windowFixed(3) nhóm các phần tử thành các danh sách có kích thước 3.

Ví dụ 3: Gatherer tùy chỉnh — Phát hành chỉ giá trị tăng dần

java Copy
import java.util.stream.*;
import java.util.function.*;

public class GathererExample3 {
    public static void main(String[] args) {
        var increasingGatherer = Gatherer.of(
            () -> new int[]{Integer.MIN_VALUE}, // bộ giữ trạng thái
            (state, elem, downstream) -> {
                if (elem > state[0]) {
                    state[0] = elem;
                    downstream.push(elem); // chỉ phát hành nếu lớn hơn
                }
                return true; // tiếp tục
            }
        );

        var result = Stream.of(1, 2, 2, 5, 3, 7, 6, 8)
            .gather(increasingGatherer)
            .toList();

        System.out.println(result); // [1, 2, 5, 7, 8]
    }
}

Ở đây, chúng ta đã viết một Gatherer tùy chỉnh chỉ phát hành khi chuỗi tăng dần.

5. Các Gatherers tích hợp sẵn trong Java

Java cung cấp một số gatherers sẵn có trong java.util.stream.Gatherers:

  • distinctAdjacent() → xóa các phần tử trùng lặp liên tiếp.
  • scanLeft(initial, op) → quét tích lũy (giống như reduce nhưng giữ lại các giá trị trung gian).
  • windowFixed(size) → nhóm các phần tử thành cửa sổ có kích thước cố định.
  • windowSliding(size) → cửa sổ trượt.
  • mapConcurrent(...) → ánh xạ đồng thời.

6. Tóm tắt

  • Gatherer = Biến đổi trung gian tùy chỉnh cho các stream.
  • Sử dụng khi bạn cần xử lý phần tử có trạng thái hoặc phức tạp.
  • Cung cấp các gatherers tích hợp sẵn (windowFixed, distinctAdjacent, v.v.).
  • Bạn cũng có thể định nghĩa gatherers tùy chỉnh cho các nhu cầu cụ thể trong miền của bạn.

Các lưu ý và mẹo khi sử dụng Gatherer

  • Hãy chắc chắn rằng bạn hiểu rõ về trạng thái mà bạn đang duy trì, vì điều này có thể ảnh hưởng đến hiệu suất và cách thức hoạt động của stream.
  • Kiểm tra hiệu suất của Gatherer trong các tình huống thực tế để đảm bảo rằng nó đáp ứng được yêu cầu của bạn.
  • Tránh lạm dụng Gatherer cho các tác vụ đơn giản, vì nó có thể gây ra overhead không cần thiết.

Câu hỏi thường gặp (FAQ)

1. Gatherer có giống với Collector không?

Trả lời: Có, nhưng Gatherer chủ yếu được sử dụng cho các phép toán trung gian trong khi Collector được sử dụng cho các phép toán cuối.

2. Tôi có thể định nghĩa Gatherer tùy chỉnh không?

Trả lời: Có, bạn có thể định nghĩa Gatherer tùy chỉnh theo nhu cầu cụ thể của bạn.

3. Gatherer có hiệu suất tốt không?

Trả lời: Gatherer có thể tốt cho các biến đổi phức tạp nhưng cần được kiểm tra trong các tình huống cụ thể để đánh giá hiệu suất.

4. Có bao nhiêu loại Gatherers tích hợp sẵn trong Java?

Trả lời: Java cung cấp một số gatherers tích hợp sẵn như distinctAdjacent, windowFixed, và scanLeft. Hãy khám phá chúng để biết thêm chi tiết.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào