Bài 4: Map Interface và Các Implementations
I. Giới Thiệu Về Map Interface
Map là một interface trong Java Collection Framework đại diện cho một tập hợp các cặp khóa-giá trị (key-value pairs). Mỗi khóa là duy nhất, và mỗi khóa chỉ liên kết với một giá trị. Map không là một phần của Collection interface, nhưng nó là một phần của Java Collections Framework. Map thường được sử dụng để lưu trữ và truy xuất dữ liệu dựa trên khóa.
II. Các Phương Thức Chính Trong Map Interface
V put(K key, V value)
Thêm một cặp khóa-giá trị vào Map. Nếu khóa đã tồn tại, giá trị cũ sẽ được thay thế bởi giá trị mới.
Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); map.put(2, "B"); map.put(1, "C"); // Thay thế giá trị của khóa 1 System.out.println(map); // Output: {1=C, 2=B}
V get(Object key)
Trả về giá trị liên kết với khóa
key. Nếu khóa không tồn tại, trả vềnull.Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); map.put(2, "B"); String value = map.get(1); System.out.println(value); // Output: A
V remove(Object key)
Xóa cặp khóa-giá trị khỏi Map dựa trên khóa
key. Trả về giá trị liên kết với khóa nếu tồn tại, ngược lại trả vềnull.Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); map.put(2, "B"); String removedValue = map.remove(1); System.out.println(removedValue); // Output: A System.out.println(map); // Output: {2=B}
boolean containsKey(Object key)
Kiểm tra xem Map có chứa khóa
keyhay không. Trả vềtruenếu có, ngược lại trả vềfalse.Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); boolean contains = map.containsKey(1); System.out.println(contains); // Output: true
boolean containsValue(Object value)
Kiểm tra xem Map có chứa giá trị
valuehay không. Trả vềtruenếu có, ngược lại trả vềfalse.Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); boolean contains = map.containsValue("A"); System.out.println(contains); // Output: true
Set keySet()
Trả về tập hợp các khóa trong Map.
Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); map.put(2, "B"); Set<Integer> keys = map.keySet(); System.out.println(keys); // Output: [1, 2]
Collection values()
Trả về tập hợp các giá trị trong Map.
Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); map.put(2, "B"); Collection<String> values = map.values(); System.out.println(values); // Output: [A, B]
Set<Map.Entry<K, V>> entrySet()
Trả về tập hợp các cặp khóa-giá trị trong Map.
Ví dụ:
Map<Integer, String> map = new HashMap<>(); map.put(1, "A"); map.put(2, "B"); Set<Map.Entry<Integer, String>> entries = map.entrySet(); for (Map.Entry<Integer, String> entry : entries) { System.out.println(entry.getKey() + " = " + entry.getValue()); } // Output: // 1 = A // 2 = B
III. Các Lớp Triển Khai Map Interface
HashMap
Đặ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ụ:
Map<Integer, String> hashMap = new HashMap<>(); hashMap.put(1, "A"); hashMap.put(2, "B"); hashMap.put(1, "C"); // Thay thế giá trị của khóa 1 System.out.println(hashMap); // Output: {1=C, 2=B}
LinkedHashMap
Đặc điểm:
Kế thừa từ HashMap 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ư HashMap 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ụ:
Map<Integer, String> linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put(1, "A"); linkedHashMap.put(2, "B"); linkedHashMap.put(1, "C"); // Thay thế giá trị của khóa 1 System.out.println(linkedHashMap); // Output: {1=C, 2=B}
TreeMap
Đặ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ụ:
Map<Integer, String> treeMap = new TreeMap<>(); treeMap.put(2, "B"); treeMap.put(1, "A"); treeMap.put(3, "C"); System.out.println(treeMap); // Output: {1=A, 2=B, 3=C}
Hashtable
Đặc điểm:
Giống như HashMap nhưng có tính đồng bộ (synchronized).
Hiệu năng kém hơn HashMap do tính đồng bộ.
Khi nào sử dụng: Khi cần một Map hỗ trợ đa luồng (multi-threading).
Ví dụ:
Map<Integer, String> hashtable = new Hashtable<>(); hashtable.put(1, "A"); hashtable.put(2, "B"); hashtable.put(1, "C"); // Thay thế giá trị của khóa 1 System.out.println(hashtable); // Output: {1=C, 2=B}
ConcurrentHashMap
Đặc điểm:
Một phiên bản đồng bộ của HashMap được tối ưu hóa cho môi trường đa luồng.
Hỗ trợ đồng thời nhiều thao tác đọc và viết.
Khi nào sử dụng: Khi cần một Map đồng bộ và hiệu suất cao trong môi trường đa luồng.
Ví dụ:
Map<Integer, String> concurrentHashMap = new ConcurrentHashMap<>(); concurrentHashMap.put(1, "A"); concurrentHashMap.put(2, "B"); concurrentHashMap.put(1, "C"); // Thay thế giá trị của khóa 1 System.out.println(concurrentHashMap); // Output: {1=C, 2=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 |
| HashMap | Không | Rất nhanh (O(1)) | Rất nhanh (O(1)) | Không | Khi không cần thứ tự |
| LinkedHashMap | Theo thứ tự chèn | Nhanh (O(1)) | Nhanh (O(1)) | Không | Khi cần duy trì thứ tự chèn |
| TreeMap | 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ử | | Hashtable | Không | Rất nhanh (O(1)) | Rất nhanh (O(1)) | Có | Khi cần hỗ trợ đa luồng | | ConcurrentHashMap | Không | Rất nhanh (O(1)) | Rất nhanh (O(1)) | Có | Khi cần đồng bộ và hiệu suất cao |
V. Ví Dụ Minh Họa Chi Tiết
Sử Dụng HashMap
Mô tả: Lưu trữ thông tin sinh viên theo mã sinh viên.
import java.util.HashMap; import java.util.Map; public class HashMapExample { public static void main(String[] args) { Map<Integer, String> studentMap = new HashMap<>(); studentMap.put(1, "John Doe"); studentMap.put(2, "Jane Smith"); studentMap.put(3, "Mike Johnson"); // Truy xuất thông tin sinh viên theo mã sinh viên String student = studentMap.get(2); System.out.println("Student with ID 2: " + student); // Kiểm tra xem có sinh viên với mã 4 không boolean hasStudent = studentMap.containsKey(4); System.out.println("Has student with ID 4: " + hasStudent); } }Sử Dụng LinkedHashMap
Mô tả: Lưu trữ và duy trì thứ tự chèn các sản phẩm trong giỏ hàng.
import java.util.LinkedHashMap; import java.util.Map; public class LinkedHashMapExample { public static void main(String[] args) { Map<String, Integer> cart = new LinkedHashMap<>(); cart.put("Apple", 3); cart.put("Banana", 2); cart.put("Orange", 5); // In ra các sản phẩm trong giỏ hàng theo thứ tự chèn for (Map.Entry<String, Integer> entry : cart.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } }Sử Dụng TreeMap
Mô tả: Lưu trữ và sắp xếp các điểm số sinh viên theo thứ tự mã sinh viên.
import java.util.Map; import java.util.TreeMap; public class TreeMapExample { public static void main(String[] args) { Map<Integer, Integer> scores = new TreeMap<>(); scores.put(102, 85); scores.put(101, 90); scores.put(103, 75); // In ra các điểm số theo thứ tự mã sinh viên for (Map.Entry<Integer, Integer> entry : scores.entrySet()) { System.out.println("Student ID " + entry.getKey() + ": " + entry.getValue()); } } }Sử Dụng ConcurrentHashMap
Mô tả: Lưu trữ và truy xuất thông tin nhân viên trong một ứng dụng đa luồng.
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ConcurrentHashMapExample { public static void main(String[] args) { Map<Integer, String> employeeMap = new ConcurrentHashMap<>(); employeeMap.put(1, "Alice Brown"); employeeMap.put(2, "Bob White"); employeeMap.put(3, "Charlie Green"); // Truy xuất thông tin nhân viên theo mã nhân viên trong môi trường đa luồng Runnable task = () -> { String employee = employeeMap.get(2); System.out.println("Employee with ID 2: " + employee); }; Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); } }
VI. Bài Tập Thực Hành
Tạo chương trình Java sử dụng HashMap để quản lý danh bạ điện thoại.
Yêu cầu: Thêm, xóa, và tìm kiếm thông tin liên lạc dựa trên số điện thoại.
Ví dụ:
import java.util.HashMap; import java.util.Map; public class PhoneBook { public static void main(String[] args) { Map<String, String> phoneBook = new HashMap<>(); phoneBook.put("123-456-7890", "John Doe"); phoneBook.put("098-765-4321", "Jane Smith"); // Thêm liên lạc mới phoneBook.put("111-222-3333", "Alice Brown"); // Xóa liên lạc phoneBook.remove("098-765-4321"); // Tìm kiếm liên lạc String contact = phoneBook.get("123-456-7890"); System.out.println("Contact with phone number 123-456-7890: " + contact); } }
Tạo chương trình Java sử dụng LinkedHashMap để quản lý lịch sử truy cập web.
Yêu cầu: Thêm URL mới vào lịch sử, in ra lịch sử truy cập theo thứ tự chèn.
Ví dụ:
import java.util.LinkedHashMap; import java.util.Map; public class BrowserHistory { public static void main(String[] args) { Map<String, String> history = new LinkedHashMap<>(); history.put("google.com", "Google"); history.put("yahoo.com", "Yahoo"); history.put("bing.com", "Bing"); // Thêm URL mới vào lịch sử history.put("github.com", "GitHub"); // In ra lịch sử truy cập theo thứ tự chèn for (Map.Entry<String, String> entry : history.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } } }
Tạo chương trình Java sử dụng TreeMap để quản lý danh sách học sinh và điểm số của họ theo thứ tự tăng dần của mã học sinh.
Yêu cầu: Thêm, xóa và in ra danh sách học sinh theo thứ tự tăng dần của mã học sinh.
Ví dụ:
import java.util.Map; import java.util.TreeMap; public class StudentScores { public static void main(String[] args) { Map<Integer, Integer> scores = new TreeMap<>(); scores.put(103, 75); scores.put(101, 90); scores.put(102, 85); // Thêm điểm số học sinh mới scores.put(104, 80); // Xóa điểm số học sinh scores.remove(103); // In ra danh sách học sinh theo thứ tự tăng dần của mã học sinh for (Map.Entry<Integer, Integer> entry : scores.entrySet()) { System.out.println("Student ID " + entry.getKey() + ": " + entry.getValue()); } } }
Tạo chương trình Java sử dụng ConcurrentHashMap để quản lý dữ liệu phiên truy cập người dùng trong một ứng dụng đa luồng.
Yêu cầu: Thêm, xóa và truy xuất thông tin phiên truy cập một cách an toàn trong môi trường đa luồng.
Ví dụ:
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class UserSessions { public static void main(String[] args) { Map<String, String> sessions = new ConcurrentHashMap<>(); sessions.put("session1", "User1"); sessions.put("session2", "User2"); // Thêm phiên truy cập mới sessions.put("session3", "User3"); // Xóa phiên truy cập sessions.remove("session1"); // Truy xuất thông tin phiên truy cập Runnable task = () -> { String user = sessions.get("session2"); System.out.println("User with session2: " + user); }; Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); } }
VII. Kết Luận
Trong bài viết này, chúng ta đã khám phá Map 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 Map 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. Các ví dụ minh họa và bài tập thực hành sẽ giúp củng cố kiến thức và ứng dụng hiệu quả trong các dự án thực tế.
VIII. 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.