post-image

Stream Xử Lý Dữ Liệu Thế Nào Trong Java

1. Tổng quan

Sự ra đời của Java 8 đã làm thay đổi thói quen sử dụng của nhiều người lập trình đối với các Collection so với các phiên bản Java trước. Một trong những điều mới mẻ nhất chính là thêm các phương thức mở rộng vào các interface có sẵn (Collection, List, Iterable) cùng các lớp abstraction như Stream để thực hiện các phép toán tổng hợp trên tập dữ liệu và không làm thay đổi dữ liệu cũ. Trong bài viết này chúng ta sẽ tìm hiểu về Stream là gì và các đặc điểm của nó.

1. Stream là gì?

Stream được giới thiệu từ bản java 8 và được nằm trong gói java.util.stream. Stream đại diện cho một chuỗi các giá trị và phục vụ nhiều chức năng tổng hợp để thao tác với dữ liệu như duyệt, tìm giá trị lớn (bé) nhất, sorting, filter, hay limit. Stream có rất nhiều methods đa dạng và chúng có thể được kết hợp với nhau, một vài trong số chúng trả về kiểu Stream được gọi là intermediate operations và một số khác trả về kiểu non-stream(int, long, list, arrays,…) được gọi là terminal operations.

Một ví dụ là khi bạn muốn sắp xếp các giá trị trong một mảng thì thay vì phải viết các vòng lặp for hoặc while, bạn chỉ cần dùng một hay một vài phương thức của stream một cách ngắn gọn và đơn giản.

Các tính chất của Stream:

  • Stream không phải là một cấu trúc dữ liệu, đầu vào của Stream có thể là các Colllections (Arraylist, Set, LinkedList,…), Arrays và các kênh Input/Output.
  • Stream không làm thay đổi dữ liệu gốc mà chỉ trả về kết quả thông qua các methods.
  • Về cơ bản các method của stream được phân làm 2 loại là hoạt động trung gian(Intermediate Operation) và hoạt động đầu cuối(Terminal Operation).

Sau đây là một ví dụ về Stream:

Collection<Integer> collection = Arrays.asList(1,2,3);
Stream<Integer> streamOfCollection = collection.stream();

2. Các methods trong Stream

2.1 Terminal Operations

Các hoạt động đầu cuối này gồm các method có kiểu trả về khác stream như kiểu nguyên thủy(primitive), đối tượng(String, Integer,…) hay collections.

Collect method: Dùng để trả về kết quả của stream dưới dạng List hoặc Set.

public static void main(String[] args) {
      List<String> strings = Arrays.asList("args", "", "code", "learn", "...");
      List<String> filter = strings.stream().collect(Collectors.toList());
      System.out.println(filter);
  }

Ouput:

forEach method: Dùng để duyệt qua mọi phần tử trong stream.

public static void main(String[] args) {
      List<String> strings = Arrays.asList("args", "", "code", "learn", "...");
      strings.stream().forEach(s -> System.out.println(s));
  }

Output:

Reduce method: Reduce() method với 1 trong 2 tham số truyền vào là method reference, dùng dể kết hợp các phần tử thành một giá trị đơn cùng kiểu với dữ liệu ban đầu.

public static void main(String[] args) {
      List<String> strings = Arrays.asList("args", "", "code", "learn", "...");
      String result = strings.stream().reduce("-", String::concat);
      System.out.println(result);
  }

Tham số đầu tiên là chỉ giá trị ban đầu, tham số thứ hai là một method reference String::concat nhằm mục đích ghép các phần tử của Stream với nhau.

Output:

Max, Min method: Trả về giá trị lớn nhất hoặc bé nhất trong các phần tử.

public static void main(String[] args) {
      List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
      Integer maxx = list.stream().max(Integer::compare).get();
      Integer minn = list.stream().min(Integer::compare).get();
      System.out.println("Max: "+maxx+"\nMin: "+minn);
  }

Output:

2.2 Intermediate Operations

Mỗi hoạt động trung gian này được thực thi một cách riêng biệt và có kết quả trả về cũng là một Stream do đó ta có thể kết hợp nhiều intermediate methods với nhau. Sau đây là một vài intermediate method:

Distinct method: Được dùng để loại bỏ các phần tử trùng lặp.

public static void main(String[] args) {
      List<Integer> list = Arrays.asList(1,2,2,2,2,3,4,5);
      list.stream().distinct().forEach(System.out::println);
  }

Output: các số 2 trùng lặp đã được loại bỏ

Map method: Map method được sử dụng để trả về một stream mà ở đó các phần tử đã  được thay đổi theo cách người dùng tự định nghĩa.

public static void main(String[] args) {
      List<Integer> list = Arrays.asList(1,2,2,2,2,3,4,5);
      list.stream().distinct().map(i -> i*i).forEach(System.out::println);
  }

Output: bình phương của các phần tử

Filter method: Dùng để lọc và xóa bỏ các phần tử với điều kiện do người dùng định nghĩa.

public static void main(String[] args) {
      List<Integer> list = Arrays.asList(1,2,2,2,2,3,4,5);
      list.stream().distinct().map(i->i*i).forEach(System.out::println);

  }

Output: các phần tử lớn hơn 2

Sorted method: Dùng cho việc sắp xếp các phần tử.

public static void main(String[] args) {
      List<Integer> list = Arrays.asList(4,3,2,1,0,3,4,5);
      list.stream().sorted().forEach(System.out::println);
  }

Output: các phần tử được sắp xếp theo thứ tự tăng dần.

Limit method: limit(n) với tham số đầu vào là số nguyên không âm n nó sẽ trả về một stream chứa n phần tử đầu tiên.

public static void main(String[] args) {
      List<Integer> list = Arrays.asList(4,3,2,1,0,3,4,5);
      list.stream().limit(3).forEach(System.out::println);
  }

Output: lấy ra 3 phần tử đầu tiên.

Skip method: skip(n) với tham số truyền vào là số nguyên không âm n nó sẽ trả về các phần tử còn lại đằng sau n phần tử đầu tiên.

    List<Integer> list = Arrays.asList(4,3,2,1,0,3,4,5);
      list.stream().skip(3).forEach(System.out::println);
  }

Output: In ra các phần tử trừ 3 phần tử đầu tiên.

3. Parallel Stream

ParallelStream là một sự thay thế của stream để phục vụ cho việc xử lý song song các phần tử. Kết quả của đoạn code sau thể hiện sự khác nhau giữa Stream và Parallel Stream.

public static void main(String[] args) {
      List<String> strings = Arrays.asList("code", "learn", "...");
      String streamString = strings.stream().reduce(" I'm-", String::concat);
      String parallelString = strings.parallelStream().reduce(" I'm-", String::concat);
      System.out.println("Stream: "+streamString +"\nParallelStream: "+parallelString);
  }

Output:

Các bạn có thể thấy rằng parallelStream() đã thực hiện intermediate method song song với tất cả các phần tử cùng một lúc nên trước các phần tử đều có chuỗi kí tự ” I’m”.

Chú ý:

  • Thực chất các intermediate method không hoạt động khi chúng ta gọi đến mà chỉ thực thi khi có một terminal method kết thúc stream của nó.
  • Một luồng hay một stream có thể không có hoặc có nhiều intermediate method kết hợp với nhau nhưng chỉ bao gồm một terminal method để xử lý stream được trả về từ các intermediate method bên trên.
  • Stream được sử dụng để trả về kết quả các phần tử sau khi được xử lý thông qua các method mà không làm thay đổi giá trị các phần tử gốc.

Kết

Qua bài viết mình và các bạn đã tìm hiểu qua về Stream trong java, thực tế stream còn rất nhiều ứng dụng khác mà các bạn có thể dễ dàng tìm hiểu thêm trên internet. Nếu bài viết có ý nghĩa hãy để lại đánh giá cũng như comment giúp phát triển bài viết tốt hơn. Cảm ơn bạn đọc!

Trả lời

Email của bạn sẽ không được hiển thị công khai.