# Bài 3: Set Interface và Các Implementations

#### I. Giới Thiệu Về Set Interface

**Set** là một interface trong Java Collection Framework đại diện cho một tập hợp các phần tử không trùng lặp. Không giống như List, Set không duy trì thứ tự chèn phần tử. Set được sử dụng khi cần lưu trữ các phần tử duy nhất và tránh trùng lặp.

#### II. Các Phương Thức Chính Trong Set Interface

1. **boolean add(E e)**
    
    * Thêm phần tử `e` vào Set nếu nó không tồn tại trong Set. Trả về `true` nếu phần tử được thêm vào, ngược lại trả về `false`.
        
    * Ví dụ:
        
        ```java
        Set<String> set = new HashSet<>();
        boolean isAdded = set.add("A");
        System.out.println(isAdded); // Output: true
        isAdded = set.add("A");
        System.out.println(isAdded); // Output: false
        ```
        
2. **boolean contains(Object o)**
    
    * Kiểm tra xem Set có chứa phần tử `o` hay không. Trả về `true` nếu Set chứa phần tử, ngược lại trả về `false`.
        
    * Ví dụ:
        
        ```java
        Set<String> set = new HashSet<>();
        set.add("A");
        boolean contains = set.contains("A");
        System.out.println(contains); // Output: true
        contains = set.contains("B");
        System.out.println(contains); // Output: false
        ```
        
3. **boolean remove(Object o)**
    
    * Xóa phần tử `o` khỏi Set nếu nó tồn tại. Trả về `true` nếu phần tử bị xóa, ngược lại trả về `false`.
        
    * Ví dụ:
        
        ```java
        Set<String> set = new HashSet<>();
        set.add("A");
        boolean isRemoved = set.remove("A");
        System.out.println(isRemoved); // Output: true
        isRemoved = set.remove("B");
        System.out.println(isRemoved); // Output: false
        ```
        
4. **int size()**
    
    * Trả về số lượng phần tử trong Set.
        
    * Ví dụ:
        
        ```java
        Set<String> set = new HashSet<>();
        set.add("A");
        set.add("B");
        int size = set.size();
        System.out.println(size); // Output: 2
        ```
        
5. **void clear()**
    
    * Xóa tất cả các phần tử trong Set.
        
    * Ví dụ:
        
        ```java
        Set<String> set = new HashSet<>();
        set.add("A");
        set.add("B");
        set.clear();
        System.out.println(set.size()); // Output: 0
        ```
        

#### III. Các Lớp Triển Khai Set Interface

1. **HashSet**
    
    * **Đặc điểm:**
        
        * Dựa trên bảng băm (hash table).
            
        * Không duy trì thứ tự các phần tử.
            
        * Hiệu suất tốt cho các thao tác thêm, xóa, và tìm kiếm phần tử (O(1)).
            
    * **Khi nào sử dụng:** Khi cần một tập hợp không trùng lặp và không quan tâm đến thứ tự phần tử.
        
    * **Ví dụ:**
        
        ```java
        Set<String> hashSet = new HashSet<>();
        hashSet.add("A");
        hashSet.add("B");
        hashSet.add("A"); // Không thêm phần tử trùng lặp
        System.out.println(hashSet); // Output: [A, B]
        ```
        
2. **LinkedHashSet**
    
    * **Đặc điểm:**
        
        * Kế thừa từ HashSet và sử dụng danh sách liên kết kép để duy trì thứ tự chèn.
            
        * Hiệu suất tương tự như HashSet nhưng có thêm chi phí để duy trì thứ tự (O(1)).
            
    * **Khi nào sử dụng:** Khi cần một tập hợp không trùng lặp nhưng duy trì thứ tự chèn.
        
    * **Ví dụ:**
        
        ```java
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("A");
        linkedHashSet.add("B");
        linkedHashSet.add("A"); // Không thêm phần tử trùng lặp
        System.out.println(linkedHashSet); // Output: [A, B]
        ```
        
3. **TreeSet**
    
    * **Đặc điểm:**
        
        * Dựa trên cây nhị phân tìm kiếm (Red-Black tree).
            
        * Duy trì thứ tự các phần tử theo thứ tự tự nhiên hoặc theo bộ so sánh (comparator) tùy chỉnh.
            
        * Hiệu suất cho các thao tác thêm, xóa, và tìm kiếm là O(log n).
            
    * **Khi nào sử dụng:** Khi cần một tập hợp không trùng lặp và duy trì thứ tự sắp xếp.
        
    * **Ví dụ:**
        
        ```java
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("B");
        treeSet.add("A");
        treeSet.add("C");
        System.out.println(treeSet); // Output: [A, B, C]
        ```
        
4. **EnumSet**
    
    * **Đặc điểm:**
        
        * Tối ưu hóa cho các tập hợp enum.
            
        * Hiệu suất rất cao cho các thao tác thêm, xóa, và tìm kiếm (O(1)).
            
        * Chỉ hoạt động với các phần tử kiểu enum.
            
    * **Khi nào sử dụng:** Khi cần một tập hợp các giá trị enum.
        
    * **Ví dụ:**
        
        ```java
        enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
        Set<Day> enumSet = EnumSet.of(Day.MONDAY, Day.WEDNESDAY, Day.FRIDAY);
        System.out.println(enumSet); // Output: [MONDAY, WEDNESDAY, FRIDAY]
        ```
        
5. **CopyOnWriteArraySet**
    
    * **Đặc điểm:**
        
        * Dựa trên CopyOnWriteArrayList, hỗ trợ đa luồng.
            
        * Hiệu suất tốt khi đọc nhiều hơn ghi.
            
        * Mỗi khi có thay đổi (thêm/xóa), danh sách sẽ được sao chép.
            
    * **Khi nào sử dụng:** Khi cần một tập hợp đồng bộ mà chủ yếu là thao tác đọc trong môi trường đa luồng.
        
    * **Ví dụ:**
        
        ```java
        Set<String> cowArraySet = new CopyOnWriteArraySet<>();
        cowArraySet.add("A");
        cowArraySet.add("B");
        cowArraySet.add("A"); // Không thêm phần tử trùng lặp
        System.out.println(cowArraySet); // Output: [A, B]
        ```
        

#### IV. So Sánh Hiệu Năng Và Trường Hợp Sử Dụng

| Lớp | Thứ Tự Phần Tử | Hiệu Suất Thêm/Xóa | Hiệu Suất Tìm Kiếm | Đồng Bộ | Khi Nào Sử Dụng |
| --- | --- | --- | --- | --- | --- |
| HashSet | Không | Rất nhanh (O(1)) | Rất nhanh (O(1)) | Không | Khi không cần thứ tự |
| LinkedHashSet | Theo thứ tự chèn | Nhanh (O(1)) | Nhanh (O(1)) | Không | Khi cần duy trì thứ tự chèn |
| TreeSet | Theo thứ tự sắp xếp | Trung bình (O(log n)) | Trung bình (O(log n)) | Không | Khi cần sắp xếp phần tử |
| EnumSet | Theo thứ tự định nghĩa enum | Rất nhanh (O(1)) | Rất nhanh (O(1)) | Không | Khi làm việc với enum |
| CopyOnWriteArraySet | Theo thứ tự chèn | Chậm khi ghi | Nhanh khi đọc | Có | Khi cần đồng bộ và đọc nhiều hơn ghi |

#### V. Kết Luận

Trong bài viết này, chúng ta đã khám phá Set interface và các lớp triển khai của nó. Hiểu rõ sự khác nhau và đặc điểm của từng loại Set sẽ giúp bạn chọn lựa cấu trúc dữ liệu phù hợp cho các tình huống cụ thể trong ứng dụng của mình.

#### VI. Tài Liệu Tham Khảo

* Java SE Documentation: [Java Collections Framework](https://docs.oracle.com/javase/8/docs/technotes/guides/collections/index.html)
    
* "Effective Java" by Joshua Bloch: Các nguyên tắc và best practices khi sử dụng Collection Framework.
    

### Bài Tập Thực Hành

1. Tạo một chương trình Java sử dụng HashSet để lưu trữ các từ duy nhất trong một đoạn văn và in ra danh sách các từ này.
    
2. Tạo một chương trình Java sử dụng LinkedHashSet để lưu trữ và in ra danh sách các phần tử theo thứ tự chèn.
    
3. Tạo một chương trình Java sử dụng TreeSet để lưu trữ và sắp xếp các số nguyên, sau đó in ra danh sách các số này theo thứ tự tăng dần.
    
4. Tạo một chương trình Java sử dụng EnumSet để lưu trữ các ngày trong tuần và in ra danh sách các ngày này.
    
5. Tạo một chương trình Java sử dụng CopyOnWriteArraySet để lưu trữ các yêu cầu HTTP trong một server
