A Map is a Set where each element has an object riding shotgun.

In other words, a Map is a way of looking up a value using another value as a key. It’s very similar to looking up a definition associated with a word in a dictionary–so much like it, in fact, that in the Python programming language, the term for the same concept is “dictionary.”

And the use of “key” and “value” in the previous paragraph is no accident. These are the terms we use to describe these entities.

Just as Set is an interface, so is Map. And just as there are a HashSet and TreeSet implementation (among others), there are HashMap and TreeMap implementations (among others) of Map. Not surprisingly, the keys of a HashMap are a HashSet, and the keys of a TreeMap are a sorted TreeSet, with all the properties and responsibilities those implementations have (see the previous page for details), including uniqueness.

Just as a HashSet can contain a null, so can the keys of a HashMap. And just as a TreeSet cannot contain a null, neither can the keys of a TreeMap.

Map.Entry

Map has a subinterface: Entry. The entry Set of a Map is the key/value pairs contained in the Map, and is iterable (i.e., you can use it with for/each and it has an Iterator); in fact, iterating through the entries is the only way to retrieve one. You’d be amazed how handy this is.

Entry has three methods of interest:

MethodFunction
K getKey()Returns the Entry’s key.
V getValue()Returns the Entry’s value.
V setValue()Changes the value of the Entry, and returns its previous value. If the Entry has been removed, the result is undefined. Remember: changing the value in the Entry changes it in the Map.

HashMap and TreeMap Constructors

HashMap has four constructors. Here they are with examples.

HashMap<Integer, String> integerMap 
      = new HashMap<Integer, String>();

// ... or, at a higher level of abstraction...
Map<Integer, String> integerMap 
      = new HashMap<Integer, String>();

The simplest: This creates a new HashMap with a capacity of 16 elements and a load factor of 0.75. The generic indicates that the keys are Integers and the values are Strings (The JVM increases the capacity as needed when you add more elements. See the documentation for details on the management of capacity.)

Map<Integer, List<String>> integerMap = new HashMap<Integer, List<String>>(mySourceMap);

This creates a HashMap initialized with the contents of the Map provided, which is presumably also a Map whose keys are Integers and whose values are Lists of String.

int capacity = 25;
Map<Integer> integerMap = new HashMap<Integer>(capacity);

This creates a HashMap with an initial capacity equal to the value specified. Very efficient if you know how many elements you’re likely to need.

int capacity = 25;
float loadFactor = .60;
Map<Integer> integerMap 
      = new HashMap<Integer>(capacity, loadFactor);

This creates a HashMap with an initial capacity and load factor equal to the values specified.

TreeMap also has the default constructor and constructor that replicates an existing Map that HashMap does. But like TreeSet, it has no concept of capacity or load factor. And like TreeSet, there’s a constructor that accepts a Comparator to establish a sort order for the keys.

Map Common Methods

Map is a generic interface whose definition is always expressed as

Map<K, V>

Where K is the key type and V is the value type. That being said, here are some of the more common methods of Map, inherited by both HashMap and TreeMap.

void clear()

Empties the Map.

boolean containsKey(Object key)

Returns true if the keyset contains the value of key. As always, the outcome depends on the values of each key’s equals() method and, for HashSet, hashCode().

Set<Map.Entry<K, V>> entrySet()

Returns a Set of each key/value pair in the Map. Just the thing for when you want to have both the key and the value of each entry in the Map. Here’s an example:

    for (Entry<String, Chicken> entry : chickenMap.entrySet()) {
        String name = entry.getKey();
        Chicken chick = entry.getValue();
        ...
    }
V get(Object key)

Returns the value associated with the key. If key doesn’t appear in the keyset, this returns a null. Note that there’s no way using get() alone to distinguish between a key that doesn’t appear in the Map and a key associated with a value of null.

getOrDefault(Object key, V defaultValue)

Like get(Object key), but if the key isn’t found in the Map, defaultValue is returned.

boolean isEmpty()

Returns true if the Map has no elements and false otherwise. Equivalent to map.size() == 0.

Set<K> keySet()

Returns the Set of keys.

V put(K key, V value)

Adds the key to the Map (if there’s not already an equal key there) and associates it with value. The value returned is the value previously associated with key, if any–otherwise null. Note that the original value, if any, is replaced in the Map; but as is the rule for Set, the key is not.

V putIfAbsent(K key, V value)

Same as put(key, value) but the original value is replaced only if the key doesn’t already appear in the Map or if the associated value is null.

V replace(K key, V value)

Nearly the opposite of putIfAbsent–the value is replaced only if the key appears in the Map. Returns the original value if so, and null if not.

int size()

Returns the number of Entries in the Map.

Collection<V> values()

Returns a Collection of the values in the Map.

TreeMap Methods

TreeMap extends NavigableMap, which extends SortedMap, which extends Map. As such, a TreeMap is arranged by sorted key values and has a host of methods inherited from SortedMap and NavigableMap that HashMap doesn’t have.

From SortedMap

SortedMap<K, V> headMap(K toKey)
SortedMap<K, V> tailMap(K fromKey)

Returns a view of the portion of this map whose keys are strictly less than toKey (headMap()) or greater than or equal to fromKey (tailMap()).

SortedMap<K, V> subMap(K fromKey, K toKey)

Returns a view of the portion of this map whose keys range from fromKey, inclusive, to toKey, exclusive.

K firstKey()
K lastKey()

Returns the lowest (firstKey()) and highest (lastKey()) key values in the Map.

From NavigableMap

Map.Entry<K, V> ceilingEntry(K key)
Map.Entry<K, V> floorEntry(K key)
Map.Entry<K, V> higherEntry(K key)
Map.Entry<K, V> lowerEntry(K key)

Returns the Entry associated with:

  • ceilingEntry: the least key greater than or equal to key
  • floorEntry: the greatest key less than or equal to key
  • higherEntry: the least key greater than key
  • lowerEntry: the greatest key less than key

In all these cases, null is returned if there is no key that satisfies the condition.

Map.Entry<K, V> firstEntry()
Map.Entry<K, V> lastEntry()
Map.Entry<K, V> pollFirstEntry()
Map.Entry<K, V> pollLastEntry()

Returns the entry associated with:

  • firstEntry: the lowest key in the Map in collating sequence
  • lastEntry: the highest key in the Map in collating sequence
  • pollFirst|Entry, pollLastEntry: same as firstEntry and lastEntry, only the Entry is removed from the Map as well as being returned.

In all cases, if the Map is empty, null is returned.

Navigablemap<K, V> headMap(K toKey, boolean inclusive)
Navigablemap<K, V> tailMap(K fromKey, boolean inclusive)

headMap(), returns a NavigableMap view of the portion of the Map whose keys are less than, or less than or equal to (if inclusive is true) the value of toKey.

tailMap(), returns a NavigableMap view of the portion of the Map whose keys are greater than, or greater than or equal to (if inclusive is true) the value of fromKey.

NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive,
         K toKey, boolean toInclusive)

Returns a view of the portion of this map whose keys are greater than fromKey (or greater than or equal to, if fromInclusive is true) and less than (or less than or equal to, if toInclusive is true) toKey.

Exercise

In this exercise, we’ll use two Maps.

  • Create a project to map the letters in a familiar quote of your choice to two maps.
  • The first map will contain the number of occurrences of each letter (not including blanks).
  • The second will contain a set of all the distinct words in the quote that contain each letter.
  • Display the results on the console as letter followed by count, or letter followed by list of containing words.

You can download a sample solution that uses the first sentence of Lincoln’s Gettysburg Address here. As always, help with importing a project is here.

What You Need to Know

  • A Map is like a dictionary in that given one object, you can find another object that corresponds to it.
  • A Map consists of Entries (class Entry), each consisting of a key and a value.
  • A Map’s keyset (the Set of all the Map’s keys), values (the Collection of all the values), and entryset (the Set of all the Map’s entries) can be processed separately and are all iterable.

Next: Date and Time