diff --git a/src/share/classes/java/util/ArrayList.java b/src/share/classes/java/util/ArrayList.java index 1377326988f1064ba4b77208ef6517315ddff282..42a97e70f044601b0386f9858285d44083157e06 100644 --- a/src/share/classes/java/util/ArrayList.java +++ b/src/share/classes/java/util/ArrayList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -104,11 +104,6 @@ public class ArrayList extends AbstractList { private static final long serialVersionUID = 8683452581122892189L; - /** - * Shared empty array instance used for empty instances. - */ - private static final Object EMPTY_ELEMENTDATA[] = new Object[0]; - /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. @@ -141,8 +136,7 @@ public class ArrayList extends AbstractList * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { - super(); - this.elementData = EMPTY_ELEMENTDATA; + this(10); } /** @@ -168,7 +162,8 @@ public class ArrayList extends AbstractList */ public void trimToSize() { modCount++; - if (size < elementData.length) { + int oldCapacity = elementData.length; + if (size < oldCapacity) { elementData = Arrays.copyOf(elementData, size); } } @@ -182,20 +177,11 @@ public class ArrayList extends AbstractList */ public void ensureCapacity(int minCapacity) { if (minCapacity > 0) - ensureExplicitCapacity(minCapacity); + ensureCapacityInternal(minCapacity); } private void ensureCapacityInternal(int minCapacity) { - if(elementData == EMPTY_ELEMENTDATA) { - minCapacity = Math.max(10, minCapacity); - } - - ensureExplicitCapacity(minCapacity); - } - - private void ensureExplicitCapacity(int minCapacity) { modCount++; - // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); @@ -520,7 +506,8 @@ public class ArrayList extends AbstractList modCount++; // Let gc do its work - Arrays.fill(elementData, null); + for (int i = 0; i < size; i++) + elementData[i] = null; size = 0; } @@ -601,8 +588,8 @@ public class ArrayList extends AbstractList // Let gc do its work int newSize = size - (toIndex-fromIndex); - Arrays.fill(elementData, newSize, size, null); - size = newSize; + while (size != newSize) + elementData[--size] = null; } /** @@ -690,8 +677,8 @@ public class ArrayList extends AbstractList w += size - r; } if (w != size) { - // Let gc do its work - Arrays.fill(elementData, w, size, null); + for (int i = w; i < size; i++) + elementData[i] = null; modCount += size - w; size = w; modified = true; @@ -715,7 +702,7 @@ public class ArrayList extends AbstractList s.defaultWriteObject(); // Write out array length - s.writeInt((elementData == EMPTY_ELEMENTDATA) ? 10 : elementData.length); + s.writeInt(elementData.length); // Write out all elements in the proper order. for (int i=0; i extends AbstractList if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } + } /** @@ -735,16 +723,10 @@ public class ArrayList extends AbstractList // Read in size, and any hidden stuff s.defaultReadObject(); - // Read in array length - int initialCapacity = s.readInt(); - elementData = EMPTY_ELEMENTDATA; - - if((size > 0) || (initialCapacity != 10)) { - // allocate array based upon size. - ensureCapacityInternal(size); - } + // Read in array length and allocate array + int arrayLength = s.readInt(); + Object[] a = elementData = new Object[arrayLength]; - Object[] a = elementData; // Read in all elements in the proper order. for (int i=0; i */ static final float DEFAULT_LOAD_FACTOR = 0.75f; - /** - * An empty table instance to share when the table is not inflated. - */ - static final Entry[] EMPTY_TABLE = {}; - /** * The table, resized as necessary. Length MUST Always be a power of two. */ - transient Entry[] table = EMPTY_TABLE; + transient Entry[] table; /** * The number of key-value mappings contained in this map. @@ -228,8 +223,14 @@ public class HashMap throw new IllegalArgumentException("Illegal load factor: " + loadFactor); + // Find a power of 2 >= initialCapacity + int capacity = 1; + while (capacity < initialCapacity) + capacity <<= 1; + this.loadFactor = loadFactor; - threshold = initialCapacity; + threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); + table = new Entry[capacity]; init(); } @@ -264,30 +265,9 @@ public class HashMap public HashMap(Map m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); - inflateTable(threshold); - putAllForCreate(m); } - private static int roundUpToPowerOf2(int number) { - int rounded = (rounded = Integer.highestOneBit(number)) != 0 - ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded - : 1; - - return rounded; - } - - /** - * Inflate the table - */ - final void inflateTable(int toSize) { - // Find a power of 2 >= initialCapacity - int capacity = roundUpToPowerOf2(toSize); - - threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); - table = new Entry[capacity]; - } - // internal utilities /** @@ -325,7 +305,6 @@ public class HashMap * Returns index for hash code h. */ static int indexFor(int h, int length) { - if (Integer.bitCount(length) != 1) { throw new Error("Ya dun messed up good"); } return h & (length-1); } @@ -390,10 +369,6 @@ public class HashMap */ @SuppressWarnings("unchecked") final Entry getEntry(Object key) { - if (isEmpty()) { - return null; - } - int hash = (key == null) ? 0 : hash(key); for (Entry e = table[indexFor(hash, table.length)]; e != null; @@ -406,6 +381,7 @@ public class HashMap return null; } + /** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old @@ -419,9 +395,6 @@ public class HashMap * previously associated null with key.) */ public V put(K key, V value) { - if (table == EMPTY_TABLE) { - inflateTable(threshold); - } if (key == null) return putForNullKey(value); int hash = hash(key); @@ -556,10 +529,6 @@ public class HashMap if (numKeysToBeAdded == 0) return; - if (table == EMPTY_TABLE) { - inflateTable(Math.max((int) (numKeysToBeAdded * loadFactor), threshold)); - } - /* * Expand the map if the map if the number of mappings to be added * is greater than or equal to threshold. This is conservative; the @@ -604,9 +573,6 @@ public class HashMap * for this key. */ final Entry removeEntryForKey(Object key) { - if(isEmpty()) { - return null; - } int hash = (key == null) ? 0 : hash(key); int i = indexFor(hash, table.length); @SuppressWarnings("unchecked") @@ -639,7 +605,7 @@ public class HashMap * for matching. */ final Entry removeMapping(Object o) { - if (isEmpty() || !(o instanceof Map.Entry)) + if (!(o instanceof Map.Entry)) return null; Map.Entry entry = (Map.Entry) o; @@ -675,7 +641,9 @@ public class HashMap */ public void clear() { modCount++; - Arrays.fill(table, null); + Entry[] tab = table; + for (int i = 0; i < tab.length; i++) + tab[i] = null; size = 0; } @@ -688,10 +656,6 @@ public class HashMap * specified value */ public boolean containsValue(Object value) { - if(isEmpty()) { - return false; - } - if (value == null) return containsNullValue(); @@ -729,9 +693,7 @@ public class HashMap } catch (CloneNotSupportedException e) { // assert false; } - result.table = (table == EMPTY_TABLE) - ? EMPTY_TABLE - : new Entry[table.length]; + result.table = new Entry[table.length]; result.entrySet = null; result.modCount = 0; result.size = 0; @@ -787,7 +749,8 @@ public class HashMap } public final int hashCode() { - return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); + return (key==null ? 0 : key.hashCode()) ^ + (value==null ? 0 : value.hashCode()); } public final String toString() { @@ -1045,7 +1008,7 @@ public class HashMap * serialize it). * * @serialData The capacity of the HashMap (the length of the - * bucket array, a power of 2) is emitted (int), followed by the + * bucket array) is emitted (int), followed by the * size (an int, the number of key-value * mappings), followed by the key (Object) and value (Object) * for each key-value mapping. The key-value mappings are @@ -1054,14 +1017,14 @@ public class HashMap private void writeObject(java.io.ObjectOutputStream s) throws IOException { + Iterator> i = + (size > 0) ? entrySet0().iterator() : null; + // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); // Write out number of buckets - if (table==EMPTY_TABLE) - s.writeInt(roundUpToPowerOf2(threshold)); - else - s.writeInt(table.length); + s.writeInt(table.length); // Write out size (number of Mappings) s.writeInt(size); @@ -1095,15 +1058,7 @@ public class HashMap sun.misc.Hashing.randomHashSeed(this)); // Read in number of buckets and allocate the bucket array; - table = EMPTY_TABLE; - - int buckets = s.readInt(); - - if ((buckets < 0) || // negative - (buckets > HashMap.MAXIMUM_CAPACITY) || // fits in array - (Integer.bitCount(buckets) > 1)) /* not power of 2 or zero */ { - throw new InvalidObjectException("Illegal capacity: " + buckets); - } + s.readInt(); // ignored // Read number of mappings int mappings = s.readInt(); @@ -1111,24 +1066,23 @@ public class HashMap throw new InvalidObjectException("Illegal mappings count: " + mappings); - int mappingsCapacity = Math.max( - (int) Math.min( - // capacity chosen by number of mappings - // and desired load (if >= 0.25) - mappings * Math.min(1 / loadFactor, 4.0f), - // we have limits... - HashMap.MAXIMUM_CAPACITY), - // maybe they want extra buckets. - buckets); - - if(mappings > 0) { - inflateTable(mappingsCapacity); - } else { - threshold = mappingsCapacity; + int initialCapacity = (int) Math.min( + // capacity chosen by number of mappings + // and desired load (if >= 0.25) + mappings * Math.min(1 / loadFactor, 4.0f), + // we have limits... + HashMap.MAXIMUM_CAPACITY); + int capacity = 1; + // find smallest power of two which holds all mappings + while (capacity < initialCapacity) { + capacity <<= 1; } + table = new Entry[capacity]; + threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1); init(); // Give subclass a chance to do its thing. + // Read the keys and values, and put the mappings in the HashMap for (int i=0; i Map mapClone(Map map) { - Method cloneMethod; - - try { - cloneMethod = map.getClass().getMethod("clone", new Class[]{}); - } catch (NoSuchMethodException | SecurityException all) { - cloneMethod = null; - } - - if (null != cloneMethod) { - try { - Map result = (Map)cloneMethod.invoke(map, new Object[]{}); - return result; - } catch (Exception all) { - fail("clone() failed " + map.getClass().getSimpleName(), all); - return null; - } - } else { - Constructor copyConstructor; - try { - copyConstructor = (Constructor)map.getClass().getConstructor(new Class[]{Map.class}); - - Map result = (Map)copyConstructor.newInstance(new Object[]{map}); - - return result; - } catch (Exception all) { - return serialClone(map); - } - } - } - - @Test(dataProvider = "Map") - public void testSerialization(String description, Map map) { - Object foo = new Object(); - - Map clone = mapClone(map); - Map serialClone = serialClone(map); - - assertEquals(map, map, description + ":should equal self"); - assertEquals(clone, map, description + ":should equal clone"); - assertEquals(map, clone, description + ": should equal orginal map"); - assertEquals(serialClone, map, description + ": should equal deserialized clone"); - assertEquals(map, serialClone, description + ": should equal original map"); - assertEquals(serialClone, clone, description + ": deserialized clone should equal clone"); - assertEquals(clone, serialClone, description + ": clone should equal deserialized clone"); - - assertFalse(map.containsKey(EXTRA_KEY), description + ":unexpected key"); - assertFalse(clone.containsKey(EXTRA_KEY), description + ":unexpected key"); - assertFalse(serialClone.containsKey(EXTRA_KEY), description + ":unexpected key"); - map.put(EXTRA_KEY, EXTRA_VALUE); - clone.put(EXTRA_KEY, EXTRA_VALUE); - serialClone.put(EXTRA_KEY, EXTRA_VALUE); - assertTrue(map.containsKey(EXTRA_KEY), description + ":missing key"); - assertTrue(clone.containsKey(EXTRA_KEY), description + ":missing key"); - assertTrue(serialClone.containsKey(EXTRA_KEY), description + ":missing key"); - assertSame(map.get(EXTRA_KEY), EXTRA_VALUE, description + ":wrong value"); - assertSame(clone.get(EXTRA_KEY), EXTRA_VALUE, description + ":wrong value"); - assertSame(serialClone.get(EXTRA_KEY), EXTRA_VALUE, description + ":wrong value"); - - assertEquals(map, map, description + ":should equal self"); - assertEquals(clone, map, description + ":should equal clone"); - assertEquals(map, clone, description + ": should equal orginal map"); - assertEquals(serialClone, map, description + ": should equal deserialized clone"); - assertEquals(map, serialClone, description + ": should equal original map"); - assertEquals(serialClone, clone, description + ": deserialized clone should equal clone"); - assertEquals(clone, serialClone, description + ": clone should equal deserialized clone"); - } - - static byte[] serializedForm(Object obj) { - try { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - new ObjectOutputStream(baos).writeObject(obj); - return baos.toByteArray(); - } catch (IOException e) { - fail("Unexpected Exception", e); - return null; - } - } - - static Object readObject(byte[] bytes) throws IOException, ClassNotFoundException { - InputStream is = new ByteArrayInputStream(bytes); - return new ObjectInputStream(is).readObject(); - } - - @SuppressWarnings("unchecked") - static T serialClone(T obj) { - try { - return (T)readObject(serializedForm(obj)); - } catch (IOException | ClassNotFoundException e) { - fail("Unexpected Exception", e); - return null; - } - } - - @DataProvider(name = "Map", parallel = true) - private static Iterator makeMaps() { - return Arrays.asList( - // empty - new Object[]{"HashMap", new HashMap()}, - new Object[]{"LinkedHashMap", new LinkedHashMap()}, - new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(new HashMap(), IntegerEnum.class, String.class)}, - new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(new HashMap())}, - // null hostile - new Object[]{"EnumMap", new EnumMap(IntegerEnum.class)}, - new Object[]{"Hashtable", new Hashtable()}, - new Object[]{"TreeMap", new TreeMap()}, - new Object[]{"ConcurrentHashMap", new ConcurrentHashMap()}, - new Object[]{"ConcurrentSkipListMap", new ConcurrentSkipListMap()}, - new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(new ConcurrentHashMap(), IntegerEnum.class, String.class)}, - new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(new EnumMap(IntegerEnum.class))}, - // filled - new Object[]{"HashMap", fillMap(new HashMap())}, - new Object[]{"LinkedHashMap", fillMap(new LinkedHashMap())}, - new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(fillMap(new HashMap()), IntegerEnum.class, String.class)}, - new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(fillMap(new HashMap()))}, - // null hostile - new Object[]{"EnumMap", fillMap(new EnumMap(IntegerEnum.class))}, - new Object[]{"Hashtable", fillMap(new Hashtable())}, - new Object[]{"TreeMap", fillMap(new TreeMap())}, - new Object[]{"ConcurrentHashMap", fillMap(new ConcurrentHashMap())}, - new Object[]{"ConcurrentSkipListMap", fillMap(new ConcurrentSkipListMap())}, - new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(fillMap(new ConcurrentHashMap()), IntegerEnum.class, String.class)}, - new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(fillMap(new EnumMap(IntegerEnum.class)))}).iterator(); - } - - private static Map fillMap(Map result) { - for (int each = 0; each < TEST_SIZE; each++) { - result.put(KEYS[each], VALUES[each]); - } - - return result; - } -}