# Bài 6: Giới thiệu về Stream trong Java 8

### **Mục tiêu:**

* Giúp người đọc hiểu khái niệm và cách sử dụng **Stream** trong Java 8.
    
* Giải thích các đặc điểm chính của Stream, cách tạo Stream và các thao tác cơ bản trên Stream.
    
* Cung cấp ví dụ minh họa chi tiết để áp dụng vào thực tế.
    

---

### **Dàn ý chi tiết:**

#### **1\. Stream là gì?**

Stream là một tính năng mới trong Java 8, giúp xử lý dữ liệu một cách hiệu quả và ngắn gọn hơn. Stream không phải là cấu trúc dữ liệu mà là một API để thao tác trên các tập dữ liệu (như Collection, Array, v.v.).

1. **Đặc điểm của Stream:**
    
    * **Lazy evaluation (đánh giá lười):** Các thao tác trên Stream chỉ được thực hiện khi cần thiết (khi gọi terminal operation).
        
    * **Không lưu trữ dữ liệu:** Stream không lưu trữ dữ liệu, nó chỉ đại diện cho một luồng dữ liệu từ nguồn (source).
        
    * **Không thay đổi dữ liệu gốc:** Các thao tác trên Stream không làm thay đổi dữ liệu gốc mà tạo ra một Stream mới.
        
    * **Hỗ trợ thao tác song song (parallel processing):** Stream có thể xử lý dữ liệu song song để tăng hiệu suất.
        
2. **Ví dụ minh họa:**
    
    * Sử dụng Stream để tính tổng các số chẵn trong một danh sách:
        
        ```java
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        int sum = numbers.stream()
                         .filter(n -> n % 2 == 0)
                         .mapToInt(Integer::intValue)
                         .sum();
        System.out.println(sum); // 12
        ```
        

---

#### **2\. Tạo Stream**

Stream có thể được tạo từ nhiều nguồn khác nhau như Collection, Array, hoặc giá trị tĩnh.

1. **Tạo Stream từ Collection:**
    
    * Sử dụng phương thức `stream()` hoặc `parallelStream()`:
        
        ```java
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        Stream<String> stream = names.stream();
        ```
        
2. **Tạo Stream từ Array:**
    
    * Sử dụng [`Arrays.stream`](http://Arrays.stream)`()`:
        
        ```java
        int[] numbers = {1, 2, 3, 4, 5};
        IntStream stream = Arrays.stream(numbers);
        ```
        
3. **Tạo Stream từ giá trị tĩnh:**
    
    * Sử dụng `Stream.of()`:
        
        ```java
        Stream<String> stream = Stream.of("Java", "Python", "C++");
        ```
        
4. **Tạo Stream từ hàm sinh (Supplier):**
    
    * Sử dụng `Stream.generate()`:
        
        ```java
        Stream<Double> randomStream = Stream.generate(Math::random).limit(5);
        randomStream.forEach(System.out::println);
        ```
        

---

#### **3\. Các thao tác cơ bản trên Stream**

Stream cung cấp hai loại thao tác chính: **intermediate operations** (thao tác trung gian) và **terminal operations** (thao tác kết thúc).

1. **Intermediate operations:**
    
    * Là các thao tác trung gian, trả về một Stream mới.
        
    * Các thao tác này không thực thi ngay mà chỉ được thực thi khi gọi terminal operation.
        
    * Ví dụ:
        
        * `filter(Predicate<T>)`: Lọc các phần tử thỏa mãn điều kiện.
            
            ```java
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
            Stream<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0);
            ```
            
        * `map(Function<T, R>)`: Biến đổi từng phần tử.
            
            ```java
            List<String> names = Arrays.asList("Alice", "Bob");
            Stream<Integer> nameLengths = names.stream().map(String::length);
            ```
            
        * `sorted()`: Sắp xếp các phần tử.
            
            ```java
            List<Integer> numbers = Arrays.asList(5, 3, 1, 4, 2);
            Stream<Integer> sortedNumbers = numbers.stream().sorted();
            ```
            
2. **Terminal operations:**
    
    * Là các thao tác kết thúc, trả về kết quả hoặc thực hiện hành động cuối cùng.
        
    * Sau khi gọi terminal operation, Stream không thể sử dụng lại.
        
    * Ví dụ:
        
        * `forEach(Consumer<T>)`: Thực hiện hành động trên từng phần tử.
            
            ```java
            List<String> names = Arrays.asList("Alice", "Bob");
            names.stream().forEach(System.out::println);
            ```
            
        * `collect(Collector<T, A, R>)`: Thu thập kết quả vào một Collection.
            
            ```java
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
            List<Integer> evenNumbers = numbers.stream()
                                               .filter(n -> n % 2 == 0)
                                               .collect(Collectors.toList());
            ```
            
        * `reduce(BinaryOperator<T>)`: Kết hợp các phần tử thành một giá trị duy nhất.
            
            ```java
            List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
            int sum = numbers.stream().reduce(0, Integer::sum);
            ```
            

---

### **Ví dụ tổng hợp**

1. **Sử dụng Stream để lọc và biến đổi dữ liệu:**
    
    ```java
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    List<String> result = names.stream()
                               .filter(name -> name.length() > 4) // Lọc tên có độ dài > 4
                               .map(String::toUpperCase)         // Chuyển đổi thành chữ in hoa
                               .collect(Collectors.toList());    // Thu thập kết quả
    System.out.println(result); // [ALICE, CHARLIE, DAVID]
    ```
    
2. **Sử dụng Stream để tính tổng các số chẵn:**
    
    ```java
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    int sum = numbers.stream()
                     .filter(n -> n % 2 == 0) // Lọc số chẵn
                     .mapToInt(Integer::intValue) // Chuyển đổi thành IntStream
                     .sum(); // Tính tổng
    System.out.println(sum); // 12
    ```
    

---

### **Tổng kết**

* **Stream** là một API mạnh mẽ trong Java 8, giúp xử lý dữ liệu một cách hiệu quả và ngắn gọn.
    
* Stream có hai loại thao tác chính: **intermediate operations** (thao tác trung gian) và **terminal operations** (thao tác kết thúc).
    
* Hiểu rõ cách tạo và sử dụng Stream là nền tảng để áp dụng các tính năng nâng cao như **Parallel Stream** và **Collectors** trong các bài viết tiếp theo.
    

---

### **Bài tập thực hành**

1. Tạo một Stream từ một danh sách các chuỗi và lọc ra các chuỗi có độ dài lớn hơn 5. In kết quả ra màn hình.
    
2. Sử dụng Stream để tính tổng bình phương của các số nguyên từ một danh sách.
    
3. Tạo một Stream từ một mảng số nguyên và sắp xếp chúng theo thứ tự tăng dần. In kết quả ra màn hình.
    

---

### **Gợi ý đáp án**

1. **Lọc chuỗi có độ dài lớn hơn 5:**
    
    ```java
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
    names.stream()
         .filter(name -> name.length() > 5)
         .forEach(System.out::println); // Charlie
    ```
    
2. **Tính tổng bình phương:**
    
    ```java
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    int sumOfSquares = numbers.stream()
                              .map(n -> n * n)
                              .reduce(0, Integer::sum);
    System.out.println(sumOfSquares); // 55
    ```
    
3. **Sắp xếp mảng số nguyên:**
    
    ```java
    int[] numbers = {5, 3, 1, 4, 2};
    Arrays.stream(numbers)
          .sorted()
          .forEach(System.out::println); // 1, 2, 3, 4, 5
    ```
    

---

### **Kết luận**

Bài viết này đã giới thiệu chi tiết về **Stream** trong Java 8, từ khái niệm cơ bản đến cách tạo và sử dụng các thao tác trên Stream. Hiểu rõ Stream là nền tảng để áp dụng các tính năng nâng cao như **Parallel Stream** và **Collectors** trong các bài viết tiếp theo.
