×

Làm việc với luồng (Stream) để xử lý dữ liệu trong Java

Trong lập trình Java, việc xử lý dữ liệu một cách hiệu quả là một yếu tố quan trọng đảm bảo hiệu năng của ứng dụng. Một trong những phương pháp vượt trội để đạt được điều này là sử dụng các luồng (Stream). Đây là một tính năng mạnh mẽ được giới thiệu từ Java 8, giúp lập trình viên làm việc với các tập hợp dữ liệu một cách dễ dàng và hiệu quả. Bài viết này sẽ giới thiệu cách làm việc với luồng trong Java để bạn có thể xử lý dữ liệu mượt mà hơn.

Định Nghĩa Luồng (Stream)

Luồng là một trình tự các phần tử có thể được thao tác liên tục và dễ dàng. Nó cho phép bạn thực hiện các thao tác như lọc, sắp xếp và biến đổi dữ liệu một cách dễ dàng. Các thao tác này có thể được thực hiện song song, giúp tối ưu hóa tài nguyên và hiệu suất.

Cách Khởi Tạo Luồng

Một luồng có thể được khởi tạo từ nhiều nguồn khác nhau, chẳng hạn như từ các danh sách (List), mảng (Array), hoặc thậm chí từ các tập tin hay thư mục. Dưới đây là một số ví dụ về cách khởi tạo luồng:

List<String> stringList = Arrays.asList("one", "two", "three");
Stream<String> streamFromList = stringList.stream();

String[] stringArray = {"one", "two", "three"};
Stream<String> streamFromArray = Arrays.stream(stringArray);

Stream<String> streamOfStrings = Stream.of("one", "two", "three");

Các Toán Tử Trung Gian (Intermediate Operations)

Các toán tử trung gian sử dụng để biến đổi hoặc lọc các phần tử trong luồng mà không thay đổi kích cỡ của luồng. Một số toán tử trung gian phổ biến bao gồm:

  • filter(Predicate<T> predicate): Lọc các phần tử thỏa điều kiện.
  • map(Function<T, R> mapper): Biến đổi các phần tử từ kiểu này sang kiểu khác.
  • flatMap(Function<T, Stream<R>> mapper): Biến đổi các phần tử và sau đó ghép các luồng con thành một luồng duy nhất.
  • sorted(): Sắp xếp các phần tử theo trật tự tự nhiên hoặc theo một Comparator.

Ví dụ:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> evenNumbers = numbers.stream()
                                     .filter(num -> num % 2 == 0)
                                     .sorted();

Các Toán Tử Kết Thúc (Terminal Operations)

Các toán tử kết thúc sẽ kết thúc luồng và trả về kết quả, hoặc thực hiện một số hành động nào đó. Một số ví dụ phổ biến bao gồm:

  • collect(Collector<T, A, R>): Thu thập các phần tử thành một cấu trúc dữ liệu khác, chẳng hạn như List, Set, hoặc Map.
  • forEach(Consumer<T> action): Thực hiện một hành động đối với mỗi phần tử.
  • reduce(BinaryOperator<T> accumulator): Gộp các phần tử lại với nhau theo một cách nào đó.
  • count(): Đếm số phần tử trong luồng.

Ví dụ:

List<String> collectedList = streamFromList
                             .filter(s -> s.startsWith("t"))
                             .collect(Collectors.toList());

streamFromList.forEach(System.out::println);

int sum = numbers.stream()
                 .reduce(0, Integer::sum);

Xử Lý Song Song

Java cung cấp các luồng song song để tối ưu hóa hiệu suất, bằng cách tận dụng đa luồng. Ví dụ:

List<Integer> bigList = Arrays.asList(1, 2, 3, ..., 1000);
long count = bigList.parallelStream()
                    .filter(num -> num % 2 == 0)
                    .count();

Kết Luận

Sử dụng luồng để xử lý dữ liệu trong Java mang lại nhiều lợi ích, từ việc dễ dàng thao tác dữ liệu tới khả năng tối ưu hóa hiệu suất thông qua xử lý song song. Bằng cách hiểu rõ và áp dụng các phương pháp làm việc với luồng, bạn có thể viết mã dễ đọc và bảo trì hơn, đồng thời cải thiện hiệu năng cho ứng dụng của mình.

Comments