Assumptions / Use Case:
- Implement the
Mapinterface. This allows for passing into other methods that only accept an implementation of
Map, as well as providing a consistent and familiar API to other developers.
- Add some additional processing, e.g.:
- Disallowing certain keys or values from being inserted.
- Keeping one or more associated maps or sets for performance, e.g. a map of lists when an entry needs to be obtained by value rather than key.
HashMapinto an "
OrderedMap", where unique keys are still guaranteed, but the order of insertions is also kept.
Clearly public interfaces are designed to be implemented, and public abstract classes are available to extend. Sun's "Custom Implementations" lesson in the Collections tutorial demonstrates this. As the lesson describes, there are several abstract classes available to serve as a starting point for a custom collection implementation. However, extending these to match the full functionality and efficiency of the existing concrete implementations is not a small effort, especially for
Map implementations. The most significant features to note are the methods that return a "view" to a different part or representation of the map, e.g.
entrySet(). All are documented to return a view of the Map, such that changes in the view are reflected in the map, and vice-versa.
If that's not already a lot of extra work, consider the methods that are available on the returned view that should also remain functional, such as the
remove() method, or worse, the
List.listIterator() method. Sure, many of these are listed as "optional operations", which may simply throw an
UnsupportedOperationException for simple implementations. However, this can be frustrating to developers attempting to use these methods, and some code may depend on these methods to function.
One solution to save a lot of work is to extend an existing concrete implementation, e.g.
HashMap. However, there always seems to be some debate over extending a non-abstract class. My view is that if the class wasn't meant to be extended, it should be marked final or have less-than-public constructors. (As a compromise, the class could remain open to extension, while protecting various methods by marking them final.)
java.util.Properties is one example of a public class that extends
java.util.Hashtable, another concrete, public class.
Considerable progress seems possible on a
HashMap subclass by simply overriding the
clear() methods. However, as described above, the "view" methods provide alternate access points to modify the underlying collection data, not all of which chain down to the overridden methods. This can quickly lead to an incomplete and buggy class.
My working solution
The best approach I've found is to follow the recommendation in the tutorial, and to extend
AbstractMap. A child
HashMap can then be held as an instance variable, with most of the
AbstractMap methods delegated or proxied to it. For returning the "view" methods, they can be made "unmodifiable" by wrapping them in calls to the
Collections.unmodifiable*(…) wrapper methods. While this may lead to some frustrations defined above, it provides a solid class that still properly implements the core collection methods.
I did find one Sun bug report that is a request for enhancement regarding this issue: 5078552 - "(coll) ChangeListener, VetoableChangeListener for Collections, Lists and others". It was submitted in July 2004, and hasn't yet received any attention since.