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
boolean add(E e)
Thêm phần tử
evào Set nếu nó không tồn tại trong Set. Trả vềtruenếu phần tử được thêm vào, ngược lại trả vềfalse.Ví dụ:
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
boolean contains(Object o)
Kiểm tra xem Set có chứa phần tử
ohay không. Trả vềtruenếu Set chứa phần tử, ngược lại trả vềfalse.Ví dụ:
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
boolean remove(Object o)
Xóa phần tử
okhỏi Set nếu nó tồn tại. Trả vềtruenếu phần tử bị xóa, ngược lại trả vềfalse.Ví dụ:
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
int size()
Trả về số lượng phần tử trong Set.
Ví dụ:
Set<String> set = new HashSet<>(); set.add("A"); set.add("B"); int size = set.size(); System.out.println(size); // Output: 2
void clear()
Xóa tất cả các phần tử trong Set.
Ví dụ:
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
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ụ:
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]
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ụ:
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]
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ụ:
Set<String> treeSet = new TreeSet<>(); treeSet.add("B"); treeSet.add("A"); treeSet.add("C"); System.out.println(treeSet); // Output: [A, B, C]
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ụ:
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]
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ụ:
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
"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
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.
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.
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.
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.
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