{"id":"hash-maps-keys-and-values","slug":"keys-and-values","title":"Keys and values","text":"A list keeps its values in a row, and you find a value by its index: `get(0)`, `get(1)` and so on. Often, though, you want a value by something else. Tickets and luggage tags name airports by three-letter codes: `ACC` is Accra, `LIS` is Lisbon. With the codes in one list and the cities in another, finding the city of `LIS` takes a linear search (module 28): go through the codes until one matches, then take the city at the same index.\n\nA **hash map** is made for this. It holds **pairs**: each **key** (here, a code) is linked to a **value** (here, a city), and you get a value by giving its key.\n\n```java\nimport java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<String, String> airports = new HashMap<>();\n        airports.put(\"ACC\", \"Accra\");\n        airports.put(\"LIS\", \"Lisbon\");\n        airports.put(\"YVR\", \"Vancouver\");\n\n        System.out.println(airports.get(\"LIS\"));\n        System.out.println(airports.get(\"YVR\"));\n        System.out.println(airports.get(\"XYZ\"));\n    }\n}\n```\n\n```output\nLisbon\nVancouver\nnull\n```\n\n- `HashMap` is in `java.util`, so it needs an import line, like `ArrayList`.\n- `HashMap<String, String>` has two types in its angle brackets: first the type of the keys, then the type of the values.\n- `put(key, value)` adds a pair to the map. `get(key)` gives the value linked to that key.\n- When the key isn't in the map, `get` gives `null` (module 22): this map has no airport with the code `XYZ`. Nothing crashes, but the program has to be ready for that `null`.\n\nWhy \"hash\"? From each key, the map works out a number, the key's **hash code**, and uses it to decide where to keep the pair. To find the key again, `get` works out the same number and looks right there, instead of going through the pairs one by one. So finding a value by its key usually takes about the same short time whether the map holds ten pairs or a million. (Module 32 says more about hash codes, and part 12 of the course builds a small hash map to show how one works inside.)\n\n### Numbers as values\n\nKeys and values can be of other types, but as with the values of a list, they must be class types: `Integer`, not `int`. A pantry counts its jars:\n\n```java\nHashMap<String, Integer> jars = new HashMap<>();\njars.put(\"marmalade\", 3);\njars.put(\"plum jam\", 5);\njars.put(\"pickles\", 4);\n\nint jam = jars.get(\"plum jam\");\nSystem.out.println(\"Plum jam: \" + jam);\nSystem.out.println(\"Marmalade and pickles: \" + (jars.get(\"marmalade\") + jars.get(\"pickles\")));\nSystem.out.println(\"Salsa: \" + jars.get(\"salsa\"));\n```\n\n```output\nPlum jam: 5\nMarmalade and pickles: 7\nSalsa: null\n```\n\nAn `Integer` from the map turns into an `int` by itself, as one from a list does. But `null` isn't a number, so the `null` of a missing key can't become an `int`. Storing it in an `int` variable crashes the program:\n\n```java\nimport java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<String, Integer> jars = new HashMap<>();\n        jars.put(\"marmalade\", 3);\n        int salsa = jars.get(\"salsa\");\n        System.out.println(\"Salsa: \" + salsa);\n    }\n}\n```\n\n```crash\nException in thread \"main\" java.lang.NullPointerException: Cannot invoke \"java.lang.Integer.intValue()\" because the return value of \"java.util.HashMap.get(Object)\" is null\n\tat Main.main(Main.java:7)\n```\n\nTo turn an `Integer` into an `int`, Java calls the Integer's `intValue()` method, and `get` gave no Integer to call it on.\n\n### A value for missing keys: getOrDefault\n\n`getOrDefault(key, fallback)` gives the value of the key, or the fallback when the key isn't in the map. For counts, 0 is the natural fallback: no salsa in the map means no jars of salsa.\n\n```java\nHashMap<String, Integer> jars = new HashMap<>();\njars.put(\"marmalade\", 3);\nint marmalade = jars.getOrDefault(\"marmalade\", 0);\nint salsa = jars.getOrDefault(\"salsa\", 0);\nSystem.out.println(marmalade + \" jars of marmalade, \" + salsa + \" jars of salsa\");\n```\n\n```output\n3 jars of marmalade, 0 jars of salsa\n```\n\n- The fallback has the type of the values. For a `HashMap<String, String>`, it's a String, such as `\"unknown\"`.\n- `getOrDefault` only reads. It doesn't add the key to the map: after it, the map still has no salsa.\n","task":"A travel desk looks up airport codes. Fill in the blanks. The program reads one airport code. If the map has it, the program prints the code and its city, like `NBO is in Nairobi`. If it doesn't, it prints `No airport with the code ` and the code. For the input `NBO`, it prints:\n\n```\nNBO is in Nairobi\n```\n","kind":"fill","seed":"import java.util.HashMap;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        HashMap<String, String> airports = new HashMap<>();\n        airports.[[put]](\"KEF\", \"Reykjavik\");\n        airports.put(\"NBO\", \"Nairobi\");\n        airports.put(\"AKL\", \"Auckland\");\n        airports.put(\"CPT\", \"Cape Town\");\n\n        String code = scanner.nextLine();\n        String city = airports.[[get]](code);\n        if (city == [[null]]) {\n            System.out.println(\"No airport with the code \" + code);\n        } else {\n            System.out.println(code + \" is in \" + city);\n        }\n    }\n}\n","solution":"import java.util.HashMap;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        HashMap<String, String> airports = new HashMap<>();\n        airports.put(\"KEF\", \"Reykjavik\");\n        airports.put(\"NBO\", \"Nairobi\");\n        airports.put(\"AKL\", \"Auckland\");\n        airports.put(\"CPT\", \"Cape Town\");\n\n        String code = scanner.nextLine();\n        String city = airports.get(code);\n        if (city == null) {\n            System.out.println(\"No airport with the code \" + code);\n        } else {\n            System.out.println(code + \" is in \" + city);\n        }\n    }\n}\n","hints":["`put(key, value)` adds a pair to the map, like the three lines after it. `get(key)` gives the value linked to a key.","For a code that isn't in the map, `get` gives `null`, so that's what `city` is compared with."],"tests":[{"name":"Test 1","stdin":"NBO\n","expect":"NBO is in Nairobi","hidden":false},{"name":"Test 2","stdin":"CPT\n","expect":"CPT is in Cape Town","hidden":true},{"name":"Test 3","stdin":"KEF\n","expect":"KEF is in Reykjavik","hidden":true},{"name":"Test 4","stdin":"XYZ\n","expect":"No airport with the code XYZ","hidden":true},{"name":"Test 5","stdin":"Nairobi\n","expect":"No airport with the code Nairobi","hidden":true}],"require":[],"forbid":[],"more":[{"task":"What does this program print? One of the foods isn't in the pantry.\n","kind":"predict","seed":"import java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<String, Integer> jars = new HashMap<>();\n        jars.put(\"plum jam\", 4);\n        jars.put(\"marmalade\", 2);\n        jars.put(\"mustard\", 6);\n        System.out.println(jars.get(\"marmalade\"));\n        System.out.println(jars.get(\"ketchup\"));\n        System.out.println(jars.getOrDefault(\"mustard\", 0));\n        System.out.println(jars.getOrDefault(\"ketchup\", 0));\n        System.out.println(jars.getOrDefault(\"plum jam\", 10) + jars.get(\"marmalade\"));\n    }\n}\n","solution":"import java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<String, Integer> jars = new HashMap<>();\n        jars.put(\"plum jam\", 4);\n        jars.put(\"marmalade\", 2);\n        jars.put(\"mustard\", 6);\n        System.out.println(jars.get(\"marmalade\"));\n        System.out.println(jars.get(\"ketchup\"));\n        System.out.println(jars.getOrDefault(\"mustard\", 0));\n        System.out.println(jars.getOrDefault(\"ketchup\", 0));\n        System.out.println(jars.getOrDefault(\"plum jam\", 10) + jars.get(\"marmalade\"));\n    }\n}\n","hints":["`get` gives the value of a key that's in the map, and `null` for one that isn't: there's no ketchup.","`getOrDefault` uses the fallback only for a missing key. Plum jam is in the map, so the last line adds its 4 jars, not 10, to the 2 jars of marmalade."],"tests":[{"name":"Output","stdin":"","expect":"2\nnull\n6\n0\n6","hidden":false}],"require":[],"forbid":[],"lines":["2","null","6","0","6"]},{"task":"Chat apps turn shortcodes such as `:tada:` into emoji. Write the method `expand(HashMap<String, String> emoji, String message)`. The map links shortcodes to the names of their emoji. The method returns the message with every word that is a shortcode in the map replaced by the emoji's name in square brackets. Other words stay as they are. The words of a message are separated by single spaces. The check runs:\n\n```java\nHashMap<String, String> emoji = new HashMap<>();\nemoji.put(\":tada:\", \"party popper\");\nemoji.put(\":rocket:\", \"rocket\");\nemoji.put(\":coffee:\", \"hot beverage\");\nSystem.out.println(expand(emoji, \"Launch day :rocket: :tada:\"));\nSystem.out.println(expand(emoji, \":coffee: first, then :eyes: code\"));\n```\n\nand expects:\n\n```\nLaunch day [rocket] [party popper]\n[hot beverage] first, then :eyes: code\n```\n","kind":"code","seed":"import java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<String, String> emoji = new HashMap<>();\n        emoji.put(\":tada:\", \"party popper\");\n        emoji.put(\":rocket:\", \"rocket\");\n        emoji.put(\":coffee:\", \"hot beverage\");\n        System.out.println(expand(emoji, \"Launch day :rocket: :tada:\"));\n        System.out.println(expand(emoji, \":coffee: first, then :eyes: code\"));\n    }\n\n    // Write the method expand here.\n}\n","solution":"import java.util.HashMap;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashMap<String, String> emoji = new HashMap<>();\n        emoji.put(\":tada:\", \"party popper\");\n        emoji.put(\":rocket:\", \"rocket\");\n        emoji.put(\":coffee:\", \"hot beverage\");\n        System.out.println(expand(emoji, \"Launch day :rocket: :tada:\"));\n        System.out.println(expand(emoji, \":coffee: first, then :eyes: code\"));\n    }\n\n    public static String expand(HashMap<String, String> emoji, String message) {\n        String result = \"\";\n        for (String word : message.split(\" \")) {\n            String name = emoji.get(word);\n            if (name == null) {\n                result += word + \" \";\n            } else {\n                result += \"[\" + name + \"] \";\n            }\n        }\n        return result.trim();\n    }\n}\n","hints":["Split the message into words with `message.split(\" \")` and go through them. `emoji.get(word)` is the name of the word's emoji, or `null` when the word isn't a shortcode in the map.","Build the result in a String: add either the name in brackets or the word itself, and then a space. At the end, `trim()` removes the last space."],"tests":[{"name":"Test 1","stdin":"","call":"HashMap<String, String> emoji = new HashMap<>();\nemoji.put(\":tada:\", \"party popper\");\nemoji.put(\":rocket:\", \"rocket\");\nemoji.put(\":coffee:\", \"hot beverage\");\nSystem.out.println(expand(emoji, \"Launch day :rocket: :tada:\"));\nSystem.out.println(expand(emoji, \":coffee: first, then :eyes: code\"));\n","expect":"Launch day [rocket] [party popper]\n[hot beverage] first, then :eyes: code","hidden":false},{"name":"Test 2","stdin":"","call":"HashMap<String, String> emoji = new HashMap<>();\nemoji.put(\":wave:\", \"waving hand\");\nemoji.put(\":fire:\", \"fire\");\nemoji.put(\":eyes:\", \"eyes\");\nSystem.out.println(expand(emoji, \":wave: :wave: hello\"));\nSystem.out.println(expand(emoji, \"this demo is :fire:\"));\nSystem.out.println(expand(emoji, \":tada: :Fire: fire :eyes:\"));\n","expect":"[waving hand] [waving hand] hello\nthis demo is [fire]\n:tada: :Fire: fire [eyes]","hidden":true},{"name":"Test 3","stdin":"","call":"HashMap<String, String> emoji = new HashMap<>();\nemoji.put(\":sparkles:\", \"sparkles\");\nSystem.out.println(expand(emoji, \":sparkles:\"));\nSystem.out.println(expand(emoji, \"nothing to see\"));\nSystem.out.println(expand(new HashMap<>(), \"no :sparkles: here\"));\n","expect":"[sparkles]\nnothing to see\nno :sparkles: here","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+String\\s+expand\\s*\\(\\s*HashMap<String,\\s*String>\\s+\\w+\\s*,\\s*String\\s+\\w+\\s*\\)","message":"Write the method as `public static String expand(HashMap<String, String> emoji, String message)`."},{"pattern":"\\.(get|getOrDefault)\\s*\\(","message":"Look each word up in the map with `get`."}],"forbid":[{"pattern":"\\.equals\\s*\\(\\s*\":","raw":true,"message":"Look the words up in the map instead of comparing them with shortcodes written in the code."}]}]}