Search…

Các Operations Hữu Ích của Stream<T>

Trần Thị Thu HiềnTrần Thị Thu Hiền
10/11/20205 min read
Các operations thường dùng của Stream<T> trong Java: Intermediate operations và Terminal operations.

Giới thiệu và cách sử dụng các operations hữu ích thường được sử dụng với Stream<T> trong Java.

Để dễ hiểu, Stream đơn thuần dùng để xử lý các vấn đề thường gặp ở danh sách như.

  • Duyệt danh sách.
  • Lọc bớt phần tử trong danh sách theo các tiêu chí.
  • Thêm hoặc cắt gọt các thuộc tính của các phần tử trong danh sách.
  • Sắp xếp danh sách.
  • ...

*Có thể nói, C# có LinQ thì Java có Stream.

Pipeline

Khi sử dụng Stream, bạn sẽ biết thêm về khái niệm Pipeline; có thể hiểu là 1 đường ống mà dòng chảy sẽ đi qua, trên đường ống này, có các operation (trong lập trình đơn thuần là các phương thức) sẽ làm thay đổi dòng chảy đó.

Operation có thể hiểu là các "đốt" mà ở đó dòng chảy sẽ thay đổi tính chất, có 2 loại operation là intermediate (đốt trên đường đi) và terminal (đốt cuối cùng).

Ví dụ có 1 danh sách tài khoản vừa truy vấn từ cơ sở dữ liệu và cần các thao tác sau trước khi trả về cho Frontend.

  • Cắt gọt các thuộc tính password, hoặc các thông tin bảo mật trong tất cả tài khoản - dùng map operation.
  • Tiếp tục lọc bớt tất cả tài khoản đã tạo trước năm 1990 - dùng filter operation.
  • Tiếp tục thêm 1 thuộc tính là fullName vào tất cả tài khoản bằng cách nối firstNamelastName trong tài khoản - dùng map operation.
Stream pipeline
Dòng chảy (stream) trong pipeline

Phân loại operation

Có 2 loại operation:

  • Intermediate operations: là các operations sau khi vận hành sẽ trả về 1 stream khác (lúc này dòng chảy vẫn chưa kết thúc).
  • Terminal operations: là các operations sau khi vận hành sẽ không trả về 1 stream mà có thể là mảng, collections, giá trị hoặc không trả về kết quả (void) (dòng chảy kết thúc tại đây để đưa ra kết quả cuối cùng có thể dùng được).

Các intermediate operations thường được đặt trước terminal operation và chúng sẽ không được thực thi cho đến khi có terminal operation.

Các Intermediate operations

filter

Phương thức dùng để lọc ra tất cả những phần tử trong stream có giá trị thỏa với điều kiện.

Collection<String> collection = Arrays.asList("aa" , "ba", "aa", "abb");
Stream<String> streamOfCollection = collection.stream();

streamOfCollection.filter(x -> x.startsWith("a"))
                  .forEach(x -> System.out.println(x + "; "));

//aa; aa; abb; 

map

Map giá trị của từng phần tử trong Stream vào một function mà chúng ta truyền vào và trả về giá trị mới.

Stream<String> streamOfArray = Stream.of("aa", "aa", "abb");

streamOfArray.map(x -> x + "c")
             .forEach(x -> System.out.print(x + "; "));

//aac; bac; aac; abbc;

flatmap

Biến đổi mỗi phần tử trong Stream (thường là những phần tử phức tạp như List, Set, Array) thành những phần tử đơn giản hơn.

List<Integer> s1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> s2 = Arrays.asList(6, 7, 8, 9, 10);
		
List<List<Integer>> s = Arrays.asList(s1, s2);

Stream<List<Integer>> stream = s.stream();
stream.flatMap(a -> a.stream())
		   .forEach(x -> System.out.print(x + "; "));

//1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 

distinct

Loại bỏ các phần tử trùng nhau trong Stream và trả về 1 Stream với các phần tử riêng biệt.

Stream<String> streamOfArray = Stream.of("aa", "ba", "aa", "abb");
        
streamOfArray.distinct()
             .forEach(x -> System.out.print(x + "; "));

//aa; ba; abb;

sorted

Trả về một Stream mà các phần tử đã được sắp xếp theo thứ tự.

Stream<String> streamOfArray = Stream.of("aa", "ba", "aa", "abb");
        
streamOfArray.sorted((a,b)->a.compareTo(b))
             .forEach(x -> System.out.print(x + "; "));

//aa; aa; abb; ba;

limit

Giới hạn số lượng phần tử tối đa trong Stream.

Stream<String> streamOfArray = Stream.of("aa", "ba", "aa", "abb");
        
streamOfArray.limit(3)
             .forEach(x -> System.out.print(x + "; "));

//aa; ba; aa; 

peek

Gần giống như forEach, peek cho phép duyệt qua tất cả các phần tử trong Stream và thực hiện các tính toán trên các phần tử đó đồng thời trả về 1 Stream.

Stream<Integer> streamOfArray = Stream.of(1, 2, 3, 4);
               
System.out.print("The number divisible by two is: ");
long count = streamOfArray.peek(x -> {if (x % 2 == 0) System.out.print(x + "; ");})
                          .count();
    
//The number divisible by two is: 2; 4; 

Các Terminal operations

forEach

Phương thức cho phép duyệt qua tất cả các phần từ trong Stream và thực hiện các tính toán từ các giá trị đó.

streamOfCollection.forEach(x -> System.out.println(x));

collect

Phương thức dùng để convert một Stream thành một List, Set, Map.

List<String> _stream = items.stream()
                            .filter( item -> item.startsWith("stdio") )
                            .collect(Collectors.toList());

toArray()

Convert 1 Stream sang Array.

Stream<Integer> intStream = Stream.of(1,2,3,4);

Integer[] intArray = intStream.toArray(Integer[]::new);

System.out.println(Arrays.toString(intArray)); 

//[1, 2, 3, 4]

count

Trả về số lượng phần tử của stream sau khi thực hiện filter.

Stream<Integer> streamOfArray = Stream.of(1, 2, 3, 4);

long count = streamOfArray.count();

System.out.println("Number of elements in the array is: " + count);

Number of elements in the array is: 4

min

Trả về giá trị nhỏ nhất của Stream.

Stream.of(1, 5, 4)
      .min()
      .ifPresent(System.out::println); 

//1

max

Trả về giá trị lớn nhất của Stream.

Stream.of(1, 5, 4)
      .max()
      .ifPresent(System.out::println); 

//5

reduce

Giúp làm giảm các phần tử của stream về một giá trị.

Stream<Integer> streamOfArray = Stream.of(1, 2 , 3, 4);
        
Integer result  = streamOfArray.reduce((a, b) -> a + b)
                               .get();

System.out.print(result);

//10

Vì sao sử dụng Stream?

Stream cũng chỉ là 1 cách tiếp cận, 1 phương pháp code trong lập trình với Java và các danh sách. Tuy nhiên, nó tiện lợi, như những code mẫu về các operations trên, bạn không cần thiết phải for (int i = 0; i < accounts.length(); i++) mọi lúc mọi nơi và thực tế của các trường hợp ứng dụng, vòng lặp for biến i hay accounts.length() cũng không đóng góp vai trò gì ngoài việc kiểm soát chỉ số, số lần lặp nhưng lại chiếm dụng thời gian code, sự quan tâm và làm cho code dài hơn.

IO Stream

IO Stream Co., Ltd

developer@iostream.co
383/1 Quang Trung, ward 10, Go Vap district, Ho Chi Minh city
Business license number: 0311563559 issued by the Department of Planning and Investment of Ho Chi Minh City on February 23, 2012

©IO Stream, 2013 - 2025