Java
2024-04-10
25 phút đọc
Hiểu sâu Java Collections: List, Set, Map và cách chọn cấu trúc phù hợp
Java Collections: Chọn đúng cấu trúc dữ liệu cho bài toán của bạn
Rất nhiều dự án Java chậm không phải vì thuật toán quá phức tạp, mà vì chọn sai Collection. Ví dụ: dùng List để kiểm tra tồn tại phần tử thay vì Set, hoặc dùng HashMap nơi cần giữ thứ tự phần tử.
1. List – Khi bạn cần danh sách có thứ tự
ArrayList: lưu trữ liên tiếp trong bộ nhớ, truy cập theo index O(1), thêm/xoá giữa danh sách O(n).LinkedList: mỗi phần tử là một node liên kết, thêm/xoá ở đầu/cuối O(1), nhưng truy cập index O(n).
Trong hầu hết trường hợp, ArrayList là lựa chọn mặc định:
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
System.out.println(names.get(0)); // Alice
Bạn chỉ nên dùng LinkedList khi thực sự cần rất nhiều thao tác chèn/xóa ở giữa danh sách và ít truy cập ngẫu nhiên.
2. Set – Khi bạn cần giá trị duy nhất và kiểm tra tồn tại nhanh
HashSet: không đảm bảo thứ tự, kiểm tra tồn tại O(1) trung bình.LinkedHashSet: giữ thứ tự chèn phần tử.TreeSet: giữ phần tử luôn được sắp xếp (dựa trênComparablehoặcComparator).
Ví dụ kiểm tra trùng email nhanh chóng:
Set<String> emails = new HashSet<>();
if (!emails.add("user@example.com")) {
System.out.println("Email đã tồn tại");
}
3. Map – Khi bạn cần ánh xạ key → value
HashMap: nhanh, không có thứ tự.LinkedHashMap: lưu thứ tự chèn, tiện cho cache đơn giản.TreeMap: duy trì key được sắp xếp.
Ví dụ dùng LinkedHashMap cho LRU cache mini:
Map<String, String> cache = new LinkedHashMap<>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return size() > 100;
}
};
4. Một số best practice quan trọng
- Luôn dùng interface (
List,Set,Map) ở khai báo biến, chỉ dùng class cụ thể ở bên phải dấunew. - Tránh
VectorvàHashtabletrong code mới – đây là legacy, đã có lựa chọn tốt hơn. - Cẩn thận với
ConcurrentHashMap: rất hữu ích trong môi trường multi-thread, nhưng đừng lạm dụng nơi không cần thiết.
Hiểu rõ Collections giúp bạn viết code vừa dễ đọc, vừa có hiệu năng tốt mà không cần tối ưu sớm bằng những kỹ thuật phức tạp.