# 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

1. **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ụ:
        
        ```java
        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}
        ```
        
2. **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ụ:
        
        ```java
        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
        ```
        
3. **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ụ:
        
        ```java
        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}
        ```
        
4. **boolean containsKey(Object key)**
    
    * Kiểm tra xem Map có chứa khóa `key` hay không. Trả về `true` nếu có, ngược lại trả về `false`.
        
    * Ví dụ:
        
        ```java
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "A");
        boolean contains = map.containsKey(1);
        System.out.println(contains); // Output: true
        ```
        
5. **boolean containsValue(Object value)**
    
    * Kiểm tra xem Map có chứa giá trị `value` hay không. Trả về `true` nếu có, ngược lại trả về `false`.
        
    * Ví dụ:
        
        ```java
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "A");
        boolean contains = map.containsValue("A");
        System.out.println(contains); // Output: true
        ```
        
6. **Set keySet()**
    
    * Trả về tập hợp các khóa trong Map.
        
    * Ví dụ:
        
        ```java
        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]
        ```
        
7. **Collection values()**
    
    * Trả về tập hợp các giá trị trong Map.
        
    * Ví dụ:
        
        ```java
        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]
        ```
        
8. **Set&lt;Map.Entry&lt;K, V&gt;&gt; entrySet()**
    
    * Trả về tập hợp các cặp khóa-giá trị trong Map.
        
    * Ví dụ:
        
        ```java
        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

1. **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ụ:**
        
        ```java
        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}
        ```
        
2. **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ụ:**
        
        ```java
        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}
        ```
        
3. **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ụ:**
        
        ```java
        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}
        ```
        
4. **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ụ:**
        
        ```java
        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}
        ```
        
5. **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ụ:**
        
        ```java
        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

1. **Sử Dụng HashMap**
    
    **Mô tả:** Lưu trữ thông tin sinh viên theo mã sinh viên.
    
    ```java
    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);
        }
    }
    ```
    
2. **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.
    
    ```java
    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());
            }
        }
    }
    ```
    
3. **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.
    
    ```java
    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());
            }
        }
    }
    ```
    
4. **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.
    
    ```java
    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

1. **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ụ:
        
        ```java
        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);
            }
        }
        ```
        
2. **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ụ:
        
        ```java
        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());
                }
            }
        }
        ```
        
3. **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ụ:
        
        ```java
        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());
                }
            }
        }
        ```
        
4. **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ụ:
        
        ```java
        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](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.
