Maps
The Map interface is part of the Collections API, but unlike List and Set it does not extend the Collection interface. The primary reason for this is that each entry in a Map consists of two parts: a key and a value. This means that adding an element to a Map requires two values to be provided rather than one.
In some ways Maps are like HashSets: both data structures use indexed keys to facilitate fast lookup. The main difference is that with HashSets the key is implicitly generated from the hashKey method, whereas with Maps the key is explicitly provided, and therefore allows two otherwise unrelated objects to be associated with one another.
The two most common implementations of Map are HashMap and TreeMap. The main difference between these implementations is that TreeMaps sort elements based on their key, while HashMaps do not. HashMap should be your preferred option unless you have a specific reason for needing sorting, because it generally provides superior performance.
Consider a case where you want to quickly retrieve the Address of a particular person. You will start by defining a very simple Person class and adding hashCode and equals methods:
package addresses;
public class Person {
String firstName;
String lastName;
public Person(String firstName, String lastName ) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return "Person [firstName=" + firstName +
", lastName=" + lastName + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +
((firstName == null) ? 0 : firstName.hashCode());
result = prime * result +
((lastName == null) ? 0 : lastName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
}
Getters and setters have been omitted for brevity, and the fields have instead been defined using the default access modifier. You can choose to use private fields and getters and setters in your version.
Next, you will write a short program that associates Person and Address instances:
package addresses;
import java.util.HashMap;
import java.util.Map;
public class MapMain {
public static void main(String[] args) {
Address a1 = new Address(799, "E Dragham",
"Tucson", 85705, "USA");
Address a2 = new Address(200, "Main Street",
"Phoenix", 85123, "USA");
Address a3 = new Address(100, "Main Street",
"Seattle", 98104, "USA");
Person p1 = new Person("Dane", "Cameron");
Person p2 = new Person("James", "Smith");
Person p3 = new Person("Keith", "Rogers");
Person p4 = new Person("Owen", "Heart");
Map people = new HashMap();
people.put(p1, a3);
people.put(p2, a1);
people.put(p3, a2);
people.put(p4, a1);
System.out.println("Dane lives at " + people.get(p1));
people.put(p1, a2);
System.out.println("Now Dane lives at " + people.get(p1));
}
}
This produces the following output:
Dane lives at 100 Main Street, Seattle, USA
Now Dane lives at 200 Main Street, Phoenix, USA
Maps enforce a number of rules:
• >Each key can only occur once in the Map, if you put a key that already exists, the value associated with this will be updated. This is why the Address for Dane changed in the example program.
• null values can be used for both the key and the value, but there can only be a single entry with a null key.
Maps generally provide extremely good performance when accessing values based on their key. This is, however, dependent on the object being used as the key providing a hashCode implementation that produced well-distributed values, because, as with Sets, lookups are optimized through the use of the hashCode.