Maps

Pairing Keys with Values

What is it?

A map is a construct that allows us to pair keys and values. Maps are sometimes referred to as tables or dictionaries.

Let's say that I have the following student information:

Student ID Name
23A52 Harvey Dent
68Z29 Jessica Jones
57W85 J Jonah Jameson

Each student will have a unique ID, but multiple students could have the same name.


How Do I…?

Each student ID (key) corresponds to a student name (value). Each of these key/value pairs is known as a map entry. In Java, we can create a Map to hold these entries.

key
↓↓↓

Student ID
value
↓↓↓↓↓

Name
23A52 Harvey Dent
68Z29 Jessica Jones
57W85 J Jonah Jameson

Map is a parameterized type, so we declare it thus, specifying the types for key and value:

Map<String, String> students;

The first type parameter indicates the type of the key. Student IDs are Strings.

Map<String, String> students;

The second indicates the type of the value. Student names are also Strings.

MapString> students;

…build it?

Map is an interface, an abstract type rather than a concrete type, so in order to instantiate (create) a map, we must call the constructor for a concrete type. The most common concrete Map is HashMap:

Map<String, String> students = new HashMap<String, String>();

To add students, we use put(key, value):

students.put("23A52", "Harvey Dent");
students.put("68Z29", "Jessica Jones");
students.put("57W85", "J Jonah Jameson");

Let's look at what we've got:

System.out.println("The students are " + students);

What is it Good For?

We know that we could use an array or an ArrayList to hold collections of things, so why Map?

Imagine we wanted to look up students by name from our example student information.


First, we'd need to create a class to hold student ID and name:

public class Student {
  String id;
  String name;
}

Assuming we have populated a collection of Student objects, to find a student by ID, we'd do something like this:

for(Student current: students) {
  if("23A52".equalsIgnoreCase(current.id)) {
    System.out.println("Found the student!");
    System.out.println("The student's name name is " + current.name);
    break;
  }
}

Maps make this easier. Also, what if there were 20,000 students and the one we were looking for was 19,998th in our list? Maps also perform better for doing lookups like this.


Finding our Student

Map defines a method called get. Given a key, the get method will return its value.

Assuming we have populated a Map named students whose keys are IDs (Strings) and whose values are student names (Strings), finding a student by ID with a Map is simple:

String studentName = students.get("23A52");
System.out.println("Found the student!");
System.out.println("The student's name name is " + studentName);

A Collection Ally

Maps are part of the JCF (Java Collections Framework), but a Map is not a Collection.

Remember the two types of Collection we have talked about?

  • List: characterized by being ordered. May contain duplicate elements.
  • Set: elements are unordered. Elements must be unique.

Map keys can not be duplicated and we don't care about their order, so we use a Set to represent them.

Map values can be duplicated, but their order isn't significant. They don't fit our concepts of a List or a Set. For these, we use the parent of List and Set, Collection. A Collection promises to be no more than a collection of elements over which you can iterate.


Looking at Keys

To look at a Map's keys, we call its keySet method:

Set<String> studentIds = students.keySet();
System.out.println("The student IDs are " + studentIds);

Looking at Values

To look at a Map's values, we call its values (Surprise!) method:

Collection<String> studentNames = students.values();
System.out.println("The student names are " + studentNames);

Looking at Entries

If we want to iterate over all (or many) of the entries in a map, looking at both key and value, it is intuitive to do something like this:

for(String id: students.keySet()) {
  System.out.println("This student's name is " + students.get(id));
}

The problem with this approach is that it will result in poor performance. (This is a common interview question.) This is because looking up the value for a key requires effort, and this code would look up the value for every key.

If we want to iterate over all keys and values, we do something like this instead:

for(Entry<String, String> entry: students.entrySet()) {
  System.out.println("The student's id is " + entry.getKey());
  System.out.println("The student's name is " + entry.getValue());
}

Other Useful Map Methods

method what does it do?
remove(key) removes the entry associated with key
containsKey(key) returns true if this map contains the key
containsValue(value) returns true if this map contains the value
size() returns the number of entries in the map
isEmpty() returns true if the map does not have any entries
clear() removes all entries from the map