diff --git a/src/share/classes/java/util/HashMap.java b/src/share/classes/java/util/HashMap.java --- a/src/share/classes/java/util/HashMap.java +++ b/src/share/classes/java/util/HashMap.java @@ -222,16 +222,23 @@ if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); + // Remember the requested capacity, but do not allocate a table yet: + threshold = initialCapacity; + this.loadFactor = loadFactor; + table = EMPTY_TABLE; + init(); + } + private static final Entry[] EMPTY_TABLE = new Entry[0]; + + private void expandEmptyTable(int initialCapacity) { // Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; - this.loadFactor = loadFactor; threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); table = new Entry[capacity]; - init(); } /** @@ -370,6 +377,7 @@ @SuppressWarnings("unchecked") final Entry getEntry(Object key) { int hash = (key == null) ? 0 : hash(key); + if (table.length == 0) return null; for (Entry e = table[indexFor(hash, table.length)]; e != null; e = e.next) { @@ -395,6 +403,9 @@ * previously associated null with key.) */ public V put(K key, V value) { + if (table.length == 0) + // do not fall through; want a dominating zero check + return putForEmptyTable(key, value); if (key == null) return putForNullKey(value); int hash = hash(key); @@ -417,6 +428,14 @@ } /** + * Offloaded version of put for initially empty table + */ + private V putForEmptyTable(K key, V value) { + expandEmptyTable(threshold); + return put(key, value); + } + + /** * Offloaded version of put for null keys */ private V putForNullKey(V value) { @@ -442,6 +461,8 @@ * addEntry. */ private void putForCreate(K key, V value) { + if (table.length == 0) + expandEmptyTable(threshold); int hash = null == key ? 0 : hash(key); int i = indexFor(hash, table.length); @@ -574,6 +595,7 @@ */ final Entry removeEntryForKey(Object key) { int hash = (key == null) ? 0 : hash(key); + if (table.length == 0) return null; int i = indexFor(hash, table.length); @SuppressWarnings("unchecked") Entry prev = (Entry)table[i]; @@ -611,6 +633,7 @@ Map.Entry entry = (Map.Entry) o; Object key = entry.getKey(); int hash = (key == null) ? 0 : hash(key); + if (table.length == 0) return null; int i = indexFor(hash, table.length); @SuppressWarnings("unchecked") Entry prev = (Entry)table[i]; @@ -693,7 +716,8 @@ } catch (CloneNotSupportedException e) { // assert false; } - result.table = new Entry[table.length]; + if (table.length != 0) + result.table = new Entry[table.length]; result.entrySet = null; result.modCount = 0; result.size = 0; @@ -781,6 +805,7 @@ * Subclass overrides this to alter the behavior of put method. */ void addEntry(int hash, K key, V value, int bucketIndex) { + assert(table.length != 0); // FIXME if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; @@ -1058,7 +1083,7 @@ sun.misc.Hashing.randomHashSeed(this)); // Read in number of buckets and allocate the bucket array; - s.readInt(); // ignored + int tableLength = s.readInt(); // original table size // Read number of mappings int mappings = s.readInt(); @@ -1078,8 +1103,13 @@ capacity <<= 1; } - table = new Entry[capacity]; - threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); + if (tableLength == 0) { + table = EMPTY_TABLE; + // and threshold == initialCapacity, already deserialized + } else { + table = new Entry[capacity]; + threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); + } init(); // Give subclass a chance to do its thing. @@ -1094,6 +1124,6 @@ } // These methods are used when serializing HashSets - int capacity() { return table.length; } + int capacity() { int tlen = table.length; return tlen == 0 ? threshold : tlen; } float loadFactor() { return loadFactor; } }