20.1. Maps¶
20.1.1. The Map Interface¶
The Map<K, V>
interface is modeled after looking up definitions for words
in a dictionary. In computer science, you will hear people refer to these
kinds of “look up” structures using names like “map”, “dictionary”,
“hash”, or even “associative array”. You can think of a map as a collection
of pairs of elements that are associated with each other. A pair consists
of a key that corresponds to a value you can look up, and
a value corresponding to the result you will find when you look up its
key. If you think of a dictionary of words, for example, each entry in the
dictionary consists of a “word” and its “definition”. We would call the “word”
a “key”, and its definition would be a “value”, and the dictionary itself is
a collection of pairs of keys and values (words and definitions). You will
sometimes hear the elements in a map referred to as a key-value pair
because it contains pairs of connected values.
Pairs can be added to maps and can be removed from maps. Maps cannot have distinct pairs with the same keys; if you attempt to add a pair to a map that already contains a pair with the same key, the second pair will replace the first.
The Map<K, V>
interface defines the map operations. It takes two separate
generic type parameters: K
is the
type parameter specifying the key type, and V
is the type parameter
specifying the value type. For example K
could be Integer
and a
V
could be String
. Or K
and V
could both be Boolean
.
Or K
could be String
and V
could be List<Book>
. There are no
limits on possible combinations, as long as they are object types and not primitive types.
The most important Map
operations are:
Note
Like we have done with previous interfaces, we only show a subset of the java.util.Map
interface below, which we call MHCMap
to differentiate it from the official Map
interface in the java.util
package.
public interface MHCMap<K, V> {
/**
* Get the value associated with a key
* @param key the key to look up
* @return the value associated with the key or null if the key is not
* in the map.
*/
public V get (K key);
/**
* Associates a value with a key.
* @param key the key whose value is set
* @param value the value to be associated with the key
* @return if the key previously had a value, return the previous value. If the key
* was not in the table, return null.
*/
public V put (K key, V value);
/**
* Returns true if the map has an entry for the key
* @param key the key to look up
* @return true if the key is in the table. Otherwise return false.
*/
public boolean containsKey (K key);
/**
* Removes the entry for the key.
* @param key the key for the entry to remove
* @return If the key was in the table, the value previously associated with
* the key is returned. If the key was not in the table, null is returned.
*/
public V remove (K key);
}
20.1.2. Classes that Implement Map¶
HashMap
and TreeMap
are two classes in the java.util
collections
framework that implement the Map
interface. They both provide
fast operations to look up a key in a map. They also
provide quick insertion of a pair into the map or removal of a
pair from a map. For large volumes of data, both are much, much faster
at lookup tasks than storing items in a List
or an array.
We usually use HashMap
as the default choice when we create new maps, similar to choosing ArrayList
as the default choice for new List
objects. The HashMap
class
works well in most cases.
One situation when you would prefer to use TreeMap
is when you would
like to iterate over all the keys in the map in sorted order. For example,
in a dictionary, you might expect words to be stored in alphabetical order,
and in a phone book, you might expect names to be stored in alphabetical order.
If keys have a natural ordering, the TreeMap
class will use this order
when iterating over keys, although his can slightly impact the overall
performance of the map by a small amount. The HashMap
does not keep keys
in any predictable order.
Note
We’ll focus on the HashMap
implementation in the upcoming classes.
20.1.3. Using a Map¶
Let’s think about a simple example for using a map data structure.
Suppose that a programmer is developing an application for a large
company for maintaining a no-call list. The programmer wishes to
store pairs of names and phone numbers. We could represent both using
strings, so we could use a Map<String, String>
to store these
pairs. The resulting map will act sort of like a phone book, associating
names (keys) with phone numbers (values) in pairs.
public static void testMap()
{
Map<String, String> noCallMap = new HashMap<String, String>();
}
20.1.3.1. Adding and Accessing Pairs in a Map¶
Now, let’s add some values to our noCallMap
. To add something to
a Map, we’ll call the put()
method:
public static void testMap()
{
Map<String, String> noCallMap = new HashMap<String, String>();
noCallMap.put("Roger M", "090−997−2918");
noCallMap.put("Jane Q", "999-777-1234");
}
put()
takes in two parameters: first a key, and then an associated value.
The two calls to put()
above create two key-value pairs, each with a name
and a phone number.
To access those pairs, we use the get()
method:
public static void testMap()
{
Map<String, String> noCallMap = new HashMap<String, String>();
noCallMap.put("Roger M", "090−997−2918");
noCallMap.put("Jane Q", "999-777-1234");
System.out.print("Jane Q's number is: " + noCallMap.get("Jane Q"));
}
When we run the code above, the following message would be printed out:
"Jane Q's number is: 999-777-1234"
20.1.3.2. Checking for and Removing Pairs in a Map¶
As you saw with get()
, when accessing values in a map, you usually use
the key to specify which pair you wish to work on. In fact, sometimes one
might say “index into a map” using a key. The alternate name of “associative
array” comes from the fact that a map uses keys as unique identifiers for the
pairs it contains, and you can think of the key as being similar to the
“position” of a pair in a map, just like numeric positions are used to
refer to positions in a List
.
So when checking to see if a pair is stored in a map, or to remove the pair
from the map, it is natural to use the key as the identifier. Maps provide
a remove()
method where you specify a key, and the pair with that key
will be removed from the map. Maps also provide a containsKey()
method that
takes a key value and returns a boolean result indicating whether a pair
with the corresponding key is present in the map. For both of these operations,
since keys must be unique in a map, we really only need a key.
public static void testMap()
{
Map<String, String> noCallMap = new HashMap<String, String>();
noCallMap.put("Roger M", "090−997−2918");
noCallMap.put("Jane Q", "999-777-1234");
noCallMap.remove("Jane Q");
System.out.print(noCallMap.containsKey("Jane Q"));
}
Here, we add “Jane Q” and her phone number to the Map, remove it, then the value false
would be printed out as there is no longer a key called “Jane Q” in our Map.
In the next section, we’ll look at how Hash Maps (also known as Hash Tables) implement the Map
interface.