Interface Map<K,V>

Map<K,V> là một interface trong Java Collections Framework, đại diện cho một cấu trúc dữ liệu lưu trữ các cặp khóa-giá trị (key-value pairs). Mỗi khóa là duy nhất và ánh xạ đến một giá trị duy nhất. Map không cho phép tồn tại các khóa trùng lặp.

Tại sao Interface Map<K,V> không kế thừa từ interface Collection<E>

Interface Map<K,V> trong Java không kế thừa từ bất kỳ interface nào khác. Thay vào đó, nó là một interface độc lập, tự định nghĩa các phương thức và cấu trúc cần thiết để quản lý các cặp khóa-giá trị. Mặc dù không kế thừa từ interface nào, Map<K,V> vẫn được coi là một phần quan trọng của Java Collections Framework, cùng với các interface như Collection<E>, List<E>, Set<E>, v.v. Interface Map<K,V> trong Java không kế thừa từ interface Collection<E> bởi vì bản chất và mục đích của hai interface này khác nhau:

Bản chất khác nhau:

↳ Collection<E> là một tập hợp các phần tử đơn lẻ (elements), trong khi Map<K,V> là một tập hợp các cặp khóa-giá trị (key-value pairs).

↳ Collection<E> tập trung vào quản lý và thao tác trên một nhóm đối tượng cùng loại (E), còn Map<K,V> quản lý các mối quan hệ giữa các khóa (K) và giá trị (V).

Các phương thức không phù hợp:

↳ Các phương thức của Collection<E>, như add(E e), remove(Object o), iterator(), không phù hợp với cách mà Map<K,V> hoạt động. Ví dụ, add(E e) không có ý nghĩa với Map<K,V> vì Map cần có cả khóa và giá trị khi thêm một phần tử.

↳ Map có các phương thức riêng để thao tác với cặp khóa-giá trị, chẳng hạn như put(K key, V value), get(Object key), remove(Object key), những phương thức này không thể được định nghĩa hợp lý trong Collection<E>.

Cách thức truy cập dữ liệu khác nhau:

↳ Trong Collection<E>, dữ liệu được truy cập trực tiếp qua các phần tử đơn lẻ, trong khi trong Map<K,V>, dữ liệu được truy cập thông qua khóa (K). Điều này dẫn đến việc Map cung cấp các chế độ xem riêng biệt cho các khóa, giá trị và cặp khóa-giá trị (keySet(), values(), entrySet()), điều mà Collection<E> không cần.

Mục đích sử dụng khác nhau:

↳ Collection<E> được thiết kế để xử lý các tập hợp đơn giản (list, set, queue, v.v.), trong khi Map<K,V> được thiết kế để ánh xạ các giá trị với các khóa duy nhất, phục vụ cho các trường hợp sử dụng khác nhau.

Vì vậy, Map<K,V> không kế thừa từ Collection<E> để duy trì sự rõ ràng về mặt thiết kế và ngữ nghĩa trong việc sử dụng hai interface này.

Đặc điểm nổi bật của interface Map<K,V>

Khi nói đến việc lưu trữ dữ liệu dưới dạng các cặp liên kết, Map<K,V> là một trong những giao diện quan trọng và được sử dụng rộng rãi nhất trong Java. Khác với Collection lưu trữ một nhóm các phần tử, Map<K,V> chuyên dùng để quản lý các liên kết giữa khóa và giá trị. Để hiểu rõ hơn về cách thức hoạt động của Map<K,V>, hãy cùng khám phá các đặc điểm nổi bật sau:

Quản lý các cặp khóa-giá trị (Key-Value Pairs):

↳ Map lưu trữ dữ liệu dưới dạng các cặp khóa-giá trị, mỗi khóa (key) tương ứng với một giá trị (value).

↳ Khóa phải là duy nhất, không thể có hai khóa trùng nhau trong một Map. Mỗi khóa chỉ ánh xạ đến một giá trị duy nhất.

↳ Không chứa các khóa trùng lặp: Nếu bạn cố gắng chèn một phần tử mới với khóa đã tồn tại, giá trị cũ sẽ bị ghi đè bởi giá trị mới.

Map cung cấp ba dạng chế độ xem:

↳ Tập hợp các khóa (Set<K> keySet()).

↳ Tập hợp các giá trị (Collection<V> values()).

↳ Tập hợp các cặp khóa-giá trị (Set<Map.Entry<K,V>> entrySet()), cho phép duyệt qua toàn bộ các cặp khóa-giá trị.

Có nhiều lớp triển khai của Map với các đặc tính khác nhau như:

↳ HashMap: Không duy trì thứ tự các phần tử.

↳ TreeMap: Duy trì thứ tự sắp xếp của các khóa.

↳ LinkedHashMap: Duy trì thứ tự chèn các phần tử.

↳ Hỗ trợ thao tác nhanh chóng: Các thao tác như chèn, xóa và tìm kiếm thường diễn ra nhanh chóng trong các triển khai Map như HashMap nhờ sử dụng mã băm (hashing).

Không cho phép khóa trống trong một số triển khai:

↳ Một số triển khai như TreeMap không cho phép khóa có giá trị null, trong khi HashMap có thể chấp nhận khóa null.

Tương thích với các thao tác khác của Collection:

↳ Map cũng hỗ trợ một số phương thức như put, get, remove, containsKey, containsValue để thực hiện các thao tác cơ bản trên dữ liệu.

Hỗ trợ các phương thức "destructive" và "non-destructive":

↳ Các phương thức có thể thay đổi trực tiếp Map (như put, remove) được gọi là "destructive" và có thể ném ngoại lệ nếu Map không hỗ trợ chúng.

↳ Các phương thức "non-destructive" như get hoặc containsKey không thay đổi cấu trúc của Map.

Những đặc điểm này khiến Map trở thành cấu trúc dữ liệu mạnh mẽ và linh hoạt trong Java, phù hợp cho các trường hợp cần lưu trữ và truy cập dữ liệu theo cặp khóa-giá trị.

Các lớp và interface kế thừa/triển khai của Interface Map<K,V> trong Java - minh họa
Mô tả các lớp và interface kế thừa (extends)/triển khai (implements) của Interface Map<K,V>.

"Trong các phần tiếp theo, chúng ta sẽ tập trung tìm hiểu chi tiết về các lớp và interface kế thừa (extends)/triển khai (implements) nổi bật của Map<K,V>, mở rộng kiến thức về cách quản lý dữ liệu theo cặp khóa-giá trị trong Java."

Khai báo Interface Map<K,V> trong Java

Để sử dụng Interface Map<K,V>, bạn cần import gói java.util vào đầu file Java của mình.

Cú pháp câu lệnh import:

Cú pháp

import java.util.Map;

Cú pháp khai báo Interface Map<K,V>:

Cú pháp

public interface Map<K,V>

Dưới đây là giải thích chi tiết về cú pháp khai báo này:

↳ Map là một interface, có nghĩa là nó chỉ định các phương thức mà các lớp cụ thể phải triển khai. Interface không cung cấp triển khai thực tế mà chỉ định những gì một lớp cần phải làm để thực hiện theo hợp đồng của interface.

↳ K và V là các Tham số kiểu (Generic Type Parameters) mà Map sử dụng.

↳ K: Đây là kiểu dữ liệu của khóa (Key). Mỗi khóa trong một Map là duy nhất, nghĩa là không thể có hai khóa giống nhau trong cùng một Map.

↳ V: Đây là kiểu dữ liệu của giá trị (Value) liên kết với khóa, nó chứa dữ liệu mà bạn muốn lưu trữ trong bản đồ. Mỗi khóa sẽ ánh xạ (map) đến một giá trị.

↳ Ví dụ, nếu bạn muốn tạo một bản đồ mà khóa là kiểu String và giá trị là kiểu Integer, bạn có thể sử dụng Map<String, Integer>.

static interface Map.Entry<K,V>

Map.Entry<K,V> là một interface tĩnh nằm bên trong interface Map trong Java, đại diện cho một cặp khóa-giá trị (key-value pair) trong một Map. Nó cung cấp các phương thức để truy cập và thiết lập khóa và giá trị của một cặp.

Lưu ý về Map.Entry<K,V>

↳ Các đối tượng Map.Entry chỉ hợp lệ khi bạn đang duyệt Map với một iterator.

↳ Nếu Map bị thay đổi (ví dụ, thêm hoặc xóa phần tử) trong khi bạn đang duyệt Map, các đối tượng Map.Entry có thể trở nên không hợp lệ, dẫn đến hành vi không xác định.

Các phương thức chính của Map.Entry<K,V>

Giao diện Map.Entry được sử dụng để đại diện cho một cặp khóa-giá trị trong một bản đồ. Các phương thức trong giao diện này cho phép bạn truy cập, so sánh và thay đổi các mục nhập bản đồ. Dưới đây là danh sách tất cả các phương thức của Map.Entry<K,V> trong Java:

↳ static <K extends Comparable<? super K>,V> Comparator<Map.Entry<K,V>> comparingByKey(): Trả về một comparator so sánh Map.Entry theo thứ tự tự nhiên trên khóa.

↳ static <K,V> Comparator<Map.Entry<K,V>> comparingByKey(Comparator<? super K> cmp): Trả về một comparator so sánh Map.Entry theo khóa bằng cách sử dụng Comparator đã cho.

↳ static <K,V extends Comparable<? super V>> Comparator<Map.Entry<<K,V>> comparingByValue(): Trả về một comparator so sánh Map.Entry theo thứ tự tự nhiên trên giá trị.

↳ static <K,V> Comparator<Map.Entry<K,V>> comparingByValue(Comparator<? super V> cmp): Trả về một comparator so sánh Map.Entry theo giá trị bằng cách sử dụng Comparator đã cho.

↳ boolean equals(Object o): So sánh đối tượng đã cho với mục nhập này để kiểm tra xem chúng có bằng nhau hay không.

↳ K getKey(): Trả về khóa tương ứng với mục nhập này.

↳ V getValue(): Trả về giá trị tương ứng với mục nhập này.

↳ int hashCode(): Trả về giá trị mã băm cho mục nhập bản đồ này.

↳ V setValue(V value): Thay thế giá trị tương ứng với mục nhập này bằng giá trị đã cho (tùy chọn).

Dưới đây là một ví dụ đơn giản về cách sử dụng Map.Entry<K,V> trong Java. Map.Entry là một giao diện (interface) đại diện cho một cặp khóa-giá trị trong một Map.

Ví dụ: Example.java

import java.util.HashMap;
import java.util.Map;

public class Example {
    public static void main(String[] args) {
        // Tạo một HashMap với các cặp khóa-giá trị
        Map<String, Integer> map = new HashMap<>();
        map.put("Apple", 10);
        map.put("Banana", 20);
        map.put("Orange", 30);

        // Duyệt qua các cặp khóa-giá trị trong Map bằng Map.Entry
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();   // Lấy khóa từ Map.Entry
            Integer value = entry.getValue(); // Lấy giá trị từ Map.Entry
            System.out.println("Key: " + key + ", Value: " + value);
        }
    }
}

Kết quả của chương trình là:

Key: Apple, Value: 10
Key: Orange, Value: 30
Key: Banana, Value: 20

Ví dụ trên minh họa cách duyệt qua các phần tử trong Map và lấy ra từng cặp khóa-giá trị bằng cách sử dụng Map.Entry<K,V>.

Các phương thức chính trong interface Map<K,V>

Giao diện Map cung cấp các phương thức để thao tác với các cặp khóa-giá trị trong một bản đồ. Bạn có thể sử dụng các phương thức này để thêm, xóa, lấy, kiểm tra và duyệt qua các mục nhập trong bản đồ. Dưới đây là danh sách tất cả các phương thức của interface Map<K,V> trong Java:

↳ void clear(): Xóa tất cả các ánh xạ khỏi bản đồ này (tùy chọn).

↳ default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Thử tính toán một ánh xạ cho khóa đã cho và giá trị được ánh xạ hiện tại của nó (hoặc null nếu không có ánh xạ hiện tại).

↳ default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): Nếu khóa đã cho chưa được liên kết với một giá trị (hoặc được ánh xạ đến null), thử tính toán giá trị của nó bằng cách sử dụng hàm ánh xạ đã cho và nhập nó vào bản đồ này trừ khi null.

↳ default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Nếu giá trị cho khóa đã cho có mặt và không null, thử tính toán một ánh xạ mới dựa trên khóa và giá trị được ánh xạ hiện tại của nó.

↳ boolean containsKey(Object key): Trả về true nếu bản đồ này chứa một ánh xạ cho khóa đã cho.

↳ boolean containsValue(Object value): Trả về true nếu bản đồ này ánh xạ một hoặc nhiều khóa đến giá trị đã cho.

↳ Set<Map.Entry<K,V>> entrySet(): Trả về một view Set của các ánh xạ có trong bản đồ này.

↳ boolean equals(Object o): So sánh đối tượng đã cho với bản đồ này để kiểm tra xem chúng có bằng nhau hay không.

↳ default void forEach(BiConsumer<? super K, ? super V> action): Thực hiện hành động đã cho cho mỗi mục nhập trong bản đồ này cho đến khi tất cả các mục nhập đã được xử lý hoặc hành động ném ra một ngoại lệ.

↳ E get(Object key): Trả về giá trị mà khóa đã cho được ánh xạ đến, hoặc null nếu bản đồ này không chứa ánh xạ nào cho khóa.

↳ default getOrDefault(Object key, V defaultValue): Trả về giá trị mà khóa đã cho được ánh xạ đến, hoặc defaultValue nếu bản đồ này không chứa ánh xạ nào cho khóa.

↳ int hashCode(): Trả về giá trị mã băm cho bản đồ này.

↳ boolean isEmpty(): Trả về true nếu bản đồ này không chứa ánh xạ khóa-giá trị nào.

↳ Set<K> keySet(): Trả về một view Set của các khóa có trong bản đồ này.

↳ default V merge(K key, V value, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Nếu khóa đã cho chưa được liên kết với một giá trị hoặc được ánh xạ đến null, liên kết nó với giá trị không null đã cho.

↳ E put(K key, V value): Liên kết giá trị đã cho với khóa đã cho trong bản đồ này (tùy chọn).

↳ void putAll(Map<? extends K, ? extends V> m): Sao chép tất cả các ánh xạ từ bản đồ đã cho vào bản đồ này (tùy chọn).

↳ default V putIfAbsent(K key, V value): Nếu khóa đã cho chưa được liên kết với một giá trị (hoặc được ánh xạ đến null), liên kết nó với giá trị đã cho và trả về null, nếu không thì trả về giá trị hiện tại.

↳V remove(Object key): Xóa ánh xạ cho một khóa khỏi bản đồ này nếu nó có mặt (tùy chọn).

↳ default boolean remove(Object key, Object value): Xóa mục nhập cho khóa đã cho chỉ khi nó hiện đang được ánh xạ đến giá trị đã cho.

↳ default V replace(K key, V value): Thay thế mục nhập cho khóa đã cho chỉ khi nó hiện đang được ánh xạ đến một số giá trị.

↳ default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function): Thay thế giá trị của mỗi mục nhập bằng kết quả của việc gọi hàm đã cho trên mục nhập đó cho đến khi tất cả các mục nhập đã được xử lý hoặc hàm ném ra một ngoại lệ.

↳ int size(): Trả về số lượng ánh xạ khóa-giá trị trong bản đồ này.

↳ Collection<V> values(): Trả về một view Collection của các giá trị có trong bản đồ này.

Dưới đây là một ví dụ về cách sử dụng một số phương thức quan trọng của giao diện Map trong một lớp Java duy nhất. Ví dụ này sẽ minh họa các phương thức như put(), get(), remove(), containsKey(), containsValue(), size(), isEmpty(), keySet(), values(), và entrySet().

Ví dụ: Example.java

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Example {
    public static void main(String[] args) {
        // Tạo một HashMap với các cặp khóa-giá trị
        Map<String, Integer> map = new HashMap<>();

        // 1. put(K key, V value) - Thêm một cặp khóa-giá trị vào Map
        map.put("Apple", 10);
        map.put("Banana", 20);
        map.put("Orange", 30);
        map.put("Mango", 40);

        // 2. get(Object key) - Truy xuất giá trị dựa trên khóa
        Integer appleValue = map.get("Apple");
        System.out.println("Giá trị liên kết với 'Apple': " + appleValue);

        // 3. remove(Object key) - Loại bỏ một cặp khóa-giá trị khỏi Map
        map.remove("Banana");
        System.out.println("Map sau khi xóa 'Banana': " + map);

        // 4. containsKey(Object key) - Kiểm tra xem khóa có tồn tại trong Map hay không
        boolean containsOrange = map.containsKey("Orange");
        System.out.println("Map chứa 'Orange': " + containsOrange);

        // 5. containsValue(Object value) - Kiểm tra xem giá trị có tồn tại trong Map hay không
        boolean contains30 = map.containsValue(30);
        System.out.println("Map chứa giá trị 30: " + contains30);

        // 6. size() - Trả về số lượng cặp khóa-giá trị trong Map
        int size = map.size();
        System.out.println("Kích thước của map: " + size);

        // 7. isEmpty() - Kiểm tra xem Map có rỗng không
        boolean isEmpty = map.isEmpty();
        System.out.println("Map trống: " + isEmpty);

        // 8. keySet() - Trả về một tập hợp các khóa trong Map
        Set<String> keys = map.keySet();
        System.out.println("Keys trong map: " + keys);

        // 9. values() - Trả về một Collection các giá trị trong Map
        Collection<Integer> values = map.values();
        System.out.println("Values trong map: " + values);

        // 10. entrySet() - Trả về một tập hợp các cặp Map.Entry trong Map
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        System.out.println("Entries trong map:");
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey() + " => " + entry.getValue());
        }
    }
}

Kết quả của chương trình là:

Giá trị liên kết với 'Apple': 10
Map sau khi xóa 'Banana': {Apple=10, Mango=40, Orange=30}
Map chứa 'Orange': true
Map chứa giá trị 30: true
Kích thước của map: 3
Map trống: false
Keys trong map: [Apple, Mango, Orange]
Values trong map: [10, 40, 30]
Entries trong map:
Apple => 10
Mango => 40
Orange => 30

Chương trình trên minh họa cách sử dụng các phương thức quan trọng của giao diện Map trong Java. Bằng cách sử dụng HashMap, chúng ta có thể thực hiện các thao tác như thêm, truy xuất, xóa và kiểm tra các phần tử trong Map.

Như vậy, chúng ta đã cùng nhau tìm hiểu các phương thức chính của giao diện Map<K,V>. Với khả năng lưu trữ dữ liệu dưới dạng các cặp khóa-giá trị duy nhất, Map là một công cụ không thể thiếu trong Java để quản lý các mối liên kết giữa dữ liệu. Nắm vững các thao tác thêm, xóa, lấy giá trị theo khóa, và duyệt qua các mục nhập sẽ giúp bạn xây dựng các ứng dụng mạnh mẽ và linh hoạt hơn rất nhiều.

Câu Nói Truyền Cảm Hứng

“Bắt đầu ở đâu không quan trọng, quan trọng là bạn sẵn sàng bắt đầu.” – W. Clement Stone

Không Gian Tích Cực

“Chúc bạn luôn giữ vững niềm tin và sức mạnh để vượt qua mọi thử thách trong cuộc sống.”