Hi! In this lesson, we'll take a closer review at Java HashMap. Previously, we've studied data structures where the elements are stored as themselves. In an array or an ArrayList/LinkedList, we store some number of elements. But what if our task changes a little?
Imagine the following task: create a list of 100 people that stores each person's name and passport number. In principle, this isn't so difficult. For example, you can stuff both into a string, and then create a list of these strings:
"Amelia Aguilar, 4211 717171".
But this solution has two drawbacks. First, we may need the ability to search by passport number. And this will be problematic given this information storage format.
Second, nothing stops us from creating two different people with the same passport number. And this is our solution's most serious shortcoming. This should never be allowed: no two people have the same passport number.
A new data structure comes to our aid: Map. It is also known as an "associative array", but this term is used infrequently. More commonly, it is called a "dictionary" or "map". :)
How is it fundamentally different than the data structures we've considered previously?
Above all, in the fact that data in a Map is stored as key-value pairs. Anything can serve as keys and values: numbers, strings, or objects of other classes.
Today we'll study the most common implementation of the Map class: Java HashMap.
So, what do we need to know about HashMap in Java?
It is very easy to create:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
}
Here we create a dictionary that stores elements as "number-string" pairs. The number will act as the key, and the string as the value. Also we specify the key type (Integer) and the value type (String).
Why?
First, a HashMap key is always unique. This suits us perfectly, since we can use the passport number as the key and avoid duplicates. The value will be a string with the full name (different people can have the same name; that's nothing for us to worry about).
Adding a new pair to the HashMap looks like this:
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames);
}
}
We use the method put() for this. In addition, HashMap overrides the toString() method, so it can be displayed on the console. The output will look like this:
{212133=Bridget Logan, 8082771=Donald John Trump, 162348=Ivan the Great}
Now let's verify whether the keys are really unique?
Let's try adding a new element with a key that has already been used in the map:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
passportsAndNames.put(162348, "Albert Kent");// This key has already been used
System.out.println(passportsAndNames);
}
Output:
{212133=Bridget Logan, 8082771=Donald John Trump, 162348=Albert Kent}
As you can see, the previous value associated with the key 162348 was overwritten.
We use the term "key" for a reason. Values in a HashMap are accessed using the key, but not the other way around. The key cannot be obtained using a value, since the values may not be unique.
This can be clearly seen when getting or removing an element from the HashMap:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
String lidiaName = passportsAndNames.get(212133);
System.out.println(lidiaName);
passportsAndNames.remove(162348);
System.out.println(passportsAndNames);
}
To get a value or remove a pair from the dictionary, we must pass to the get() and remove() the unique key that corresponds to the value. Unlike arrays and lists, a HashMap in Java has no numeric indices: values are accessed using the key.
Console output:
Bridget Logan
{212133=Bridget Logan, 8082771=Donald John Trump}
The ArrayList and LinkedList classes let us check whether the list contains any particular element.
Java HashMap lets us do this. What's more, we can do this for both members of the pair: This is what the containsKey() (checks for a key) and containsValue() (checks for a value) methods are for.
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames.containsKey(11111));
System.out.println(passportsAndNames.containsValue("Donald John Trump"));
}
Output:
false
true
Another convenient feature of HashMap in Java is the fact that you can get separate lists of all the keys and all the values.
This is accomplished with the keySet() and values() methods:
public class Main {
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
Set keys = passportsAndNames.keySet();
System.out.println("Keys: " + keys);
ArrayList<String> values = new ArrayList<>(passportsAndNames.values());
System.out.println("Values: " + values);
}
}
The keys are extracted into a Set, which we haven't covered yet. It is special in that it cannot contain repeating elements. Now the main thing is to remember that the list of all keys can be retrieved from a HashMap into a separate collection.
In the example, we saved the values into an ordinary ArrayList.
Console output:
Keys: [212133, 8082771, 162348]
Values: [Bridget Logan, Donald John Trump, Ivan the Great]
The size() and clear() methods do exactly the same thing as in the previous structures we've discussed: the first returns the number of elements currently in the dictionary, the second removes all the elements.
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
System.out.println(passportsAndNames.size());
passportsAndNames.clear();
System.out.println(passportsAndNames);
}
Output:
3
{}
To check if there is at least one element in our HashMap, we can use the isEmpty() method:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
if (!passportsAndNames.isEmpty()) {
System.out.println(passportsAndNames);
}
}
Output:
{212133=Bridget Logan, 8082771=Donald John Trump, 162348=Ivan the Great}
Now we will only output to the console after a preliminary check. :)
Another interesting point is that two Maps can be combined into one. This accomplished using the putAll() method. We call it on the first HashMap, pass the second as an argument, and the elements from the second are added to the first:
public static void main(String[] args) {
HashMap<Integer, String> passportsAndNames = new HashMap<>();
HashMap<Integer, String> passportsAndNames2 = new HashMap<>();
passportsAndNames.put (212133, "Bridget Logan");
passportsAndNames.put (162348, "Ivan the Great");
passportsAndNames.put(8082771, "Donald John Trump");
passportsAndNames2.put(917352, "Clifford Patrick");
passportsAndNames2.put(925648, "Mitchell Salgado");
passportsAndNames.putAll(passportsAndNames2);
System.out.println(passportsAndNames);
}
Output:
{917352=Clifford Patrick, 212133=Bridget Logan, 8082771=Donald John Trump, 925648=Mitchell Salgado, 162348=Ivan the Great}
All the pairs in passportsAndNames2 have been copied to passportsAndNames.
Now consider a more complicated example. Specifically, iterating over a HashMap in a loop.
for (Map.Entry<Integer, String> entry: passportsAndNames.entrySet()) {
System.out.println(entry);
}
The Map.Entry class denotes the key-value pair inside the dictionary. The entrySet() method returns a list of all pairs in our HashMap. Because our map consists of these Map.Entry pairs, we're iterating over pairs, not separate keys or values.
Output:
212133=Bridget Logan
8082771=Donald John Trump
162348=Ivan the Great
Also, don't forget to study the official Oracle documentation for HashMap.
GO TO FULL VERSION