JDK-4075132 : Hashtable implementation makes extending hashtable dangerous
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 1.1.2
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_nt
  • CPU: x86
  • Submitted: 1997-08-28
  • Updated: 2021-03-03
  • Resolved: 1997-11-14
Related Reports
Duplicate :  
Description

Name: joT67522			Date: 08/28/97


The implementation of Hashtable.put() makes creating classes that
extend Hashtable and reimplement put and get very dangerous, in a
subtle and undocumented way.

Consider a class, say OrderedHashtable, that extends Hashtable 
and implements its own get and put methods.  OrderedHashtable 
wants to maintain the insertion order of entries in the table by
keeping a separate linked list of OrderedHashtableEntry objects,
each of which wraps an Object that was passed into its put() 
method.  The Objects stored in the underlying Hashtable are 
instances of OrderedHashtableEntry, not the origial objects that
were passed to OrderedHashtable.put

(One can argue about the merits of this implementation and 
suggest that the right relationship is OrderedHashtable HAS-A 
Hashtable, rather than IS-A Hashtable, but that's beside the 
point for now.)

Given the class described above, you get the following scenario
when the base Hashtable runs out of space:

(1) Client calls OrderedHashtable.put on some object (O) of 
    type T, with some key (K) of type String.

(2) OrderedHashtable constructs a wrapper (W) of type 
    OrderedHashtableEntry around O and calls super.put(K, W) 
    to put it into the base hashtable.  This is usually okay 
    because OrderedHashtable.get will unwrap the value 
    for you and return O.

(3) If the hashtable is nearly full, Hashtable.put extends 
    the underlying data structure AND THEN RECURSIVELY CALLS 
    put() TO PUT THE OBJECT INTO THE NEW TABLE.

(4) The call to put resolves to OrderedHashtable.put instead 
    of to the normal Hashtable.put (because all non-final 
    methods in Java are virtual), which means 
    OrderedHashtable.put is now asked to use key K to store 
    object W instead of the original object O.

(5) OrderedHashtable dutifully complies with this request, 
    wrapping the value in yet another instance of 
    OrderedHashtableEntry (W2) and storing that in
    the table under key K.

When you go to retrieve the value for K, OrderedHashtable 
retrieves W2 and then unwraps it, handing you back W, which 
is what it got from the recursive call to put that was made 
by the Hashtable internals in step (3), but which is NOT the 
object you originally asked it to store.

Hashtable should not be calling put() recurisively after 
extending the table -- it should use a private method to actually
store data in the table so that subclasses can't get into this
broken state, or else it should extend the table and then store
the value inline in Hashtable.put

company - Art Technology Group , email - ###@###.###
======================================================================

Comments
WORK AROUND Name: joT67522 Date: 08/28/97 A hastable subclass like OrderedHashtable can check in its own put() method to see whether the value being stored is of type OrderedHashtableEntry, and if so assume a recursive call from the superclass put method, but this is a truly ugly hack. ======================================================================
11-06-2004