Menu

HashMap trong Java: put, get, duyệt map và mẫu thường dùng

Cách dùng HashMap của Java để tra cứu theo khóa: put, get, getOrDefault, containsKey, duyệt qua các entry, và những mẫu code bạn dùng nhiều nhất.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Map lưu các cặp khóa và giá trị

HashMap (trong java.util) lưu các liên kết: mỗi khóa (key) ánh xạ tới một giá trị (value), và bạn tra giá trị theo khóa trong thời gian gần như hằng số. Hãy nghĩ tới một cuốn từ điển: từ là khóa, định nghĩa là giá trị.

Hai tham số kiểu là <KeyType, ValueType>. Ở đây khóa là String và giá trị là Integer. Cũng như với ArrayList, bạn thường khai báo biến theo interface Map và tạo đối tượng HashMap.

put, get và ghi đè

Hai điều cần ghi nhớ:

  • Khóa là duy nhất. put với một khóa đã có sẽ thay giá trị của nó và trả về giá trị cũ.
  • get với khóa không tồn tại trả về null, không báo lỗi. Tự động unboxing giá trị null đó thành int sẽ ném NullPointerException, một nguồn bug rất phổ biến.

getOrDefault tránh bẫy null

Thay vì lần nào cũng kiểm tra null, hãy yêu cầu một giá trị dự phòng:

Đây là cách gọn nhất để xử lý những lần tra cứu "có thể có, có thể không", và nó dẫn thẳng tới mẫu code nổi tiếng nhất của HashMap.

Đếm số lần xuất hiện

Đếm mỗi phần tử xuất hiện bao nhiêu lần là bài toán kinh điển của HashMap:

Mẫu map.put(key, map.getOrDefault(key, 0) + 1) đọc là "lấy số đếm hiện tại (hoặc 0), cộng một, rồi lưu lại". Một cách viết gọn hơn tương đương là counts.merge(word, 1, Integer::sum).

Kiểm tra và xóa

putIfAbsent(key, value) chỉ ghi khi khóa chưa tồn tại, hữu ích cho việc khởi tạo trễ (lazy initialization).

Duyệt một HashMap

Vòng lặp phổ biến nhất duyệt entrySet(), cho bạn cùng lúc khóa và giá trị:

Nếu bạn chỉ cần khóa hoặc chỉ cần giá trị:

Bạn cũng có thể dùng forEach với lambda: ages.forEach((name, age) -> System.out.println(name + ": " + age));.

HashMap không giữ thứ tự

HashMap không cam kết gì về thứ tự duyệt: thứ tự phụ thuộc vào kết quả băm và có thể thay đổi giữa các lần chạy. Nếu bạn cần thứ tự dự đoán được:

  • LinkedHashMap giữ thứ tự thêm vào.
  • TreeMap giữ khóa được sắp xếp theo thứ tự tự nhiên (hoặc theo Comparator bạn cung cấp).

Cả ba đều cài đặt interface Map, nên muốn đổi chỉ cần sửa một dòng ở constructor.

Khóa phải băm được

HashMap tìm phần tử bằng cách băm khóa, nên hashCode() và equals() của khóa phải nhất quán với nhau. Các kiểu có sẵn như String và Integer đã làm đúng điều này. Nếu dùng lớp của riêng bạn làm khóa, hãy override cả equals lẫn hashCode, nếu không hai đối tượng "bằng nhau" về ý nghĩa sẽ rơi vào hai bucket khác nhau và việc tra cứu sẽ thất bại một cách khó hiểu.

Tiếp theo: HashSet

HashMap trả lời câu hỏi "giá trị nào được lưu dưới khóa này?". Khi bạn chỉ quan tâm một thứ có tồn tại hay không, tức một tập giá trị duy nhất không kèm dữ liệu liên kết, công cụ cần dùng là HashSet, nội dung của trang tiếp theo.

Câu hỏi thường gặp

Làm sao tạo HashMap trong Java?

Khai báo với hai tham số kiểu, kiểu của khóa và kiểu của giá trị, rồi gọi constructor: Map<String, Integer> ages = new HashMap<>();. Sau đó thêm phần tử bằng ages.put("Ada", 36); và đọc ra bằng ages.get("Ada");. Nhớ import java.util.HashMap và java.util.Map.

Làm sao duyệt HashMap trong Java?

Duyệt map.entrySet() bằng vòng lặp for-each để lấy cùng lúc khóa và giá trị: for (Map.Entry<String, Integer> e : map.entrySet()) { ... }, rồi đọc e.getKey() và e.getValue(). Bạn cũng có thể duyệt map.keySet() nếu chỉ cần khóa, hoặc map.values() nếu chỉ cần giá trị. Lưu ý HashMap không giữ thứ tự thêm vào.

get và getOrDefault khác nhau thế nào?

get(key) trả về giá trị của khóa, hoặc null nếu khóa không tồn tại, và điều này có thể gây NullPointerException nếu bạn dùng kết quả trực tiếp. getOrDefault(key, fallback) trả về giá trị nếu có, nếu không thì trả về giá trị dự phòng bạn truyền vào, nhờ vậy bạn khỏi phải kiểm tra null. Nó đặc biệt tiện khi đếm: counts.put(c, counts.getOrDefault(c, 0) + 1).

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu