Seluruh struktur yang kita jelajahi sejauh ini — List, Set, Queue, Deque — menyimpan elemen satu per satu dan semuanya mewarisi Collection. Ada satu keluarga kebutuhan yang tidak terjawab oleh struktur tersebut: mencari nilai berdasarkan penanda tertentu. Stok toko perlu menjawab “berapa jumlah Pensil?” tanpa menyisir daftar; daftar nilai siswa perlu memetakan nama ke angka; statistik kata perlu tahu berapa kali java muncul. Kebutuhan-kebutuhan itu dijawab oleh interface Map, struktur yang memasangkan key (penanda unik) dengan value (nilai yang dicari). Pada Chapter ini kita pelajari kontrak dasarnya, tiga cara memandang isinya, dan method modern yang membuat pola hitung frekuensi menjadi ringkas.

Key dan Value dalam Satu Kontrak #

Kontrak Map sederhana namun tegas: satu key paling banyak memetakan ke satu value. Memasang key yang sudah ada lewat put tidak menambah baris baru — nilai lama diganti dan dikembalikan sebagai hasil put, sehingga kita bisa tahu nilai sebelumnya yang terganti. Operasi pencarian (get, containsKey) bekerja pada key, bukan pada value, karena key adalah alamat datanya.

Map berdiri di luar hierarki Collection — Map tidak meng-extend Collection. Alasannya struktural: method add(E e) milik Collection tidak punya makna untuk pasangan key-value, sedangkan Collection memproses satu elemen per operasi. Meski begitu, Map tetap menyediakan jembatan ke dunia iterasi lewat tiga pandangan yang dibahas setelah contoh pertama.

Operasi Dasar Map #

Program berikut memakai Map<String, Integer> untuk stok toko: key berupa nama barang, value berupa jumlahnya. Kita menjajal seluruh operasi inti kontrak Map satu per satu:

java StokToko.java
import java.util.*;

public class StokToko {
    public static void main(String[] args) {
        Map<String, Integer> stok = new HashMap<>();
        stok.put("Pensil", 120);
        stok.put("Buku Tulis", 45);
        stok.put("Penghapus", 30);

        System.out.println("get(Pensil)         : " + stok.get("Pensil"));
        System.out.println("get(Penggaris)      : " + stok.get("Penggaris"));

        System.out.println("containsKey(Buku Tulis) : " + stok.containsKey("Buku Tulis"));
        System.out.println("containsValue(45)       : " + stok.containsValue(45));

        stok.put("Pensil", 100);
        System.out.println("Pensil setelah put ulang : " + stok.get("Pensil"));

        stok.remove("Penghapus");
        System.out.println("size() setelah remove    : " + stok.size());

        System.out.println("keySet() : " + stok.keySet());
        System.out.println("values() : " + stok.values());

        System.out.println("entrySet():");
        for (Map.Entry<String, Integer> entri : stok.entrySet()) {
            System.out.println("  " + entri.getKey() + " -> " + entri.getValue());
        }

        stok.forEach((barang, jumlah) ->
                System.out.println("  forEach: " + barang + " berjumlah " + jumlah));
    }
}

Output yang Dihasilkan:

text
get(Pensil)         : 120
get(Penggaris)      : null
containsKey(Buku Tulis) : true
containsValue(45)       : true
Pensil setelah put ulang : 100
size() setelah remove    : 2
keySet() : [Pensil, Buku Tulis]
values() : [100, 45]
entrySet():
  Pensil -> 100
  Buku Tulis -> 45
  forEach: Pensil berjumlah 100
  forEach: Buku Tulis berjumlah 45

Mari kita bedah baris-baris pentingnya:

  1. Baris 5-9 (tiga put pertama): tiga pasangan masuk; Pensil, Buku Tulis, dan Penghapus menjadi key masing-masing. Pemilihan HashMap sebagai implementasi hanya untuk kelangsungan contoh — kontrak yang didemonstrasikan berlaku untuk semua Map.
  2. Baris 11-12 (get): pencarian mengembalikan value (120) untuk key yang ada dan null untuk key yang tidak ada — bentuk umum sinyal “tidak ditemukan” pada Map.
  3. Baris 14-15 (containsKey / containsValue): keanggotaan key dan keberadaan value adalah dua pertanyaan berbeda; containsValue(45) benar karena Buku Tulis bernilai 45.
  4. Baris 17-18 (put ulang): key Pensil sudah terpasang, jadi 100 menggantikan 120 — jumlah pasangan tetap tiga, bukan empat.
  5. Baris 20-21 (remove + size): penghapusan berdasarkan key; size() turun menjadi 2.

Tiga Pandangan Isi Map #

Karena Map bukan Collection, iterasi dilakukan lewat tiga pandangan yang semuanya adalah Collection:

PandanganTipe elemenKegunaan
keySet()Set<K>mengiterasi seluruh key
values()Collection<V>mengiterasi seluruh value
entrySet()Set<Map.Entry<K,V>>mengiterasi pasangan lengkap

Pandangan entrySet() adalah yang paling kaya: setiap elemennya bertipe Map.Entry — objek kecil berisi getKey() dan getValue() sehingga kita membaca pasangan lengkap dalam satu putaran loop. forEach di akhir contoh memakai bentuk lambda (barang, jumlah) — method tersebut menerima BiConsumer yang menerima key dan value secara bersamaan, versi ringkas dari iterasi entrySet().

Pola Hitung dengan Method Modern #

Kasus klasis pemakaian Map adalah menghitung frekuensi kemunculan tiap kata. Java modern menyediakan empat method yang membuat pola ini ringkas. Program berikut membandingkan dua cara menghitung, lalu membangun struktur kelompok kata:

java HitungKata.java
import java.util.*;

public class HitungKata {
    public static void main(String[] args) {
        String teks = "belajar java setiap hari belajar java coding setiap pagi";

        // Versi 1: getOrDefault mengembalikan nilai lama, atau 0 jika key belum ada.
        Map<String, Integer> hitungan = new HashMap<>();
        for (String kata : teks.split(" ")) {
            hitungan.put(kata, hitungan.getOrDefault(kata, 0) + 1);
        }
        System.out.println("getOrDefault : " + hitungan);

        // Versi 2: merge memasang nilai baru lalu menggabungkannya dengan nilai lama.
        Map<String, Integer> hitunganMerge = new HashMap<>();
        for (String kata : teks.split(" ")) {
            hitunganMerge.merge(kata, 1, Integer::sum);
        }
        System.out.println("merge        : " + hitunganMerge);

        // putIfAbsent hanya menulis jika key belum terpasang.
        hitunganMerge.putIfAbsent("algoritma", 1);
        hitunganMerge.putIfAbsent("java", 99);
        System.out.println("putIfAbsent  : algoritma=" + hitunganMerge.get("algoritma")
                + ", java=" + hitunganMerge.get("java"));

        // computeIfAbsent menyiapkan wadah kosong sekali, lalu wadah itu diisi berulang.
        Map<Integer, List<String>> perPanjang = new HashMap<>();
        for (String kata : teks.split(" ")) {
            perPanjang.computeIfAbsent(kata.length(), k -> new ArrayList<>()).add(kata);
        }
        System.out.println("Kelompok panjang kata:");
        perPanjang.forEach((panjang, daftar) ->
                System.out.println("  " + panjang + " huruf : " + daftar));
    }
}

Output yang Dihasilkan:

text
getOrDefault : {hari=1, coding=1, java=2, setiap=2, pagi=1, belajar=2}
merge        : {hari=1, coding=1, java=2, setiap=2, pagi=1, belajar=2}
putIfAbsent  : algoritma=1, java=2
Kelompok panjang kata:
  4 huruf : [java, hari, java, pagi]
  6 huruf : [setiap, coding, setiap]
  7 huruf : [belajar, belajar]

Poin bedahnya:

  1. Baris 8-10 (getOrDefault): saat kata pertama kali muncul, getOrDefault(kata, 0) mengembalikan 0, lalu + 1 menghasilkan 1; kemunculan berikutnya membaca nilai lama dan menaikkannya. Pola baca-naikkan-tulis ini adalah idiom klasis sebelum Java 8.
  2. Baris 14-16 (merge): satu baris menggantikan tiga langkah — merge memasang 1 jika key belum ada, atau memanggil Integer::sum untuk menjumlahkan nilai lama dengan 1. Method merge juga menghapus entry jika hasil penggabungan null, perilaku yang berguna saat menghitung selisih stok.
  3. Baris 19-21 (putIfAbsent): key baru (algoritma) terpasang dengan nilai 1, sedangkan key lama (java) tetap bernilai 2 — penulisan hanya terjadi ketika key belum ada, kebalikan dari put yang selalu menimpa.
  4. Baris 25-26 (computeIfAbsent): lambda k -> new ArrayList<>() hanya dijalankan sekali per key; setelah list tercipta, pemanggilan berikutnya menerima list yang sama sehingga add menumpuk kata ke kelompoknya. Pola ini adalah cara standar membangun multimap (satu key, banyak value) tanpa memeriksa null manual.

Dengan kontrak Map di tangan, pertanyaan berikutnya menyasar mesin di baliknya: bagaimana get bisa begitu cepat meski map berisi jutaan pasangan? Jawabannya adalah hashing — dan HashMap adalah implementasi yang paling banyak dipakai di seluruh ekosistem Java. Chapter berikutnya membedah bucket, collision, load factor, dan kontrak equals/hashCode yang membuat semuanya bekerja.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

HitungKata.java

Unduh berkas
java
import java.util.*;

public class HitungKata {
    public static void main(String[] args) {
        String teks = "belajar java setiap hari belajar java coding setiap pagi";

        // Versi 1: getOrDefault mengembalikan nilai lama, atau 0 jika key belum ada.
        Map<String, Integer> hitungan = new HashMap<>();
        for (String kata : teks.split(" ")) {
            hitungan.put(kata, hitungan.getOrDefault(kata, 0) + 1);
        }
        System.out.println("getOrDefault : " + hitungan);

        // Versi 2: merge memasang nilai baru lalu menggabungkannya dengan nilai lama.
        Map<String, Integer> hitunganMerge = new HashMap<>();
        for (String kata : teks.split(" ")) {
            hitunganMerge.merge(kata, 1, Integer::sum);
        }
        System.out.println("merge        : " + hitunganMerge);

        // putIfAbsent hanya menulis jika key belum terpasang.
        hitunganMerge.putIfAbsent("algoritma", 1);
        hitunganMerge.putIfAbsent("java", 99);
        System.out.println("putIfAbsent  : algoritma=" + hitunganMerge.get("algoritma")
                + ", java=" + hitunganMerge.get("java"));

        // computeIfAbsent menyiapkan wadah kosong sekali, lalu wadah itu diisi berulang.
        Map<Integer, List<String>> perPanjang = new HashMap<>();
        for (String kata : teks.split(" ")) {
            perPanjang.computeIfAbsent(kata.length(), k -> new ArrayList<>()).add(kata);
        }
        System.out.println("Kelompok panjang kata:");
        perPanjang.forEach((panjang, daftar) ->
                System.out.println("  " + panjang + " huruf : " + daftar));
    }
}

StokToko.java

Unduh berkas
java
import java.util.*;

public class StokToko {
    public static void main(String[] args) {
        Map<String, Integer> stok = new HashMap<>();
        stok.put("Pensil", 120);
        stok.put("Buku Tulis", 45);
        stok.put("Penghapus", 30);

        System.out.println("get(Pensil)         : " + stok.get("Pensil"));
        System.out.println("get(Penggaris)      : " + stok.get("Penggaris"));

        System.out.println("containsKey(Buku Tulis) : " + stok.containsKey("Buku Tulis"));
        System.out.println("containsValue(45)       : " + stok.containsValue(45));

        stok.put("Pensil", 100);
        System.out.println("Pensil setelah put ulang : " + stok.get("Pensil"));

        stok.remove("Penghapus");
        System.out.println("size() setelah remove    : " + stok.size());

        System.out.println("keySet() : " + stok.keySet());
        System.out.println("values() : " + stok.values());

        System.out.println("entrySet():");
        for (Map.Entry<String, Integer> entri : stok.entrySet()) {
            System.out.println("  " + entri.getKey() + " -> " + entri.getValue());
        }

        stok.forEach((barang, jumlah) ->
                System.out.println("  forEach: " + barang + " berjumlah " + jumlah));
    }
}