--- 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 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