Tải bản đầy đủ (.pdf) (20 trang)

Maps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (52.11 KB, 20 trang )

Chapter 10: Maps
Overview
The Map interface of the Collections Framework replaces the historical Dictionary class. While Dictionary
was a class, and an abstract one at that, it was entirely full of abstract methods and should have been an
interface. Its replacement truly is just an interface. The interface defines the basic support for storing
key−value pairs in collections where each key can map to, at most, one value.
While Map is part of the framework, it does not extend from the Collection interface. Instead, it is the root of
its own hierarchy. There are four concrete implementations of the Map interface: HashMap, WeakHashMap,
TreeMap, and the historical Hashtable. In addition, the elements of the map are defined to be of type
Map.Entry. To help you see how the Map interface interconnects with the other aspects of the Collections
Framework, Figure 10−1 shows its abstract and concrete implementations as well as its related interfaces.
Figure 10−1: The Map class hierarchy.
Note The WeakHashMap relies on weakly reachable keys through the WeakReference class. We'll discuss this
in more depth in the "WeakHashMap" section later in this chapter.
Map Basics
Ever need to look up a name in a phone book or a definition in a dictionary? This is the basic operation of a
map: given some key, a name or word, the map finds out what value is associated with that key. While
nowadays people have many phone numbers (with cell phones and whatnot), and words can have multiple
definitions, the concept is what is important—given some key, the map finds its value. If it isn't in the phone
book or dictionary, it has no value.
You'll find the Map interface methods listed in Table 10−1. Remember that Map doesn't extend from
Collection, so this is everything...there are no additional inherited methods.
Table 10−1: Summary of the Map Interface
VARIABLE/METHOD NAME VERSION DESCRIPTION
clear() 1.2 Removes all the elements from the map.
containsKey() 1.2 Checks to see if an object is a key for the map.
containsValue() 1.2 Checks to see if an object is a value within the map.
entrySet() 1.2 Returns the set of key−value pairs in the map.
equals() 1.2 Checks for equality with another object.
127
get() 1.2 Retrieves a value for a key in the map.


hashCode() 1.2 Computes a hash code for the map.
isEmpty() 1.2 Checks if hash map has any elements.
keySet() 1.2 Retrieves a collection of the keys of the map.
put() 1.2 Places a key−value pair into the map.
putAll() 1.2 Places a collection of key−value pairs into the map.
remove() 1.2 Removes an element from the map.
size() 1.2 Returns the number of elements in the map.
values() 1.2 Retrieves a collection of the values of the map.
The four concrete map implementations found in the framework are Hashtable, HashMap, WeakHashMap,
and TreeMap. The historical Hashtable class has been reworked into the framework and was discussed in
Chapter 5. HashMap, which represents an unsynchronized hash table, replaces Hashtable in the new
framework. The second new map is a WeakHashMap. It is identical to a HashMap but relies on weak
references for its keys. The third and final new map is the TreeMap, which maintains the map entries sorted
by key in a balanced, red−black tree.
Warning If you use a Hashtable as a map, remember to not call the legacy methods.
Map.Entry Interface
Elements of a map are of type Map.Entry, an inner interface of the Map interface. Each key−value pair is an
instance of this interface. As a user of a map, you never create an instance; instead, the concrete maps have
package−private classes that implement the interface. When you get the entrySet() for a map, the method
returns all the key−value pairs for the entries in the map as a set of Map.Entry elements.
Table 10−2 reflects the methods of the Map.Entry interface that you can use to work with each entry.
Table 10−2: Summary of the Map.Entry Interface
VARIABLE/METHOD NAME VERSION DESCRIPTION
equals() 1.2 Checks for equality with
another object.
getKey() 1.2 Retrieves the key for a map
entry.
getValue() 1.2 Retrieves the value for a map
entry.
hashCode() 1.2 Computes the hash code for a

map entry.
setValue() 1.2 Changes the value for a map
entry.
The equals() and hashCode() methods override the Object behavior to ensure that two equal entries, possibly
from different maps, have the same hash code:
Map.Entry Interface
128
public boolean equals(Object element)
public int hashCode()
They take special care to deal with the possibility of having null keys and values.
You can access the key or value of the entry with the getKey() and getValue() methods, respectively:
public Object getKey()
public Object getValue()
Note Since maps support null keys and values, either getKey() or getValue() could return null.
The final method of the interface is the setValue() method, which allows you to replace the value for the key
associated with the entry:
public Object setValue(Object newValue)
The value originally associated with the key is returned from the setValue() method. If the map you are
working with is read−only, you'll get an UnsupportedOperationException thrown. There is no setKey()
method because changing the key would require the removal of the entry and addition of a new entry—a set
of operations you can't do from the interface.
The nice thing about getting the set of map entries is that you don't have to do a lookup for each key if you
need to get each value, too. For instance, the following allows you to print the value of each system property
using map entries:
Properties props = System.getProperties();
Iterator iter = props.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
System.out.println(entry.getKey() + " −− " + entry.getValue());
}

Now, compare that to the code necessary to do the same thing with the older Enumeration and Properties
methods:
Properties props = System.getProperties();
Enumeration enum = props.propertyNames();
while (enum.hasMoreElements()) {
String key = (String)enum.nextElement();
System.out.println(key + " −− " + props.getProperty(key));
}
While this code may look simpler, the call to getProperty() for each key adds up to make the operation more
costly. For the Iterator version, the Map.Entry already has the property value and doesn't require an extra
lookup.
Note Remember that the historical Properties class extends from Hashtable and Hashtable was reworked to
support the Map interface.
Map.Entry Interface
129
HashMap Class
The HashMap is the most commonly used implementation of the Map interface. It provides a basic key−value
map where the elements are unordered. If you need to maintain map keys in an ordered fashion, that's where
the TreeMap comes in handy, which will be explored later in this chapter.
The basics of the HashMap come from the AbstractMap. Table 10−3 lists its constructors and the methods it
overrides.
Table 10−3: Summary of the HashMap Class
VARIABLE/METHOD NAME VERSION DESCRIPTION
HashMap() 1.2 Constructs an empty hash map.
clear() 1.2 Removes all the elements from the hash map.
clone() 1.2 Creates a clone of the hash map.
containsKey() 1.2 Checks to see if an object is a key for the hash
map.
containsValue() 1.2 Checks to see if an object is a value within the
hash map.

entrySet() 1.2 Returns a set of key−value pairs in the hash
map.
get() 1.2 Retrieves the value for a key in the hash map.
isEmpty() 1.2 Checks if hash map has any elements.
keySet() 1.2 Retrieves a collection of the keys of the hash
map.
put() 1.2 Places a key−value pair into the hash map.
putAll() 1.2 Places a collection of key−value pairs into the
hash map.
remove() 1.2 Removes an element from the hash map.
size() 1.2 Returns the number of elements in the hash
map.
values() 1.2 Retrieves a collection of the values of the hash
map.
Basically, all of the methods of the Map interface defined in the AbstractMap class are overridden, adding in a
method from Object, too. Unlike some concrete collections, the HashMap class adds no new methods beyond
the Map interface.
Creating a HashMap
There are four constructors for creating a HashMap. The first three constructors permit creation of an empty
HashMap:
public HashMap()
public HashMap(int initialCapacity)
public HashMap(int initialCapacity, float loadFactor)
HashMap Class
130
Unless specified otherwise, the initial capacity of the internal data structure is 101 or 11, depending upon
which version of Java you are using. For the 1.2 release, it is 101, and for 1.3, it is 11. Unless specified
otherwise, the default load factor is 75%. When the number of elements in the map exceeds the (load factor *
the capacity), the map grows by a factor of 2 × capacity + 1.
Tip As explained in the "Creating Hash Tables" section of Chapter 5, a better initial capacity is 89.

This will keep the table growing with roughly prime sizes to around 3000 elements. Also, the
default initial size is not part of the class definition so it is possible that a third−party
implementation doesn't use 11 or 101 as the default initial size.
The fourth and final constructor is the standard copy constructor, which creates a new HashMap from another
map:
public HashMap(Map map)
This final constructor doesn't permit a custom capacity or load factor. Instead, the internal structure will be
sized at twice the map size, or 11, whichever is greater, with a default load factor of 75%. The default size of
11 is part of the class definition here.
Adding Key−Value Pairs
Now that you've created the HashMap, you can add key−value pairs to it. The mechanism to add a pair is the
put() method:
public Object put(Object key, Object value)
Unlike a Hashtable, both the key and the value for a HashMap can be null. If the key happens to already be in
the map, the old value is replaced and returned. Otherwise, null is returned. The map uses the key's hash code
to determine where to store the key−value pair internally. For a full explanation of the use of hash codes, see
the "Understanding Hash Tables" section in Chapter 5.
To copy all the key−value pairs from one Map into another, use the putAll() method:
public void putAll(Map map)
The value is replaced for every key in the passed−in map that already exists in the HashMap.
Displaying Contents
You'll find the toString() method of Object overridden in HashMap:
public String toString()
Similar to the other collection classes, the returned string will be a comma−delimited list of the collection
elements within braces ({}). For the hash map, each key−value element is displayed separated by an equal
sign. For instance, if the map contained the original thirteen colonies of the United States and their current
capital, the returned string might look something like the following:
{South Carolina=Columbia, North Carolina=Raleigh, Georgia=Atlanta,
Massachusetts=Boston, Delaware=Dover, New York=Albany,
Pennsylvania=Harrisburg, New Jersey=Trenton, Rhode Island=Providence,

Adding Key−Value Pairs
131
Maryland=Annapolis, Virginia=Richmond, Connecticut=Hartford,
New Hampshire=Concord}
The listed order does not reflect the order in which the elements are added to the hash map. Instead, the order
reflects the range conversion of the hash codes generated from the keys.
Note Keep in mind that the order of the elements within a HashMap could be different. Had the map capacity
been different, the actual key−value pair order would have been different, too.
Removing Key−Value Pairs
When you need to remove an element from a hash map, call the remove() method with the key as its
argument:
public Object remove(Object key)
If the key is present as a key within the hash table, the key−value pair will be removed and the value object
will be returned. If the object is not present in the map, null will be returned.
Warning Since null is valid for the value associated with a key, you cannot rely on the return value to
determine if the key was originally present.
Removal of all elements from a map is done through the clear() method:
public void clear()
Warning Removing all elements from a map does not return the space used by the internal data structure. The
capacity of the structure remains the same. Only the entries of the structure are nulled out.
Sizing Hash Maps
When you create a hash map, you can size the internal data structure it uses. Once it is created, you no longer
have direct control. Only when the number of elements within the map exceeds the capacity × the load factor
will the capacity increase at 2n + 1. In fact, you cannot find out its current capacity—you can only find out the
number of key−value pairs within the HashMap with the help of the size() method, and if it is empty, with
isEmpty():
public int size()
public boolean isEmpty()
When the size is zero, isEmpty() returns true. Otherwise, it returns false. Using isEmpty() to check for
emptiness is more efficient than manually comparing the size() to zero yourself.

When the hash map determines that it needs to increase its capacity, the private rehash() method is called.
This causes a new internal data structure to be created, inserting into it all of the values based upon the range
conversion of hash codes for the keys based on the new capacity.
Removing Key−Value Pairs
132
Map Operations
Maps provide several operations for working with the elements of the collection. The hash map supports
fetching one of the following: a single key, all keys, all values, or all key−value pairs. You can also search for
a specific key or value within the hash table, among other tasks that are specializations of Object methods.
Fetching Keys and Values
The HashMap supports several mechanisms to retrieve keys and values out of the map—thankfully, not as
many as Hashtable. The simplest is to find a value based upon a specific key with the get() method:
public Object get(Object key)
If the key is not found within the map, null is returned. Otherwise, the current value for the key within the
map is returned.
Note Remember that keys can have null values in a HashMap so get() can return null if the key was present
but has a null value.
Since objects are stored (and searched for) by the hash code of the key, you shouldn't modify the attributes of
a key object after it has been placed in a HashMap (or any map). If the hash code for an element changes after
it is in a map, the only way you'll be able to find the object is to iterate through all the elements. The get()
method will no longer function properly.
Instead of looking up the value for individual keys, you can get the set of all keys with the keySet() method:
public Set keySet()
The keySet() method returns the set of keys as a Set object.
To get the set of all the values in the hash map, call the values() method:
public Collection values()
The values() method returns a set of all values in the map as a Collection. Because values can have duplicates,
you get a Collection back instead of a Set. This means that if the same value is in the map multiple times, it
will be in the collection of values multiple times as well.
To get a set of Map.Entry elements back from a HashMap, use the entrySet() method:

public Set entrySet()
Each element of the Set returned is of type Map.Entry. When you need to work with the key−value pairs
together, this is the best of all of the mechanisms.
Finding Elements
There are two ways to check if something is in a hash map. The containsKey() method allows you to check
for the existence of a key within the map, and the containsValue() method allows you to check for a value
within the map:
Map Operations
133
public boolean containsKey(Object key)
public boolean containsValue(Object value)
The containsKey() method is like the get() method but instead of returning to you the value at the key, you get
back a boolean value stating whether or not the key is present. If found, you get true; false, otherwise. If your
map supports null values associated with keys, you would need to use containsKey() to find out if a key is in a
map, and not just get() to get the value as get() could return null if the key was found or not.
The containsValue() method checks for the existence of a specific value within the HashMap. The
containsValue() method is very inefficient as it must basically do a table scan to check for existence since
values are stored by key. If you frequently find yourself checking for values within a map, you're probably
better off maintaining two maps, one for each direction. The containsValue() method is especially inefficient
if you are frequently searching for values not in the map. To find nothing in the hash map requires checking
every element of the map.
Cloning Hash Map
The HashMap class provides its own implementation of the clone() method:
public Object clone()
This functions similarly to passing the hash map to the copy constructor of a HashMap or to creating an empty
HashMap and calling putAll().
Checking Hash Maps for Equality
You'll find the equals() method to define equality from Object overridden in the AbstractMap class:
public boolean equals(Object o)
Two maps are defined as equal if both of their entry sets are equal

(thisHash.entrySet().equals(map.entrySet()). Two entry sets are equal if their sizes are equal and each set
contains every member of the other set. Order is unimportant. Two entries are compared for equality by using
their equals() method. This means that the maps don't have to contain the same instances, only equivalent
instances.
Hashing Hash Maps
The AbstractMap class provides the hashCode() implementation for HashMap. The hash code for a HashMap
is the sum of all of its elements' hash codes:
public int hashCode()
Serializing Hash Maps
HashMap implements the Serializable interface. However, in order for a HashMap to be serializable, all the
keys and values of the map must also be serializable. If they are not and you try to serialize the map, you'll get
a NotSerializableException thrown.
Cloning Hash Map
134

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×