Where lists allow you to create collections that are
accessible by a numeric index, Maps are collections that
instead allow you to to access elements (values) via a
key. The idea of key-value pairs is a popular one in
programming, as it makes it easy to associate a value
with another value. If numeric 0-based indexes are not useful,
perhaps Strings or some other object could be used as an
"index" to a specific element. This is where Maps
are a good choice for your colleciton.
As with the other Collection types, the Map classes use
generic type syntax and only store objects (for both the
key and the value). The generic types are usually denoted
as <K, V>.
Maps are similar to Lists in that they contain a list of objects,
however where a List's elements are indexed with integer indexes
starting at 0, a Map's indexes can be any unique object: it can
be an Integer object, String object, or any other object.
A Map's elements are key-value pairs, where the key is the unique
object that acts as the index, and the value is the object or value
associated with each index.
Maps also use generic syntax, so Map<K, V> is used to identify
a Map with a generic type K for the key and V for the value.
In the Java Collections framework, the
Map<K, V> interface can be implemented
on an object that needs to model a map.
A Map is actually a collection of
Map.Entry<K, V> objects.
Entry is a nested class inside the Map
class, which is why we refer to it as Map.Entry.
Map.Entry<K, V> models a single
key-value pair. This is why we call map elements "entries"
rather an "elements" or "items".
So if you had
HashMap<String, Container> containers = new HashMap<String, Container>();
Then each Map.Entry object inside the hash map's entry set
is an instance of
Map.Entry<String, Container>
The Map<K, V> interface contains
abstract methods commonly used with most Map objects
such as:
clear() - clears or empties the map of all of
its entries
containsKey(object) - returns true if there
is a map entry with a specific object as its key
containsValue(object) - returns true if there
is a map entry with a specific object as its value
entry(key, value) is a static method that returns a
Map<K, V> object for the specified key and value.
This method is used when you just want to create a map entry,
it doesn't add an entry to a map.
entrySet() returns a Set<Map.Entry<K, V>>
for each entry in the map. In other words, it returns a Set, and
that Set contains a Map<K, V> object for each of the map
entries. This can be used for iterating through the map.
get(key) returns the value of the map entry
with the specified key
isEmpty() returns true if the map has no
entries
put(key, value) creates a Map.Entry object
with the specified key and value, and adds that Map.Entry
to the map
remove(key) removes the object with the specified
key and returns it
remove(key, value) searches for a map entry with
the specified key and, if the entry was found with the
specified value, removes the entry and returns the
value true (returns false if the entry was not found or the entry
was found with the specified key but not the same value)
size() returns the number of entries in the map
values() returns a Collection<V> for each
of the values in the map (not the keys)
Map.Entry<K, V> also has some useful methods:
getKey() returns the key for the map entry, as type K
getValue() returns the value for the map entry,
as type V
setValue(value) sets the value of the map entry to the new
value
The Map<K, V> interface is the parent of the
SortedMap<K, V> interface, which can be implemented when
you want a Map that can be ordered by keys.
The abstract class AbstractMap<K, V> implements the Map<K, V>
interface, so it's used as a parent class to concrete Map classes
such as TreeMap<K, V> (which implements the SortedMap<K, V>
interface) and HashMap<K, V>.
The HashMap Class
A HashMap is a kind of Map that's very efficient when it comes
to adding, removing, and searching for map elements.
When you create a HashMap, you must specify a concrete type for
both the Key and the Value:
HashMap<Integer, Cat> cats = new HashMap<Integer, Cat>();
In this example, I'm using an Integer object (a boxed int) as the key
and a Cat object as the value.
HashMap<String, Container> containers = new HashMap<String, Container>();
Here I'm using a String object as the key and a Container object as the value.
HashMap<Container, Inventory> shipments = new HashMap<Container, Inventory>();
Here I'm using a Container object as the key and an Inventory object
as the value.
Whatever you decide to use for your key, that's the entry's
index, so that key must be unique. You cannot use the same key more than
once in a HashMap.
The default capacity of a HashMap is 16, but you can use the
single-parameter int constructor to specify a different capacity:
HashMap<Container, Inventory> shipments = new HashMap<Container, Inventory>(25);
Adding, Retrieving, and Removing Entries
To add entries to a map, we use the put() method:
HashMap<Integer, Cat> cats = new HashMap<>();
cats.put(1, new Cat());
To retrieve entries from a map, we use the get(key)
method, where key is the key of the element you
want to get:
HashMap<String, Cat> cats = new HashMap<String, Cat>();
cats.put("Arti", new Cat("Arti", "Tabby"));
Cat c = cats.get("Arti");
To remove entries from a map, you can remove by key or by key-value pair.
remove(key) removes the object with the specified
key and returns it.
HashMap<String, Cat> cats = new HashMap<String, Cat>();
cats.put("Arti", new Cat("Arti", "Tabby"));
Cat c = cats.remove("Arti");
The remove(key, value) searches for a map entry with
the specified key. If the entry is not found, it
returns false. If the entry is found, it checks to see if the
entry has the specified value: if it doesn't,
it returns false, but if it does, it removes that entry and
returns true.
HashMap<String, Cat> cats = new HashMap<String, Cat>();
cats.put("Arti", new Cat("Arti", "Tabby"));
cats.put("Sophie", new Cat("Sophie", "Calico"));
// removes the first element and returns true
boolean found1 = cats.remove("Arti", new Cat("Arti", "Tabby"));
// returns false
boolean found2 = cats.remove("Sophie", new Cat("Sophie", "Tabby"));
Note that this remove method uses equals()/hashCode() to determine
equality. If the concrete class type does not override equals() and
hashCode(), using methods that search the map may not yeild the correct
results.
Iterating Maps
Iterating maps is a bit different from iterating lists and sets.
Maps don't have iterators, but you can use a for-each loop to
iterate a map. However, you're not actually iterating the HashMap
itself: HashMap<K, V> does not implement the Iterable interface
(so technically, it's not iterable).
This is where we would use the entrySet() method
of the HashMap:
The entrySet() method actually returns
a Set of Map.Entry objects where each
Map.Entry models one key-value pair stored in the map.
Sets are iterable, as they implement the Iterable interface, so
instead of iterating the HashMap, we simply use entrySet() to
convert the HashMap into an iterable Set.
So if you had a for-each loop such as:
for (Map.Entry<String, Cat> entry : cats.entrySet()) {
...
}
This loop says "For each <String, Cat> entry in the hashmap
cats, store the entry in the parameter variable
entry for this loop iteration."
The Map.Entry<K, V> class has a
getKey() method: this method returns the key as
type <K>, in the above example,
entry.getKey() would return the String
object that's the index for each entry in the map.
Map.Entry<K, V> also has getValue()
method: this method returns the
value of the entry as type <V>, in the example above,
entry.getValue() would return the
Cat object stored in an entry of the map.
So you could write a for-each loop that iterates through a hashmap
such as:
import java.util.HashMap;
import java.util.Map;
import java.time.LocalDate;
import java.time.Month;
public class Main {
public static void main (String[] args) {
HashMap <LocalDate, Double> sales = new HashMap <LocalDate, Double>();
sales.put(LocalDate.of(2024, Month.JUNE, 24), 2589.92);
sales.put(LocalDate.of(2024, Month.JUNE, 25), 2244.80);
sales.put(LocalDate.of(2024, Month.JUNE, 26), 3103.00);
sales.put(LocalDate.of(2024, Month.JUNE, 27), 3429.42);
sales.put(LocalDate.of(2024, Month.JUNE, 29), 3476.77);
sales.put(LocalDate.of(2024, Month.JUNE, 29), 6551.23);
for (Map.Entry <LocalDate, Double> entry : sales.entrySet()) {
System.out.printf ("Date: %s Sales: $%.2f%n", entry.getKey(),
entry.getValue());
// note that %s formats date object as a string
}
}
}
Here's the same example using an Iterator:
import java.util.HashMap;
import java.util.Map;
import java.time.LocalDate;
import java.time.Month;
import java.util.Iterator;
public class Main {
public static void main (String[] args) {
HashMap <LocalDate, Double> sales = new HashMap <LocalDate, Double>();
sales.put(LocalDate.of(2024, Month.JUNE, 24), 2589.92);
sales.put(LocalDate.of(2024, Month.JUNE, 25), 2244.80);
sales.put(LocalDate.of(2024, Month.JUNE, 26), 3103.00);
sales.put(LocalDate.of(2024, Month.JUNE, 27), 3429.42);
sales.put(LocalDate.of(2024, Month.JUNE, 29), 3476.77);
sales.put(LocalDate.of(2024, Month.JUNE, 29), 6551.23);
Iterator<Map.Entry<LocalDate, Double>> i = sales.entrySet().iterator();
while (i.hasNext()) {
Map.Entry<LocalDate, Double> entry = i.next();
System.out.printf ("Date: %s Sales: $%.2f%n", entry.getKey(),
entry.getValue());
// note that %s formats date object as a string
}
}
}
Notice that you get the iterator from the map's entrySet() method,
which returns an instance of the Set class. Maps don't have iterators,
but Sets do!
Notice also that the parameterized type for the Iterator<E>
has to use the type Map.Entry<String, Cat> for the generic type E.