
Chào các bạn, lại là anh Creyt đây! Hôm nay, chúng ta sẽ "bung lụa" với một chủ đề mà anh dám cá là bạn nào đã và đang "lướt" qua Java đều phải "đụng chạm" tới: Collections Framework. Nghe tên thì có vẻ "hàn lâm" đúng không? Nhưng thực ra, nó chính là "tủ đồ thần kỳ" giúp chúng ta sắp xếp, quản lý mọi thứ trong code một cách "ngon ơ" nhất. Tưởng tượng xem, nếu code của bạn là một bữa tiệc, thì Collections Framework chính là đội ngũ phục vụ chuyên nghiệp, đảm bảo mọi món ăn (dữ liệu) đều được bày biện đúng chỗ, dễ tìm, dễ dùng. Mà Gen Z mình thì thích nhanh gọn, hiệu quả, đúng không nào?
Vậy, Collections Framework là cái gì mà "ghê gớm" vậy? Nói một cách "đời thường", nó là một tập hợp các giao diện (interfaces) và lớp (classes) trong Java, được thiết kế để lưu trữ và thao tác với một nhóm các đối tượng (objects). Thay vì phải tự tay "xây nhà" để chứa từng loại dữ liệu, Java đã "xây sẵn" cho bạn cả một "khu đô thị" với nhiều kiểu nhà khác nhau, mỗi kiểu phục vụ một mục đích riêng.
Nó giống như việc bạn có một "kho đồ" mà trong đó:
- List (Danh sách): Giống như một cuốn sổ ghi chép các việc cần làm (to-do list) hoặc danh sách nhạc của bạn. Mọi thứ có thứ tự, bạn có thể thêm trùng lặp, và quan trọng là, bạn biết chính xác vị trí của từng món đồ. "Anh ơi, bài số 3 trong playlist là bài gì?" – List trả lời được ngay!
- Set (Tập hợp): Đây là "hội nhóm" của những người "độc nhất vô nhị". Mỗi thành viên chỉ có mặt một lần duy nhất. Giống như danh sách khách mời VIP không được trùng tên. Bạn không quan tâm thứ tự, chỉ cần biết "người này có trong danh sách hay không?".
- Map (Bản đồ/Từ điển): Cái này thì "chất" khỏi bàn! Nó giống như một cuốn từ điển hoặc danh bạ điện thoại. Mỗi "tên" (key) sẽ đi kèm với một "số điện thoại" (value) tương ứng. Bạn muốn tìm số của "anh Creyt"? Chỉ cần gõ "Creyt" là ra ngay! Key là duy nhất, nhưng value thì có thể trùng.
- Queue (Hàng đợi): Giống như hàng người đang xếp hàng mua vé xem concert của idol vậy. Ai đến trước thì được phục vụ trước (First-In, First-Out - FIFO). Hoặc có những loại Queue ưu tiên, ai "VIP" hơn thì được vào trước.
Mục đích chính của nó? Đơn giản là để bạn quản lý "đống data" một cách hiệu quả, dễ dàng thêm, xóa, tìm kiếm mà không phải "đau đầu" nghĩ cách tối ưu từ đầu.
Để các bạn không bị "lú", anh Creyt sẽ "show" ngay vài ví dụ code "sương sương" để các bạn hình dung nhé. Đây là những "công thức nấu ăn" cơ bản nhất để "chế biến" dữ liệu với Collections.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Queue;
import java.util.LinkedList; // LinkedList implements both List and Queue
public class CollectionsDemo {
public static void main(String[] args) {
// 1. List: Danh sách có thứ tự, cho phép trùng lặp
// Ví dụ: Danh sách các tựa game yêu thích
List<String> gameList = new ArrayList<>();
gameList.add("Cyberpunk 2077");
gameList.add("The Witcher 3");
gameList.add("Elden Ring");
gameList.add("The Witcher 3"); // Cho phép trùng lặp
System.out.println("--- Danh sách Game (List) ---");
System.out.println("Các game hiện có: " + gameList); // In ra cả danh sách
System.out.println("Game thứ 2 trong danh sách: " + gameList.get(1)); // Lấy theo chỉ mục
gameList.remove("Cyberpunk 2077"); // Xóa một game
System.out.println("Danh sách sau khi xóa Cyberpunk: " + gameList);
System.out.println("-----------------------------\n");
// 2. Set: Tập hợp không có thứ tự, không cho phép trùng lặp
// Ví dụ: Danh sách bạn bè trên mạng xã hội (mỗi người chỉ có 1 lần)
Set<String> friendSet = new HashSet<>();
friendSet.add("An");
friendSet.add("Binh");
friendSet.add("Chau");
friendSet.add("An"); // Thêm trùng lặp sẽ bị bỏ qua
System.out.println("--- Danh sách Bạn bè (Set) ---");
System.out.println("Các bạn bè hiện có: " + friendSet); // Thứ tự có thể không giống lúc thêm
System.out.println("An có trong danh sách không? " + friendSet.contains("An"));
friendSet.remove("Binh"); // Xóa một người bạn
System.out.println("Danh sách sau khi xóa Binh: " + friendSet);
System.out.println("-----------------------------\n");
// 3. Map: Lưu trữ dữ liệu dưới dạng cặp Key-Value (Từ điển)
// Ví dụ: Danh sách điểm của sinh viên (Tên là Key, Điểm là Value)
Map<String, Double> studentScores = new HashMap<>();
studentScores.put("Hoang", 8.5);
studentScores.put("Mai", 9.0);
studentScores.put("Quang", 7.8);
studentScores.put("Hoang", 9.2); // Key "Hoang" đã có, value mới sẽ ghi đè value cũ
System.out.println("--- Điểm Sinh viên (Map) ---");
System.out.println("Điểm của các sinh viên: " + studentScores);
System.out.println("Điểm của Mai: " + studentScores.get("Mai")); // Lấy điểm của Mai
studentScores.remove("Quang"); // Xóa sinh viên Quang
System.out.println("Điểm sau khi Quang bỏ học: " + studentScores);
System.out.println("-----------------------------\n");
// 4. Queue: Hàng đợi (First-In, First-Out)
// Ví dụ: Hàng đợi xử lý tin nhắn
Queue<String> messageQueue = new LinkedList<>();
messageQueue.offer("Tin nhắn từ Sơn"); // Thêm vào cuối hàng đợi
messageQueue.offer("Tin nhắn từ Thảo");
messageQueue.offer("Tin nhắn từ Nam");
System.out.println("--- Hàng đợi Tin nhắn (Queue) ---");
System.out.println("Hàng đợi hiện tại: " + messageQueue);
System.out.println("Tin nhắn đầu tiên: " + messageQueue.peek()); // Xem phần tử đầu mà không xóa
System.out.println("Xử lý tin nhắn: " + messageQueue.poll()); // Lấy và xóa phần tử đầu
System.out.println("Hàng đợi sau khi xử lý: " + messageQueue);
System.out.println("-----------------------------\n");
}
}
Được rồi, "có nghề" rồi thì phải biết vài "chiêu" để code mình "xịn" hơn chứ nhỉ? Anh Creyt có vài mẹo nhỏ mà "có võ" cho các bạn đây:

- Chọn đúng "công cụ" cho việc cần làm: Giống như bạn không thể dùng búa để đóng đinh ốc vậy. Cần danh sách có thứ tự, trùng lặp? Dùng
List. Cần tập hợp các đối tượng duy nhất? DùngSet. Cần lưu trữ theo cặp khóa-giá trị? DùngMap. Hiểu rõ đặc tính của từng loại sẽ giúp code chạy nhanh hơn và ít lỗi hơn. - "Làm việc" với Interface, không phải Class cụ thể: Thay vì khai báo
ArrayList<String> myList = new ArrayList<>();, hãy dùngList<String> myList = new ArrayList<>();. Tại sao ư? Vì nó giúp code của bạn linh hoạt hơn, dễ dàng thay đổi implementation sau này (ví dụ từArrayListsangLinkedList) mà không phải sửa quá nhiều chỗ. "Chơi" với nguyên tắc, không "chơi" với chi tiết, đó là đẳng cấp! - "Đóng gói" cẩn thận với Generics: Luôn luôn dùng
List<String>,Set<Integer>,Map<String, Double>... thay vìList,Set,Maptrần trụi. Generics giúp Java kiểm tra lỗi kiểu dữ liệu ngay từ lúc bạn viết code (compile-time), tránh được những lỗi "ngớ ngẩn" khi chạy chương trình (runtime). Giống như bạn dán nhãn rõ ràng cho từng hộp đồ vậy, tránh nhầm lẫn. - "Bất biến" nếu có thể: Nếu một Collection không cần thay đổi sau khi được tạo, hãy biến nó thành "bất biến" (immutable). Điều này giúp code an toàn hơn, dễ debug hơn, đặc biệt trong môi trường đa luồng. Java 9+ có
List.of(),Set.of(),Map.of()để làm điều này. - "Dùng Iterator để lướt qua": Khi bạn cần duyệt qua các phần tử trong Collection và có thể muốn xóa một số phần tử trong quá trình duyệt, hãy dùng
Iterator. Nó an toàn hơn và tránh đượcConcurrentModificationExceptionso với việc dùng vòng lặpforthông thường khi bạn sửa đổi Collection.
Thế Collections Framework này được ứng dụng ở đâu trong "thế giới thực" mà chúng ta đang "cày" hàng ngày? Nhiều lắm luôn!
- TikTok/Facebook/Instagram: Khi bạn lướt News Feed, danh sách bạn bè, danh sách follow/follower, hay các hashtag thịnh hành – tất cả đều được quản lý bằng các Collection. Danh sách bạn bè có thể là một
Set(độc nhất), News Feed là mộtListcác bài viết, và các hashtag có thể làMap(hashtag -> số lượng sử dụng). - Shopee/Lazada/Tiki: Giỏ hàng của bạn là một
Listcác sản phẩm (có thể trùng lặp). Danh sách sản phẩm gợi ý, danh sách sản phẩm đã xem gần đây cũng làList. Khi bạn tìm kiếm sản phẩm theo ID, đó là lúcMapphát huy tác dụng. - Các game online: Danh sách người chơi trong một trận đấu, kho đồ của nhân vật, bảng xếp hạng – tất cả đều dùng Collections để lưu trữ và quản lý.
- Ngân hàng số/Ứng dụng thanh toán: Danh sách giao dịch, danh sách khách hàng, thông tin tài khoản – đều được tổ chức bằng Collections để dễ dàng truy xuất và xử lý.
- Hệ điều hành: Quản lý các tiến trình đang chạy, các tệp tin trong một thư mục, hàng đợi các tác vụ in ấn – đều là những ví dụ điển hình của Collections.
Nói chung, cứ nơi nào cần quản lý một nhóm các "thứ" gì đó (dù là người, đồ vật, hay dữ liệu), thì ở đó có bóng dáng của Collections Framework.
Anh Creyt đã từng "vật lộn" với Collections Framework này từ những ngày đầu "vào nghề", và có vài kinh nghiệm "xương máu" muốn chia sẻ với các bạn:
ArrayListvsLinkedList:ArrayList: Giống như một cái giá sách được đóng cố định. Tối ưu khi bạn cần truy cập phần tử theo chỉ mục (nhưget(index)) rất nhanh, và thêm/xóa ở cuối danh sách. Nhưng nếu bạn cứ thêm/xóa ở giữa, nó sẽ phải "dịch chuyển" cả đống sách, tốn thời gian. Nên dùng khi bạn cần đọc nhiều, sửa ít ở giữa.LinkedList: Giống như một chuỗi các toa tàu, mỗi toa biết toa trước và toa sau nó là ai. Thêm/xóa ở đầu hoặc giữa rất nhanh vì chỉ cần "cắt nối" vài toa. Nhưng để tìm đến toa thứ N thì phải đi từ đầu, nênget(index)sẽ chậm hơn. Nên dùng khi bạn cần thêm/xóa nhiều ở đầu/giữa danh sách (ví dụ: hàng đợi, stack).
HashSetvsTreeSet:HashSet: "Hội nhóm" tự do, không theo thứ tự. Tối ưu cho việc kiểm tra xem một phần tử có tồn tại hay không (contains()) và thêm/xóa rất nhanh. Tuy nhiên, nó không đảm bảo thứ tự các phần tử.TreeSet: "Hội nhóm" có tổ chức, các phần tử được sắp xếp theo một thứ tự tự nhiên hoặc do bạn định nghĩa. Việc thêm/xóa/tìm kiếm cũng khá nhanh, nhưng chậm hơnHashSetmột chút vì phải duy trì thứ tự. Nên dùng khi bạn cần các phần tử duy nhất VÀ được sắp xếp.
HashMapvsTreeMap:HashMap: "Từ điển" siêu nhanh, không quan tâm thứ tự các từ. Tối ưu cho việc tìm kiếm, thêm, xóa theo key cực kỳ nhanh. Đây là "ngôi sao" được dùng nhiều nhất.TreeMap: "Từ điển" có sắp xếp các từ khóa theo thứ tự. Tối ưu khi bạn cần các cặp key-value được sắp xếp theo key, ví dụ bạn muốn duyệt qua danh sách sản phẩm theo tên theo thứ tự alphabet.
Lời khuyên từ anh Creyt: Hầu hết các trường hợp, bạn sẽ bắt đầu với ArrayList cho List và HashMap cho Map vì chúng cung cấp hiệu năng tốt cho các tác vụ phổ biến. Chỉ khi bạn gặp phải vấn đề về hiệu năng hoặc cần các đặc tính cụ thể (như thứ tự, thêm/xóa ở giữa), bạn mới nên cân nhắc các implementation khác.
Đó, vậy là chúng ta đã "phá đảo" xong Collections Framework rồi đấy! Nhớ nhé, nó không chỉ là một đống class khô khan mà là những "công cụ siêu phàm" giúp bạn "làm chủ" dữ liệu trong Java. Cứ thực hành nhiều, "code dạo" nhiều là sẽ "thấm" ngay thôi. Chúc các bạn "code vui vẻ" và hẹn gặp lại trong những buổi "bung lụa" tiếp theo!
Thuộc Series: Java – OOP
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!