ma'am audrey's lessons

ITC 120 › Final › Sets and maps

Sets and maps · Lesson 2 of 2

Maps and Hashing in Java

This lesson teaches:Abstract data type (ADT)Technique

Store things under a name (a key), and find them again in one jump.

By the end, you can:

  1. explain what a map is (key-value pairs, unique keys) and give an everyday example;
  2. predict what put, get, remove, containsKey, and size return on a Java HashMap, including put on a key that is already there;
  3. explain how hashing lets get check one bucket instead of every entry;
  4. compute a short String's hashCode and its bucket index (capacity 16) by hand;
  5. spot a collision and explain how chaining keeps both keys;
  6. choose between HashMap, LinkedHashMap, and TreeMap by the order you need.

You need: the Sets lesson, a calculator, and scratch paper.

Part 1 · The idea

What a map is

Think of the sari-sari store

The store owner does not search every shelf for the price of a sachet of coffee. She has a price list: item name (key) → price (value). If the price goes up, she does not add a second line; she crosses out the old price and writes the new one. One item, one price.

The price list is a mapItem (key)Price (value)coffee sachetpancit canton₱18egg₱9banana₱9₱10₱12Each item is written once.A new price replaces the old one.Egg and banana are both ₱9: values can repeat.
The store's price list works like a map: look up the item (key) to get its price (value).

Why do we need maps?

In arrays and lists, you find an item by its position (its index), like grades[2]. But in real life you usually know a name or an ID, not a position. To find the grade of student 2025-0101 in a list, you would have to check the items one by one.

Where is the grade of 2025-0101?In a list: by position[0]2025-0012891✗[1]2025-0047922✗[2]2025-0101893[3]2025-015085✓You don't know its position, so check one by one.In a map: by keykeyvalue2025-0012892025-0047922025-0101892025-015085get("2025-0101")one jumpAsk by the key you know. No counting positions.
A list makes you search by position. A map lets you ask by the key you already know.

A map fixes this: you store each value under a key, and later you ask for it by that same key.

Map

A map is an abstract data type (an idea of what you can do, not how it is stored) that stores key-value pairs. Each key is unique and is used to find its value. Maps are also called dictionaries or associative arrays.

In plain words:

  • Key: the thing you look things up by (a student ID, an item name). Like a word in a dictionary.
  • Value: the information stored under that key (a grade, a price). Like the word's meaning.
  • Key-value pair (also called an entry): one key together with its value, like one line of the price list.

Maps are everywhere. Here are some examples of keys and the values they lead to:

Key→Value
student ID number→student's name
product barcode→price
contact name→phone number
English word→its meaning

The two rules

  1. Keys are unique. Each key appears once. If you store a value under a key that already exists, the old value is replaced.
  2. Values can repeat. Two different students can have the same grade. Two products can have the same price.

A from the last lesson is like a map with only keys: it answers "is it there?", while a map answers "is it there, and what is its value?"

Part 2 · In code

Maps in Java

Java's Map interface (the list of methods every map class must have) has these main operations:

MethodWhat it does
put(key, value)insert a pair, or replace the value if the key exists; returns the old value (null if the key was new)
get(key)look up the value; returns null (Java's word for "nothing") if the key is missing
remove(key)delete the pair with that key; returns its value (null if the key was missing)
containsKey(key)true if the key is in the map
size()number of pairs

Like sets, Java has three main map types: HashMap (no guaranteed order, the usual choice; Part 3 explains the "hash"), LinkedHashMap (keeps the order keys were first added), and TreeMap (sorted by key, built on a red-black tree). The end of this part compares them.

The picture below shows the most important rule of put: when the key is already there, the value is replaced and the map does not grow.

map.put("A1", "Andy")BeforekeyvalueA1AnaB2BenAfterkeyvalueA1AndyB2Ben2 entriesstill 2 entriesput returns "Ana"Same key: the value is replaced. No second A1.
put on an existing key replaces the value and returns the old one.
import java.util.HashMap;
import java.util.Map;

public class Grades {
  public static void main(String[] args) {
    Map<String, Integer> grades = new HashMap<>();

    grades.put("2025-0012", 89);   // insert
    grades.put("2025-0047", 92);
    grades.put("2025-0101", 89);   // same value is OK
    grades.put("2025-0012", 95);   // same key: replaces 89

    System.out.println(grades.get("2025-0012"));  // 95
    System.out.println(grades.get("2025-9999"));  // null
    System.out.println(grades.containsKey("2025-0047")); // true
    System.out.println(grades.size());            // 3

    grades.remove("2025-0047");                   // delete
    System.out.println(grades.size());            // 2

    for (String id : grades.keySet()) {           // order not guaranteed
      System.out.println(id + " -> " + grades.get(id));
    }
  }
}

▶ Try it in the Programiz online compiler: tap Copy, paste it there, change public class Grades to public class Main (Programiz saves your code as Main.java, and Java needs the file name and the public class name to match), then press Run.

Output (the last two lines can come out in another order; Part 6 explains why):

95
null
true
3
2
2025-0101 -> 89
2025-0012 -> 95

Here is how to read the new lines aloud:

CodeRead it as
Map<String, Integer> grades"grades is a map from String keys to Integer values"
= new HashMap<>()"make a new, empty HashMap" (the empty <> copies the types from the left side)
grades.put("2025-0012", 89)"store 89 under the key 2025-0012"
grades.get("2025-0012")"give me the value stored under 2025-0012"
for (String id : grades.keySet())"for each key id in the map's keys"

Why Integer and not int? The types inside < > must be classes. Integer is the class version of int, and Java converts between the two for you, so put("2025-0012", 89) just works. (You first met this in the Stacks lesson.)

HashMap, LinkedHashMap, or TreeMap?

All three classes have the same Map methods, so put, get, and remove work the same way in each. They differ in one thing you can see: the order of the keys when you loop over the map or print it. This program puts the same 4 pairs, in the same order, into one map of each kind:

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class ThreeMaps {
  static void addGrades(Map<String, Integer> m) {
    m.put("Rico", 88);   // same 4 pairs,
    m.put("Bea", 92);    // same order,
    m.put("Liza", 85);   // for every map
    m.put("Dan", 90);
    m.put("Rico", 91);   // same key: new value, same place
  }

  public static void main(String[] args) {
    Map<String, Integer> hash = new HashMap<>();
    Map<String, Integer> linked = new LinkedHashMap<>();
    Map<String, Integer> tree = new TreeMap<>();
    addGrades(hash);
    addGrades(linked);
    addGrades(tree);
    System.out.println("HashMap:       " + hash);
    System.out.println("LinkedHashMap: " + linked);
    System.out.println("TreeMap:       " + tree);
    System.out.println(tree.get("Liza"));
  }
}

▶ Try it in the Programiz online compiler: tap Copy, paste it there, change public class ThreeMaps to public class Main (Programiz saves your code as Main.java, and Java needs the file name and the public class name to match), then press Run.

Output (checked in Java). Java prints a map as key=value pairs inside curly braces:

HashMap:       {Liza=85, Dan=90, Rico=91, Bea=92}
LinkedHashMap: {Rico=91, Bea=92, Liza=85, Dan=90}
TreeMap:       {Bea=92, Dan=90, Liza=85, Rico=91}
85

Read the three lines one at a time:

  • HashMap printed Liza, Dan, Rico, Bea: neither the order added nor A to Z. It is bucket order, which Part 6 explains.
  • LinkedHashMap printed Rico, Bea, Liza, Dan: the order the keys were first added. The second put("Rico", 91) changed Rico's value but not his place.
  • TreeMap printed Bea, Dan, Liza, Rico: sorted by key, A to Z.

Here is when to use each one:

ClassOrder of keysBuilt withSpeed of get and putUse it when
HashMapno guaranteed ordera hash table (Parts 3 to 6)fastest: about the same few steps, however big the mapyou only look things up by key, like student ID → grade. The usual choice.
LinkedHashMapthe order keys were first addeda hash table + a linked list through the entriesalmost as fast as HashMapthe order of arrival matters, like a sign-up sheet: name → time signed up
TreeMapsorted by keya red-black treea bit slower: grows with the tree's height (about log n)you need the keys sorted, like an alphabetical class list: name → grade

Good to know: a TreeMap sorts String keys by their character codes, so every capital letter comes before every small letter ("Zed" comes before "ana"). Write names the same way, like "Ana" and "Zed", to get A to Z.

Which map fits? Three quick situations (for self-study, not for grading):

  1. The canteen records each student's name and the time they lined up, and must call them in the order they arrived. Which map fits?

  2. The registrar looks up a grade by student ID thousands of times a day and never prints the list in any order. Which map fits?

  3. A teacher wants the class list printed alphabetically by name, with each grade. Which map fits?

Score: 0 / 3

Part 3 · How it is so fast

Hashing

Imagine a map with 10,000 students stored in one long list. To find one ID, you might have to check all 10,000. A HashMap avoids that with a trick called hashing. Here is the same trick in a place you know.

Think of the faculty pigeonholes

The faculty office has 16 pigeonholes for mail, numbered 0 to 15. Instead of one big pile, the office uses a rule that turns each name into a pigeonhole number: add up the letters' places in the alphabet (A = 1, B = 2, …, Z = 26), divide by 16, and keep the remainder. A letter for Tan goes into slot 3. To find Tan's mail later, use the same rule and check one pigeonhole, not the whole pile.

16 pigeonholes, one ruleto: TanThe office's rule1. Add up the letters' alphabet places:T 20 + A 1 + N 14 = 352. Divide by 16 and keep the remainder:35 ÷ 16 = 2 remainder 30123456789101112131415slot 3Looking for Tan's mail later? Same rule, same slot 3.
A made-up office rule turns the name Tan into slot 3. (Java uses a different rule: Parts 4 and 5 show it.)

Hashing

Hashing means using a fixed rule to turn a key into a number, and using that number to decide where the key is stored. Because the rule never changes, the same key always gives the same number, so you always know where to look.

Hashing comes with a few new words. The table below matches each one to the pigeonhole picture:

TermWhat it meansIn the picture
Hash functionthe fixed rule that turns a key into a number. In Java, it is the hashCode() method (Part 4).the office's rule, step 1
Hash codethe number the hash function gives for a key35
Hash tablethe row of storage slots inside a HashMap (an array)the whole wall
Bucketone slot of the hash table. It holds the entries (key-value pairs) that belong there.one pigeonhole
Capacityhow many buckets there are. A new HashMap starts with 16; when it holds more than 12 entries (3/4 of 16), it doubles to 32 and moves every key to its new bucket.16 pigeonholes
Bucket indexthe bucket's number, from 0 to capacity − 1, worked out from the hash codeslot 3 (step 2)

So put and get both do the same two steps: (1) compute the key's hash code, (2) turn it into a bucket index. Then they go straight to that bucket.

"CAT"Step 1: hashCode()h = 31 × h + code66486Step 2: & 15(keep the last 4 bits)0123456789101112131415buckets 0 to 15put and get both go straight to bucket 6.
The two steps for the key "CAT". Parts 4 and 5 show how to do each step by hand.

Why get finds what put stored

Remember: the same key always gives the same number. When you put("CAT", …), it goes to bucket 6. When you later call get("CAT"), Java computes bucket 6 again and looks there. It never has to look anywhere else.

The bucket keeps the whole pair (key and value), not just the value. That way, get can check that the key really is "CAT" before giving back its value.

Part 4 · Step 1 by hand

A String's hashCode

hashCode

In Java, every object has a method called hashCode(). It returns an int: that object's hash code. For a String, Java computes it from the string's characters with a fixed formula, so two equal strings always get the same hash code.

Quick review: characters and strings

A character (char in Java) is one letter, digit, symbol, or space, written in single quotes: 'D'. A string (String) is a sequence of characters in order, written in double quotes: "DOG". Each character has a position, its index, starting at 0.

Inside the computer, every character is stored as a number called its character code (from the ASCII or Unicode table). For example, 'D' is 68.

The string "DOG"indexcharcode0'D'681'O'792'G'71A string is characters in order.Each character is stored as a number: its code.
The string "DOG": three characters in order, each stored as a number.

The hashCode rule

The goal is to turn the whole string into one number that depends on every character and on their order. We build that number one character at a time and call it h (short for "hash"). Think of h as a running total: the hash code so far.

The hashCode rule

h = 0
for each character, left to right: h = 31 × h + code

Read it as: "Start with 0. For each character, multiply what you have so far by 31, then add the character's code." When there are no characters left, h is the hash code.

Building the hash code of "CAT"teal = h so far · orange = this character's codeStart:h = 0C31 × 0 + 67= 67A31 × 67 + 65= 2,142T31 × 2,142 + 84= 66,486No characters left, so the hash code is 66,486.
Each row uses the h from the row above. This is the same "CAT" → 66,486 from the .

Why multiply by 31? If we only added the codes, "DOG" and "GOD" would get the same number, because they have the same letters. Multiplying by 31 before each add makes each character's position matter: "DOG" gives 67,868, but "GOD" gives 70,748. (Java's designers chose 31 because it is an odd prime that mixes the numbers well, and computers can multiply by it quickly.)

Good to know: Java's documentation writes the same rule as one long formula, s[0]·31n−1 + s[1]·31n−2 + … + s[n−1], where s[0] is the code of the first character, s[1] the second, and n is the number of characters. The running total gives the same answer in smaller steps.

Character codes you need

Uppercase letters go from A = 65 to Z = 90. Lowercase letters are 32 more: a = 97 to z = 122. The digit 0 is 48, and a space is 32.

ABCDEFGHIJKLM
65666768697071727374757677
NOPQRSTUVWXYZ
78798081828384858687888990

Worked example: "DOG"

CharCode31 × h + codeNew h
D6831 × 0 + 6868
O7931 × 68 + 792,187
G7131 × 2,187 + 7167,868

"DOG".hashCode() = 67,868

Check with the long formula: 68 × 31² + 79 × 31 + 71 = 68 × 961 + 2,449 + 71 = 65,348 + 2,449 + 71 = 67,868. Same answer.

Exam traps

  1. Uppercase and lowercase are different characters. 'C' is 67 but 'c' is 99, so "CAT" and "cat" get different hash codes.

    "CAT"codes 67 65 8466,486hash code"cat"codes 99 97 11698,262hash code≠
  2. Start with h = 0. The first step is 31 × 0 + code, which is just the first character's code. Don't multiply the first code by 31.

    ✗ Wrong31 × 68= 2,108✓ Right31 × 0 + 68= 68first code multipliedh starts at 0
  3. Long strings can wrap around. A Java int can only go up to 2,147,483,647. Past that, it wraps around to the most negative int (−2,147,483,648) and keeps counting up from there, like an odometer rolling over from 99999 to 00000. That is how a hash code can become negative. Keys of up to 5 letters never get that big, so short keys are safe. Longer ones can wrap, like "Charlie" in Part 7.

    Odometer99999+100000Java int2,147,483,647+1−2,147,483,648largest intmost negative int

Part 5 · Step 2 by hand

From hash code to bucket

Step 1 gave us a hash code, like 67,868 for "DOG". Step 2 turns it into a bucket index. Recall from Part 3: a bucket is one slot of the hash table (one ), and the capacity is how many buckets there are (16). So the bucket index must be a number from 0 to 15, but 67,868 is far bigger. In this course, we use the same formula as the Java code you will see in Part 7:

The bucket rule

bucket index = hashCode & (capacity − 1)

With capacity 16: hashCode & 15.

To read this rule, you need two ideas: binary and the bitwise AND.

Binary and bits

Computers store numbers in binary: using only the digits 0 and 1. Each digit is called a bit. From the right, the bits are worth 1, 2, 4, 8, 16, … (each place is worth twice the one before). For example, 13 in binary is 1101 = 8 + 4 + 0 + 1.

How to turn a number into binary

Divide by 2 again and again, writing down each remainder, until you reach 0. Then read the remainders from the bottom up. For 13:

DivideAnswerRemainder
13 ÷ 261
6 ÷ 230
3 ÷ 211
1 ÷ 201

Bottom up: 1101. Big numbers take many rows, which is why Way 2 below is faster.

Bitwise AND (&)

The bitwise AND, written &, lines up two numbers in binary and compares them bit by bit. The answer has a 1 only where both numbers have a 1; everywhere else it has a 0.

Since 15 in binary is 1111, & 15 simply keeps the last 4 bits of the hash code and turns all the others into 0.

67868 & 15, bit by bit6786810000100100011100& 1500000000000001111=000000000000011008421worth:A 1 stays only where BOTH rows have a 1.15 has 1s only in the last 4 places, so only they survive.1100 = 8 + 4 = 12, so the bucket is 12.
"DOG" has hash code 67,868. Lined up bit by bit with 15, only its last 4 bits survive.

Worked example: "DOG" → bucket 12

There are two ways to get the answer. Both give the same result for a positive hash code.

Way 1: binary

Convert 67,868 to binary (divide by 2 repeatedly and read the remainders from bottom to top):

1 0000 1001 0001 1100

Keep the last 4 bits: 1100 = 8 + 4 = 12.

Way 2: remainder shortcut

The last 4 bits are the remainder when you divide by 16:

67,868 ÷ 16 = 4241 remainder 12

So the bucket is 12. Much faster with a calculator!

Why does this work? 16 = 24, so dividing by 16 is like chopping off the last 4 bits. The chopped-off bits are the remainder. This only works because the capacity is a power of 2 (16, 32, 64…), which is why HashMap capacities always are.

For exams in this course, use this course formula. (Java's real HashMap adds one mixing step first, so the bucket it uses inside can be different. The Extra below explains it, if you are curious.)

Extra: why Java's real HashMap can pick another bucket

Extra topic. Not needed for this course's quizzes or exams.

The formula above is what this course uses, and what the Java program in Part 7 prints. The real HashMap adds one small step first: it mixes the hash code's front bits into its last 4 bits, so keys that differ only in their front bits still spread out over the buckets. Then it does & (capacity − 1). So the bucket Java actually uses inside can be different: "CAT" gets bucket 6 with our formula but bucket 7 inside Java's HashMap. For exams in this course, use the course formula.

For the curious: the extra step is h ^ (h >>> 16). >>> 16 slides the bits 16 places to the right, and ^ (XOR) gives a 1 where the two bits are different.

Part 6 · When two keys clash

Collisions

There are only so many buckets (16 to start) but endless possible keys, so sometimes two different keys get the same bucket index.

Collision

A collision happens when two different keys get the same bucket index. It is normal and expected, and the map must still keep both keys.

Here is a famous one: "Aa" and "BB" have exactly the same hash code.

KeyWorkhashCodeBucket
"Aa"31 × 65 + 972,1120
"BB"31 × 66 + 662,1120

The keys are different, so the map must keep both. A common way is chaining: each bucket holds a small linked list (a chain) of entries, one after another. To get "BB", Java goes to bucket 0 and walks its short chain, comparing keys until it finds "BB" (see the picture below). (The Hashing and hash tables lesson shows more ways to handle collisions, and how to build a hash table yourself.)

get("BB"): go to bucket 0, walk its chain✗ not BB✓ foundbucket0AafirstBBsecond1empty2empty⋮6CATPuspin⋮12DOGAspin⋮15emptyAa and BB share bucket 0 (a collision). Both are kept.
Chaining: a bucket can hold more than one entry. get checks only that bucket's chain.

Why a HashMap has no order

When you loop over a HashMap's keys, Java walks its buckets in number order (0, 1, 2, …) and reads each chain. So the keys come out in bucket order, not in the order you added them. That is why the printed 2025-0101 before 2025-0012: inside Java's real HashMap (with the mixing step from Part 5), 2025-0101 sits in bucket 1 and 2025-0012 in bucket 2.

Need the keys in the order you added them? Use LinkedHashMap. Sorted by key? Use TreeMap. Compare the three in .

Extra: what Java does with a very long chain

Extra topic. Open this if you are curious.

Java's HashMap uses chaining. If one chain gets very long (8 or more entries, in a big enough table), Java turns that chain into a small red-black tree, so even a crowded bucket stays quick to search.

Part 7 · Look inside

HashMap lab

Try these in order: put CAT → Persian, then DOG → Aspin, then CAT → Puspin (watch the value get replaced). Then put Aa and BB to see a collision. Try get on a key that is not there.

HashMap lab: look inside

Type a key and a value, then tap put. Watch how the key becomes a number, then a bucket. Try get and remove too.

Quick tries:

Buckets (capacity 16)

    The same thing in Java

    This program puts three entries and prints each key's hash code and bucket, using the course formula:

    import java.util.HashMap;
    
    public class HashMapExample {
      static int capacity = 16;
    
      static void addEntry(HashMap<String, String> map,
                           String key, String value) {
        map.put(key, value);
        int hashCode = key.hashCode();
        int bucketIndex = hashCode & (capacity - 1);
        System.out.println(key + " -> " + value
            + " | hash " + hashCode + " | bucket " + bucketIndex);
      }
    
      static void removeEntry(HashMap<String, String> map, String key) {
        if (map.containsKey(key)) {
          int bucketIndex = key.hashCode() & (capacity - 1);
          map.remove(key);
          System.out.println("Removed " + key + " from bucket " + bucketIndex);
        } else {
          System.out.println(key + " not found");
        }
      }
    
      public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        addEntry(map, "Alice", "Developer");
        addEntry(map, "Bob", "Designer");
        addEntry(map, "Charlie", "Manager");
        removeEntry(map, "Bob");
        removeEntry(map, "Dana");
      }
    }

    ▶ Try it in the Programiz online compiler: tap Copy, paste it there, change public class HashMapExample to public class Main (Programiz saves your code as Main.java, and Java needs the file name and the public class name to match), then press Run.

    Output:

    Alice -> Developer | hash 63350368 | bucket 0
    Bob -> Designer | hash 66965 | bucket 5
    Charlie -> Manager | hash -1891246254 | bucket 2
    Removed Bob from bucket 5
    Dana not found

    Notice "Charlie": its hash code is negative because it wrapped around, but & 15 still gives a bucket from 0 to 15.

    Good to know: a negative hash code by hand

    For a negative hash code, Java's remainder is negative too: −1,891,246,254 % 16 = −14. Add 16 to get the bucket: −14 + 16 = 2, the same as & 15. (If the remainder is 0, the bucket is 0.)

    Part 8 · Your turn

    Practice

    Problems 1 to 3 are on paper. After them, a short coding task uses a map in Java.

    Problem 1

    Compute "BAT".hashCode(), then its bucket index with capacity 16. Show your work.

    To get you started, the first row is done. Fill in the rest on paper.

    CharCode31 × h + codeNew h
    B6631 × 0 + 6666
    A?31 × ? + ??
    T?31 × ? + ??
    Show the answer
    CharCode31 × h + codeNew h
    B6631 × 0 + 6666
    A6531 × 66 + 652,111
    T8431 × 2,111 + 8465,525

    hashCode = 65,525

    Binary: 1111 1111 1111 0101 → last 4 bits 0101 = 5. Shortcut: 65,525 ÷ 16 has remainder 5. Bucket 5.

    Problem 2

    Compute "Ab".hashCode(), then its bucket index with capacity 16. Show your work.

    Show the answer
    CharCode31 × h + codeNew h
    A6531 × 0 + 6565
    b9831 × 65 + 982,113

    hashCode = 2,113

    Binary: 1000 0100 0001 → last 4 bits 0001 = 1. Shortcut: 2,113 ÷ 16 has remainder 1. Bucket 1.

    Problem 3: trace the map

    The map starts empty, and its keys and values are Strings. What does each line do, and what does it return? (Remember: put and remove return a value too.)

    map.put("A1", "Ana")
    map.put("B2", "Ben")
    map.put("C3", "Ana")
    map.put("A1", "Andy")
    map.get("A1")
    map.get("D4")
    map.remove("B2")
    map.size()
    Show the answer
    CallReturnsWhy
    map.put("A1", "Ana")nullnew key, so there is no old value
    map.put("B2", "Ben")nullnew key
    map.put("C3", "Ana")nullnew key; "Ana" twice is fine (values can repeat)
    map.put("A1", "Andy")"Ana"A1 exists: "Ana" is replaced by "Andy", and put returns the old value
    map.get("A1")"Andy"
    map.get("D4")nullno such key
    map.remove("B2")"Ben"B2 deleted; remove returns the value it took out
    map.size()2A1 and C3 are left

    Your coding task: count the votes

    Room 1 is choosing its class president. Each vote is a name on a slip of paper. On the board, you would write each name once and add a mark beside it for every vote. A map does the same: name → number of votes.

    Finish the loop in countVotes in about 5 lines where it says YOUR CODE. You need only three methods from : containsKey, get, and put. The map is a TreeMap, so the names print sorted and your output can match the expected comments exactly. Run it: each printed line must match its expected comment.

     1import java.util.Map;
     2import java.util.TreeMap;
     3
     4public class Main {
     5  // Counts how many votes each name got.
     6  static Map<String, Integer> countVotes(String[] votes) {
     7    Map<String, Integer> counts = new TreeMap<>(); // sorted by name
     8    for (String name : votes) {
     9      // YOUR CODE:
    10      // If name is already a key, put back its count + 1.
    11      // If not, this is its first vote: put 1.
    12
    13    }
    14    return counts;
    15  }
    16
    17  public static void main(String[] args) {
    18    String[] room1 = {"Ana", "Ben", "Ana", "Carla", "Ben", "Ana"};
    19    System.out.println(countVotes(room1));
    20    // expected: {Ana=3, Ben=2, Carla=1}
    21
    22    String[] room2 = {"Dan"};
    23    System.out.println(countVotes(room2)); // expected: {Dan=1}
    24
    25    String[] room3 = {};                   // nobody voted
    26    System.out.println(countVotes(room3)); // expected: {}
    27
    28    String[] room4 = {"Ben", "ben", "Ben"};
    29    System.out.println(countVotes(room4)); // expected: {Ben=2, ben=1}
    30  }
    31}

    ▶ This program runs as is. Try it in the Programiz online compiler: tap Copy, paste it there, and press Run.

    Show one possible answer
     1for (String name : votes) {
     2  if (counts.containsKey(name)) {
     3    counts.put(name, counts.get(name) + 1); // one more vote
     4  } else {
     5    counts.put(name, 1);                    // first vote
     6  }
     7}

    Output, one per line: {Ana=3, Ben=2, Carla=1}, {Dan=1}, {}, and {Ben=2, ben=1}.

    Here is the map after each vote in room 1:

    VotecontainsKey?What runscounts after
    Anafalseput("Ana", 1){Ana=1}
    Benfalseput("Ben", 1){Ana=1, Ben=1}
    Anatrueput("Ana", 1 + 1){Ana=2, Ben=1}
    Carlafalseput("Carla", 1){Ana=2, Ben=1, Carla=1}
    Bentrueput("Ben", 1 + 1){Ana=2, Ben=2, Carla=1}
    Anatrueput("Ana", 2 + 1){Ana=3, Ben=2, Carla=1}

    The map never gets two Ana entries: put on a key that is already there replaces the old count. In room 4, "Ben" and "ben" are two different keys, because Java compares keys letter by letter, and B and b have different character codes. The capital comes first in a TreeMap.

    Here are two common mistakes:

    • Writing only counts.put(name, 1);. Each vote replaces the count with 1 again, so room 1 prints {Ana=1, Ben=1, Carla=1}.
    • Skipping the containsKey check and always writing counts.put(name, counts.get(name) + 1);. For a new name, get returns null, and Java cannot add 1 to nothing. The program stops at the very first vote with NullPointerException.

    Good to know: counts.getOrDefault(name, 0) gives 0 instead of null when the key is missing. With it, the whole if/else fits in one line: counts.put(name, counts.getOrDefault(name, 0) + 1);

    Part 9 · Check yourself

    Self-check

    Not graded. Tap an answer to see if you got it, and read why.

    1. A map stores data as:

    2. Which statement about a map is true?

    3. You call put("CAT", "Puspin"), but "CAT" is already a key with value "Persian". What happens?

    4. What does get return for a key that is not in the map?

    5. What is "AB".hashCode()? (A = 65, B = 66)

    6. With capacity 16, a key has hashCode 100. Which bucket?

    7. Two different keys land in the same bucket. This is called:

    8. Why is a HashMap fast at get?

    Score: 0 / 8

    Key takeaways

    1. A map stores key-value pairs. Keys are unique; values can repeat.
    2. put inserts or replaces (and returns the old value, or null), get looks up (or returns null), remove deletes.
    3. A HashMap hashes the key to pick a bucket, so it can jump straight to it.
    4. String hashCode: start at 0, then h = 31 × h + code for each character.
    5. Bucket index (course formula) = hashCode & (capacity − 1) = the last 4 bits when capacity is 16.
    6. Different keys in the same bucket = a collision; the bucket keeps a chain of entries.

    Next lesson: Linear and binary search, then the two sorting lessons. After them, Hashing and hash tables builds on this lesson: you build a hash table yourself and learn more ways to handle collisions.

    References

    1. Oracle, "String.hashCode()," Java SE 21 API documentation (gives the formula s[0]·31n−1 + … + s[n−1]). docs.oracle.com
    2. Oracle, "HashMap (Java SE 21 & JDK 21)," Java API documentation (default initial capacity 16). docs.oracle.com
    3. OpenJDK, java.util.HashMap source code (the hash() method that mixes in the high bits).
    4. T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to Algorithms, 4th ed. MIT Press, 2022, Chapter 11 (Hash tables).
    5. Programiz, "Hash Table." programiz.com/dsa/hash-table