Lesson Tuesday

We've learned a lot so far! And it's only your second day working with Java. As we've seen, many of the concepts we learned in Intro to Programming are fairly similar in Java (even if they don't have the exact same syntax). But, before we wrap up this weekend's homework, let's learn something completely brand new!

This lesson will discuss HashMaps, an incredibly common type of data in Java. And, when we begin working with the Spark framework in week 2, we'll use them constantly. So let's get familiar now!

About HashMap

HashMap is another Java data structure, just like the Array or ArrayList. They're used sort of like a phonebook, and are not so very different from key/value paired Objects in JavaScript. Consider the following usage of Map and HashMap in the REPL:

> import java.util.Map;
Imported java.util.Map

> import java.util.HashMap;
Imported java.util.HashMap

> Map<String, String> numbers = new HashMap();
java.util.Map numbers = {}

> numbers.put("michael", "503-555-1212");
java.lang.Object res1 = null

> numbers.put("jessica", "415-999-0000");
java.lang.Object res2 = null

> numbers.put("chris", "202-111-5599");
java.lang.Object res3 = null

> numbers.get("michael");
java.lang.String res4 = "503-555-1212"

> numbers.get("chris");
java.lang.String res5 = "202-111-5599"

As you can see, they contain multiple entries of corresponding data. These pieces of corresponding data are known as key-value pairs. For example, in the HashMap above, ("michael", "503-555-1212") is a key-value pair. "michael" is the key and "503-555-1212" is the value that corresponds to that key.

Creating HashMaps

1. Import the HashMap and Map classes.

To create a HashMap we need to import the HashMap class from Java's built-in java.util package. This is similar to the manner we imported the ArrayList and List packages in previous lessons.

We must include the following lines at the top of any .java files that use hash maps. Or, we can insert them manually when working in the REPL.

import java.util.Map;
import java.util.HashMap;

2. Determine what data the HashMap will contain.

Similar to the manner we declare the content of an ArrayList in angle brackets <>, we declare the data types of the key/value pairs the HashMap will store, like this:

Map<String, Integer> stringKeyIntegerValue = new HashMap<String, Integer>();
Map<Integer, Boolean> integerKeyBooleanValue = new HashMap<Integer, Boolean>();
Map<String, Object> stringKeyFlexibleValue = new HashMap<String, Object>();

The first two examples have specific types, while the third uses Object in the value spot. This allows the stringKeyFlexibleValue HashMap to accept many different kinds of information in its value spot, but always be referenced by a String key.

Generics Review

As we learned in Data Structures: ArrayList the keywords we see in the angle brackets <> above are generics. Generic types tell Java what kind of object this data structure can hold. We use generic types for a couple reasons:

  • They allow for better code clarity, so other programmers reading or using your code know what they are allowed to do with each list.
  • They allow the compiler to catch more errors during compilation, before you run your program. This saves time tracking down bugs.

3. Create a new instance of HashMap using the constructor.

We create HashMaps using their constructor with the new keyword, as seen in the examples from above:

import java.util.Map;
import java.util.HashMap;

Map<String, Integer> stringKeyIntegerValue = new HashMap<String, Integer>();
Map<Integer, Boolean> integerKeyBooleanValue = new HashMap<Integer, Boolean>();
Map<String, Object> stringKeyFlexibleValue = new HashMap<String, Object>();

Adding Content to a HashMap

We use the .put() method to store data in a HashMap. The first argument to .put() is called the key, and the second is called the value.

> import java.util.Map;
Imported java.util.Map

>  import java.util.HashMap;
Imported java.util.HashMap

> Map<Integer, String> numbersAndWords = new HashMap<Integer, String>(); 
java.util.Map<java.lang.Integer, java.lang.String> numbersAndWords = {}

> numbersAndWords.put(1, "one");
java.lang.Object res13 = null

> numbersAndWords.put(2, "two");
java.lang.Object res14 = null

> numbersAndWords.put(3, "three");
java.lang.Object res15 = null

> numbersAndWords;
java.util.HashMap res16 = {1=one, 2=two, 3=three}

In the code above we create a new HashMap of the <Integer, String> generic. This means the key will be an Integer, and the value will be a String. Then, we call put() thrice to add key-value pairs: 1 and "one", 2 and "two", and 3 and "three". When we reference numbersAndWords again, we can see it contains all three key-value pairs.

Retrieving Content from a HashMap

To retrieve information from a HashMap, we pass the key to the .get() method, and receive the value back, like this:

> numbersAndWords.get(1);
java.lang.String res17 = "one"

> numbersAndWords.get(2);
java.lang.String res18 = "two"

> numbersAndWords.get(3);
java.lang.String res19 = "three"

Map vs HashMap

While following along with this lesson, you may have noticed something familiar. When we create a new HashMap, it looks like this:

import java.util.Map;
import java.util.HashMap;

Map<Integer, String> numbersAndWords = new HashMap<Integer, String>(); 

Left of = we state Map<Integer, String>. Yet on the right we state new HashMap<Integer, String>();. We declare it as Map, yet use the more-specific HashMap constructor. Just like an ArrayList being declared as the more-general List.

Much like List is the parent class of ArrayList, Map is the parent class of HashMap. By declaring the HashMap as the broaderMap class, but defining it as the more specific HashMap we have much more flexibility if we later need to transform this object. In addition, it is considered best practice.

We'll explore this concept more when we discuss inheritance. For now, get in the habit of declaring HashMap objects as a Map, and using the HashMap constructor like we saw above.

More HashMap Methods

HashMaps have tons of other handy methods, too! Let's walk through a few more before this lesson concludes:

Returning all Keys

> words.keySet();
java.util.HashMap.keySet res = [fish, shoes, rain]
// This returns a key set that can be used in a for each loop. 

Returning All Values

> numbers.values();
java.util.HashMap.Values res = [503-555-1212, 415-999-0000, 202-111-5599]
// This returns all values in the hash map saved into an array. 

Checking if a HashMap Contains a Specific Key

> words.containsKey("fish");
java.lang.boolean res9 = true
// This returns true if the hash map contains the key.

Checking if a HashMap Contains a Specific Value

> capitols.containsValue("123-456-7890");
java.lang.boolean res10 = false
// This returns true if hash map contains the value.

Iterating Through HashMap

We can also iterate through a HashMap using the .keySet(); method, like this:

for ( Object key : numbers.keySet() ) {
  System.out.println(numbers.get(key)); 
}

Or, we can also use the .values() method, like this:

for ( Object value : words.values() ) {
  System.out.println(value);
}

Terminology


  • Generic: Introduced in 2004 as part of JDK 5.0 in order to allow the Java compiler to catch and alert us of more errors while compiling code, before we ever run our programs. Generic types tell Java what kind of object a data structure can hold.

  • Key-value Pair: A set of two linked pieces of information. The key is a unique identifier for some data, and the value is a piece of information that somehow corresponds with that key. See example below.

Examples


The following creates a HashMap object to store contact information like a phone book:

> import java.util.Map;
Imported java.util.Map

> import java.util.HashMap;
Imported java.util.HashMap

> Map<String, String> numbers = new HashMap();
java.util.Map numbers = {}

> numbers.put("michael", "503-555-1212");
java.lang.Object res1 = null

> numbers.put("jessica", "415-999-0000");
java.lang.Object res2 = null

> numbers.put("chris", "202-111-5599");
java.lang.Object res3 = null

> numbers.get("michael");
java.lang.String res4 = "503-555-1212"

> numbers.get("chris");
java.lang.String res5 = "202-111-5599"
  • numbers is a HashMap.
  • When we say numbers.put("michael", 5035551212), we are creating a new key-value pair.
    • "michael" is the key.
    • "503-555-1212" is the value.

Additional Resources