--- old/src/share/classes/java/util/IdentityHashMap.java 2014-07-12 17:28:30.540338308 +0200 +++ new/src/share/classes/java/util/IdentityHashMap.java 2014-07-12 17:28:30.452339866 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package java.util; -import java.io.*; import java.lang.reflect.Array; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -74,7 +73,7 @@ * maximum size and the number of buckets is unspecified. * *
If the size of the map (the number of key-value mappings) sufficiently
- * exceeds the expected maximum size, the number of buckets is increased
+ * exceeds the expected maximum size, the number of buckets is increased.
* Increasing the number of buckets ("rehashing") may be fairly expensive, so
* it pays to create identity hash maps with a sufficiently large expected
* maximum size. On the other hand, iteration over collection views requires
@@ -157,14 +156,23 @@
private static final int MINIMUM_CAPACITY = 4;
/**
- * The maximum capacity, used if a higher value is implicitly specified
- * by either of the constructors with arguments.
- * MUST be a power of two <= 1<<29.
+ * The maximum capacity (1 << 29) + (1 << 28), used if a higher value
+ * is implicitly specified by either of the constructors with arguments.
+ * MAXIMUM_CAPACITY / 3 must be an integer and power of two.
*/
- private static final int MAXIMUM_CAPACITY = 1 << 29;
+ private static final int MAXIMUM_CAPACITY = (1 << 29) + (1 << 28);
/**
- * The table, resized as necessary. Length MUST always be a power of two.
+ * The map can hold no more than MAXIMUM_SIZE items
+ * because performance degrades if size grows much over 66% of capacity.
+ * (because of serialization compatibility with older versions, we can
+ * not hold more than (1 << 29) - 1 elements)
+ */
+ private static final int MAXIMUM_SIZE = (1 << 29) - 1;
+
+ /**
+ * The table, resized as necessary. Length MUST always be a power of two
+ * or 2 * MAXIMUM_CAPACITY
*/
transient Object[] table; // non-private to simplify nested class access
@@ -181,11 +189,6 @@
transient int modCount;
/**
- * The next size value at which to resize (capacity * load factor).
- */
- private transient int threshold;
-
- /**
* Value representing null keys inside tables.
*/
static final Object NULL_KEY = new Object();
@@ -229,27 +232,24 @@
}
/**
- * Returns the appropriate capacity for the specified expected maximum
- * size. Returns the smallest power of two between MINIMUM_CAPACITY
- * and MAXIMUM_CAPACITY, inclusive, that is greater than
- * (3 * expectedMaxSize)/2, if such a number exists. Otherwise
- * returns MAXIMUM_CAPACITY. If (3 * expectedMaxSize)/2 is negative, it
- * is assumed that overflow has occurred, and MAXIMUM_CAPACITY is returned.
- */
- private int capacity(int expectedMaxSize) {
- // Compute min capacity for expectedMaxSize given a load factor of 2/3
- int minCapacity = (3 * expectedMaxSize)/2;
-
- // Compute the appropriate capacity
- int result;
- if (minCapacity > MAXIMUM_CAPACITY || minCapacity < 0) {
- result = MAXIMUM_CAPACITY;
- } else {
- result = MINIMUM_CAPACITY;
- while (result < minCapacity)
- result <<= 1;
- }
- return result;
+ * Returns the appropriate capacity for the given expected maximum size.
+ * Returns the smallest power of two between MINIMUM_CAPACITY and
+ * MAXIMUM_CAPACITY, inclusive, that is greater than (3 *
+ * expectedMaxSize)/2, if such a number exists. Otherwise returns
+ * MAXIMUM_CAPACITY. If (3 * expectedMaxSize) is negative, it is assumed
+ * that overflow has occurred, and MAXIMUM_CAPACITY is returned.
+ */
+ private static int capacity(int expectedMaxSize) {
+ // assert expectedMaxSize >= 0;
+ int capacity = Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));
+ return Math.min(
+ MAXIMUM_CAPACITY,
+ Math.max(
+ MINIMUM_CAPACITY,
+ capacity < 0 ? MAXIMUM_CAPACITY // overflow
+ : capacity
+ )
+ );
}
/**
@@ -258,11 +258,11 @@
* MINIMUM_CAPACITY and MAXIMUM_CAPACITY inclusive.
*/
private void init(int initCapacity) {
- // assert (initCapacity & -initCapacity) == initCapacity; // power of 2
+ // assert initCapacity == MAXIMUM_CAPACITY ||
+ // (initCapacity & -initCapacity) == initCapacity; // power of 2
// assert initCapacity >= MINIMUM_CAPACITY;
// assert initCapacity <= MAXIMUM_CAPACITY;
- threshold = (initCapacity * 2)/3;
table = new Object[2 * initCapacity];
}
@@ -304,15 +304,25 @@
*/
private static int hash(Object x, int length) {
int h = System.identityHashCode(x);
- // Multiply by -127, and left-shift to use least bit as part of hash
- return ((h << 1) - (h << 8)) & (length - 1);
+ // multiply by -127
+ h -= h << 7;
+ if (length < MAXIMUM_CAPACITY * 2) { // length is 2^n
+ // left-shift to use least bit as part of hash
+ return (h << 1) & (length - 1);
+ }
+ // assert length == MAXIMUM_CAPACITY * 2
+ // clamp
+ h &= (MAXIMUM_CAPACITY / 3 * 4 - 1);
+ // Multiply by 3/2 and reset 0th bit
+ return (h + (h >> 1)) & ~1;
}
/**
* Circularly traverses table of size len.
*/
private static int nextKeyIndex(int i, int len) {
- return (i + 2 < len ? i + 2 : 0);
+ int i2 = i + 2;
+ return (i2 < len ? i2 : 0);
}
/**
@@ -429,52 +439,58 @@
* @see #containsKey(Object)
*/
public V put(K key, V value) {
- Object k = maskNull(key);
- Object[] tab = table;
- int len = tab.length;
- int i = hash(k, len);
+ final Object k = maskNull(key);
- Object item;
- while ( (item = tab[i]) != null) {
- if (item == k) {
- @SuppressWarnings("unchecked")
+ retryAfterResize: for (;;) {
+ final Object[] tab = table;
+ final int len = tab.length;
+ int i = hash(k, len);
+
+ for (Object item; (item = tab[i]) != null;
+ i = nextKeyIndex(i, len)) {
+ if (item == k) {
+ @SuppressWarnings("unchecked")
V oldValue = (V) tab[i + 1];
- tab[i + 1] = value;
- return oldValue;
+ tab[i + 1] = value;
+ return oldValue;
+ }
}
- i = nextKeyIndex(i, len);
- }
- modCount++;
- tab[i] = k;
- tab[i + 1] = value;
- if (++size >= threshold)
- resize(len); // len == 2 * current capacity.
- return null;
+ final int s = size + 1;
+ // Use optimized form of 3 * s.
+ // Next capacity is len, 2 * current capacity.
+ if (s + (s << 1) > len && resize(len))
+ continue retryAfterResize;
+
+ modCount++;
+ tab[i] = k;
+ tab[i + 1] = value;
+ size = s;
+ return null;
+ }
}
/**
- * Resize the table to hold given capacity.
+ * Resizes the table if necessary to hold given capacity.
*
* @param newCapacity the new capacity, must be a power of two.
+ * @return whether a resize did in fact take place
*/
- private void resize(int newCapacity) {
+ private boolean resize(int newCapacity) {
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
- int newLength = newCapacity * 2;
Object[] oldTable = table;
int oldLength = oldTable.length;
- if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further
- if (threshold == MAXIMUM_CAPACITY-1)
- throw new IllegalStateException("Capacity exhausted.");
- threshold = MAXIMUM_CAPACITY-1; // Gigantic map!
- return;
+ if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further
+ if (size == MAXIMUM_SIZE)
+ throw new OutOfMemoryError("Capacity exhausted.");
+ return false;
}
+ int newLength = Math.min(2 * MAXIMUM_CAPACITY, 2 * newCapacity);
if (oldLength >= newLength)
- return;
+ return false;
Object[] newTable = new Object[newLength];
- threshold = newLength / 3;
for (int j = 0; j < oldLength; j += 2) {
Object key = oldTable[j];
@@ -490,6 +506,7 @@
}
}
table = newTable;
+ return true;
}
/**
@@ -504,8 +521,8 @@
int n = m.size();
if (n == 0)
return;
- if (n > threshold) // conservatively pre-expand
- resize(capacity(n));
+ if (n > size)
+ resize(capacity(n)); // conservatively pre-expand
for (Entry extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
@@ -542,7 +559,6 @@
return null;
i = nextKeyIndex(i, len);
}
-
}
/**
@@ -1266,8 +1282,8 @@
private static final long serialVersionUID = 8188218128353913216L;
/**
- * Save the state of the IdentityHashMap instance to a stream
- * (i.e., serialize it).
+ * Saves the state of the IdentityHashMap instance to a stream
+ * (i.e., serializes it).
*
* @serialData The size of the HashMap (the number of key-value
* mappings) (int), followed by the key (Object) and
@@ -1295,8 +1311,8 @@
}
/**
- * Reconstitute the IdentityHashMap instance from a stream (i.e.,
- * deserialize it).
+ * Reconstitutes the IdentityHashMap instance from a stream (i.e.,
+ * deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
@@ -1305,9 +1321,10 @@
// Read in size (number of Mappings)
int size = s.readInt();
-
- // Allow for 33% growth (i.e., capacity is >= 2* size()).
- init(capacity((size*4)/3));
+ if (size < 0)
+ throw new java.io.StreamCorruptedException
+ ("Illegal mappings count: " + size);
+ init(capacity(size));
// Read the keys and values, and put the mappings in the table
for (int i=0; i