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)TechniqueStore things under a name (a key), and find them again in one jump.
By the end, you can:
- explain what a map is (key-value pairs, unique keys) and give an everyday example;
- predict what
put,get,remove,containsKey, andsizereturn on a JavaHashMap, includingputon a key that is already there; - explain how hashing lets
getcheck one bucket instead of every entry; - compute a short String's hashCode and its bucket index (capacity 16) by hand;
- spot a collision and explain how chaining keeps both keys;
- choose between
HashMap,LinkedHashMap, andTreeMapby 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.
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.
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
- Keys are unique. Each key appears once. If you store a value under a key that already exists, the old value is replaced.
- Values can repeat. Two different students can have the same grade. Two products can have the same price.
A set 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:
| Method | What 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.
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:
| Code | Read 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:
| Class | Order of keys | Built with | Speed of get and put | Use it when |
|---|---|---|---|---|
HashMap | no guaranteed order | a hash table (Parts 3 to 6) | fastest: about the same few steps, however big the map | you only look things up by key, like student ID → grade. The usual choice. |
LinkedHashMap | the order keys were first added | a hash table + a linked list through the entries | almost as fast as HashMap | the order of arrival matters, like a sign-up sheet: name → time signed up |
TreeMap | sorted by key | a red-black tree | a 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):
-
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?
LinkedHashMapkeeps the order keys were first added, which is the order of arrival. -
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?
Only fast lookup matters, so use
HashMap, the usual choice. Its order does not matter here. -
A teacher wants the class list printed alphabetically by name, with each grade. Which map fits?
TreeMapkeeps its keys sorted, so looping over it gives the names from A to Z.
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.
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:
| Term | What it means | In the picture |
|---|---|---|
| Hash function | the 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 code | the number the hash function gives for a key | 35 |
| Hash table | the row of storage slots inside a HashMap (an array) | the whole wall |
| Bucket | one slot of the hash table. It holds the entries (key-value pairs) that belong there. | one pigeonhole |
| Capacity | how 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 index | the bucket's number, from 0 to capacity − 1, worked out from the hash code | slot 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.
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 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.
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.
| A | B | C | D | E | F | G | H | I | J | K | L | M |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
| N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
| 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
Worked example: "DOG"
| Char | Code | 31 × h + code | New h |
|---|---|---|---|
| D | 68 | 31 × 0 + 68 | 68 |
| O | 79 | 31 × 68 + 79 | 2,187 |
| G | 71 | 31 × 2,187 + 71 | 67,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
Uppercase and lowercase are different characters.
'C'is 67 but'c'is 99, so "CAT" and "cat" get different hash codes.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.
Long strings can wrap around. A Java
intcan only go up to 2,147,483,647. Past that, it wraps around to the most negativeint(−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.
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 pigeonhole ), 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:
| Divide | Answer | Remainder |
|---|---|---|
| 13 ÷ 2 | 6 | 1 |
| 6 ÷ 2 | 3 | 0 |
| 3 ÷ 2 | 1 | 1 |
| 1 ÷ 2 | 0 | 1 |
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.
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.
| Key | Work | hashCode | Bucket |
|---|---|---|---|
| "Aa" | 31 × 65 + 97 | 2,112 | 0 |
| "BB" | 31 × 66 + 66 | 2,112 | 0 |
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 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 Grades program in Part 2 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 the three-maps output from Part 2 .
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.
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.
| Char | Code | 31 × h + code | New h |
|---|---|---|---|
| B | 66 | 31 × 0 + 66 | 66 |
| A | ? | 31 × ? + ? | ? |
| T | ? | 31 × ? + ? | ? |
Show the answer
| Char | Code | 31 × h + code | New h |
|---|---|---|---|
| B | 66 | 31 × 0 + 66 | 66 |
| A | 65 | 31 × 66 + 65 | 2,111 |
| T | 84 | 31 × 2,111 + 84 | 65,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
| Char | Code | 31 × h + code | New h |
|---|---|---|---|
| A | 65 | 31 × 0 + 65 | 65 |
| b | 98 | 31 × 65 + 98 | 2,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
| Call | Returns | Why |
|---|---|---|
| map.put("A1", "Ana") | null | new key, so there is no old value |
| map.put("B2", "Ben") | null | new key |
| map.put("C3", "Ana") | null | new 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") | null | no such key |
| map.remove("B2") | "Ben" | B2 deleted; remove returns the value it took out |
| map.size() | 2 | A1 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 Part 2 : 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:
| Vote | containsKey? | What runs | counts after |
|---|---|---|---|
| Ana | false | put("Ana", 1) | {Ana=1} |
| Ben | false | put("Ben", 1) | {Ana=1, Ben=1} |
| Ana | true | put("Ana", 1 + 1) | {Ana=2, Ben=1} |
| Carla | false | put("Carla", 1) | {Ana=2, Ben=1, Carla=1} |
| Ben | true | put("Ben", 1 + 1) | {Ana=2, Ben=2, Carla=1} |
| Ana | true | put("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
containsKeycheck and always writingcounts.put(name, counts.get(name) + 1);. For a new name,getreturnsnull, and Java cannot add 1 to nothing. The program stops at the very first vote withNullPointerException.
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.
-
A map stores data as:
Each entry in a map is a key plus the value it points to. You look things up by key, not by index.
-
Which statement about a map is true?
Each key appears once (so it can identify one entry). Different keys can share the same value, like two students with the same grade.
-
You call
put("CAT", "Puspin"), but "CAT" is already a key with value "Persian". What happens?puton an existing key replaces its value. The map still has one "CAT". -
What does
getreturn for a key that is not in the map?getreturnsnullwhen the key is missing. UsecontainsKeyto check first. -
What is "AB".hashCode()? (A = 65, B = 66)
h = 65, then 31 × 65 + 66 = 2015 + 66 = 2081.
-
With capacity 16, a key has hashCode 100. Which bucket?
100 in binary is 1100100; the last 4 bits are 0100 = 4. Shortcut: 100 ÷ 16 = 6 remainder 4.
-
Two different keys land in the same bucket. This is called:
A collision. The keys are different, so both are kept: the bucket holds a small chain (linked list) of entries.
-
Why is a HashMap fast at
get?Hashing turns the key into a bucket number, so
getjumps straight to one bucket instead of searching the whole map.
Score: 0 / 8
Key takeaways
- A map stores key-value pairs. Keys are unique; values can repeat.
putinserts or replaces (and returns the old value, or null),getlooks up (or returns null),removedeletes.- A HashMap hashes the key to pick a bucket, so it can jump straight to it.
- String hashCode: start at 0, then h = 31 × h + code for each character.
- Bucket index (course formula) = hashCode & (capacity − 1) = the last 4 bits when capacity is 16.
- 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
- Oracle, "String.hashCode()," Java SE 21 API documentation (gives the formula s[0]·31n−1 + … + s[n−1]). docs.oracle.com
- Oracle, "HashMap (Java SE 21 & JDK 21)," Java API documentation (default initial capacity 16). docs.oracle.com
- OpenJDK,
java.util.HashMapsource code (thehash()method that mixes in the high bits). - T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein, Introduction to Algorithms, 4th ed. MIT Press, 2022, Chapter 11 (Hash tables).
- Programiz, "Hash Table." programiz.com/dsa/hash-table