4. Implementation Considerations for Maps and Sets
Methods hashCode and equals
-
Method
Object.equalscompares two objects based on their addresses, not their contents. Similarly, methodObject.hashCodecalculates an object’s hash code based on its address, not its contents. -
Most predefined classes (e.g., String and Integer) override method
equalsand methodhashCode. -
If you override the equals method, Java recommends you also override the
hashCodemethod. Otherwise, your class will violate the Java contract forhashCode, which states:
java
if obj1.equals(obj2) is true, then obj1.hashCode() == obj2.hashCode().
Implementing HashSetOpen
- We can modify the hash table methods to implement a hash set. Table below compares corresponding Map and Set methods.
| Map Method | Set Method |
|---|---|
| V get(Object key) | boolean contains(Object key) |
| V put(K key, V value) | boolean add(K key) |
| V remove(Object key) | boolean remove(Object key) |
Writing HashSetOpen as an Adapter Class
- Instead of writing new methods from scratch, we can implement
HashSetOpenas an adapter class with the data field.
private final IHashMap<K, V> setMap = new HashtableOpen<>();
- We can write methods contains, add, and remove as follows. Because the map stores key–value pairs, we will have each set element reference an Entry object with the same key and value.
Implementing the Java Map and Set Interfaces
- Java API uses a hash table to implement both the
MapandSetinterfaces (classHashMapand classHashSet). - The task of implementing these interfaces is simplified by the inclusion of abstract classes
AbstractMapandAbstractSetin theCollectionsframework. These classes provide implementations of several methods for the Map and Set interfaces. So if classHashTableOpenextends classAbstractMap, we can reduce the amount of additional work we need to do.- We should also replace
IHashMapwithMap. Thus, the declaration forHashTableOpenwould be classHashTableOpen<K, V>extendsAbstractMap<K, V>implementsMap<K, V>.
- We should also replace
- The
AbstractMapprovides relatively inefficient implementations of the get and put methods. Because we overrode these methods in both our implementations (HashTableOpenandHashTableChain), we will get expected performance. There are other, less critical methods that we don’t need to provide because they are implemented inAbstractMapor its superclasses, such asclear,isEmpty,putAll,equals,hashCode, andtoString.
Interface Map.Entry and Class AbstractMap.SimpleEntry
- One requirement on the key–value pairs for a Map object is that they implement the interface
Map.Entry<K, V>, which is an inner interface of interfaceMap. - This may sound a bit confusing, but what it means is that an implementer of the Map interface must contain an inner class
Entry. TheAbstractMapincludes the inner classSimpleEntrythat implements theMap.Entryinterface. We can remove the inner classEntry<K, V>and replace newEntrywith newSimpleEntry.
Creating a Set View of a Map
- Method
entrySetcreates a set view of the entries in a Map. This means that method entrySet returns an object that implements the Set interface—that is, a set.
Classes TreeMap and TreeSet
- Besides
HashMapandHashSet, theJava Collections Frameworkprovides classesTreeMapandTreeSetthat implement theMapandSetinterfaces. These classes use aRed–Black tree, which is a balanced binary search tree. - We discussed earlier that the performances for search, retrieval, insertion, and removal operations are better for a hash table than for a binary search tree (expected O(1) versus O(log n)).
- However, the primary advantage of a binary search tree is that it can be traversed in sorted order. Hash tables, however, can’t be traversed in any meaningful way. Also, subsets based on a range of key values can be selected using a
TreeMapbut not by using aHashMap