diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index 72701d1660..a9d0567d24 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -11,6 +11,7 @@ import java.util.Set; import java.util.Random; import org.bukkit.Server; +import org.bukkit.craftbukkit.util.LongHash; import org.bukkit.craftbukkit.util.LongHashSet; import org.bukkit.craftbukkit.util.LongObjectHashMap; import org.bukkit.event.world.ChunkUnloadEvent; @@ -36,7 +37,7 @@ public class ChunkProviderServer implements IChunkProvider { } public boolean isChunkLoaded(int i, int j) { - return this.chunks.containsKey(i, j); // CraftBukkit + return this.chunks.containsKey(LongHash.toLong(i, j)); // CraftBukkit } public void queueUnload(int i, int j) { @@ -50,7 +51,7 @@ public class ChunkProviderServer implements IChunkProvider { if (k < -short1 || k > short1 || l < -short1 || l > short1 || !(this.world.keepSpawnInMemory)) { // Added 'this.world.keepSpawnInMemory' this.unloadQueue.add(i, j); - Chunk c = this.chunks.get(i, j); + Chunk c = this.chunks.get(LongHash.toLong(i, j)); if (c != null) { c.mustSave = true; } @@ -60,7 +61,7 @@ public class ChunkProviderServer implements IChunkProvider { // CraftBukkit start this.unloadQueue.add(i, j); - Chunk c = this.chunks.get(i, j); + Chunk c = this.chunks.get(LongHash.toLong(i, j)); if (c != null) { c.mustSave = true; } @@ -81,7 +82,7 @@ public class ChunkProviderServer implements IChunkProvider { public Chunk getChunkAt(int i, int j) { // CraftBukkit start this.unloadQueue.remove(i, j); - Chunk chunk = (Chunk) this.chunks.get(i, j); + Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); boolean newChunk = false; // CraftBukkit end @@ -96,7 +97,7 @@ public class ChunkProviderServer implements IChunkProvider { newChunk = true; // CraftBukkit } - this.chunks.put(i, j, chunk); // CraftBukkit + this.chunks.put(LongHash.toLong(i, j), chunk); // CraftBukkit if (chunk != null) { chunk.addEntities(); } @@ -121,7 +122,7 @@ public class ChunkProviderServer implements IChunkProvider { public Chunk getOrCreateChunk(int i, int j) { // CraftBukkit start - Chunk chunk = (Chunk) this.chunks.get(i, j); + Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); chunk = chunk == null ? (!this.world.isLoading && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk; if (chunk == this.emptyChunk) return chunk; diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java index c9870fd1a6..efde3f71cc 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -7,14 +7,14 @@ import java.util.List; import java.util.Random; // CraftBukkit start -import org.bukkit.craftbukkit.util.LongObjectHashMap; import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.LongObjectHashMap; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit end public final class SpawnerCreature { - private static LongObjectHashMap b = new LongObjectHashMap(); // CraftBukkit - HashMap -> LongObjectHashMap + private static LongObjectHashMap b = new LongObjectHashMap(); // CraftBukkit - HashMap -> LongObjectHashMap protected static final Class[] a = new Class[] { EntitySpider.class, EntityZombie.class, EntitySkeleton.class}; protected static ChunkPosition getRandomPosition(World world, int i, int j) { @@ -95,7 +95,7 @@ public final class SpawnerCreature { // CraftBukkit start long key = ((Long) iterator.next()).longValue(); - if (!((Boolean) b.get(key)).booleanValue()) { + if (!b.get(key)) { ChunkPosition chunkposition = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key)); // CraftBukkit end int k1 = chunkposition.x; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index eb6695b653..41f0935b2c 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -45,6 +45,7 @@ import org.bukkit.Sound; import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.plugin.messaging.StandardMessenger; +import org.bukkit.craftbukkit.util.LongHash; public class CraftWorld implements World { private final WorldServer world; @@ -174,7 +175,7 @@ public class CraftWorld implements World { } world.chunkProviderServer.unloadQueue.remove(x, z); - world.chunkProviderServer.chunks.remove(x, z); + world.chunkProviderServer.chunks.remove(LongHash.toLong(x, z)); return true; } @@ -230,7 +231,7 @@ public class CraftWorld implements World { } world.chunkProviderServer.unloadQueue.remove(x, z); - net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) world.chunkProviderServer.chunks.get(x, z); + net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) world.chunkProviderServer.chunks.get(LongHash.toLong(x, z)); if (chunk == null) { chunk = world.chunkProviderServer.loadChunk(x, z); @@ -243,7 +244,7 @@ public class CraftWorld implements World { @SuppressWarnings("unchecked") private void chunkLoadPostProcess(net.minecraft.server.Chunk chunk, int x, int z) { if (chunk != null) { - world.chunkProviderServer.chunks.put(x, z, chunk); + world.chunkProviderServer.chunks.put(LongHash.toLong(x, z), chunk); chunk.addEntities(); diff --git a/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java b/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java index 34cc0a6ed8..01861ccff8 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java +++ b/src/main/java/org/bukkit/craftbukkit/util/LongObjectHashMap.java @@ -1,138 +1,185 @@ -/* -Based on OpenLongObjectHashMap from colt. Original copyright follows: - -Copyright © 1999 CERN - European Organization for Nuclear Research. -Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose -is hereby granted without fee, provided that the above copyright notice appear in all copies and -that both that copyright notice and this permission notice appear in supporting documentation. -CERN makes no representations about the suitability of this software for any purpose. -It is provided "as is" without expressed or implied warranty. -*/ - package org.bukkit.craftbukkit.util; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.Arrays; import java.util.Collection; +import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Iterator; -import java.util.Map.Entry; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; -public class LongObjectHashMap implements Cloneable { - private long keys[]; - private V values[]; +import static org.bukkit.craftbukkit.util.Java15Compat.Arrays_copyOf; - private int freeEntries; - private int elements; +@SuppressWarnings("unchecked") +public class LongObjectHashMap implements Cloneable, Serializable { + static final long serialVersionUID = 2841537710170573815L; - private int highWaterMark; - private int lowWaterMark; - private final double minLoadFactor; - private final double maxLoadFactor; + private static final long EMPTY_KEY = Long.MIN_VALUE; + private static final int BUCKET_SIZE = 4096; - private final long FREE = 0; - private final long REMOVED = Long.MIN_VALUE; - - private static final int defaultCapacity = 277; - private static final double defaultMinLoadFactor = 0.2; - private static final double defaultMaxLoadFactor = 0.5; - - private static final int largestPrime = Integer.MAX_VALUE; - private static final int[] primeCapacities = { - largestPrime, - - 5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877, 205759, - 411527, 823117, 1646237, 3292489, 6584983, 13169977, 26339969, 52679969, 105359939, - 210719881, 421439783, 842879579, 1685759167, - - 433, 877, 1759, 3527, 7057, 14143, 28289, 56591, 113189, 226379, 452759, 905551, 1811107, - 3622219, 7244441, 14488931, 28977863, 57955739, 115911563, 231823147, 463646329, 927292699, - 1854585413, - - 953, 1907, 3821, 7643, 15287, 30577, 61169, 122347, 244703, 489407, 978821, 1957651, 3915341, - 7830701, 15661423, 31322867, 62645741, 125291483, 250582987, 501165979, 1002331963, - 2004663929, - - 1039, 2081, 4177, 8363, 16729, 33461, 66923, 133853, 267713, 535481, 1070981, 2141977, 4283963, - 8567929, 17135863, 34271747, 68543509, 137087021, 274174111, 548348231, 1096696463, - - 31, 67, 137, 277, 557, 1117, 2237, 4481, 8963, 17929, 35863, 71741, 143483, 286973, 573953, - 1147921, 2295859, 4591721, 9183457, 18366923, 36733847, 73467739, 146935499, 293871013, - 587742049, 1175484103, - - 599, 1201, 2411, 4831, 9677, 19373, 38747, 77509, 155027, 310081, 620171, 1240361, 2480729, - 4961459, 9922933, 19845871, 39691759, 79383533, 158767069, 317534141, 635068283, 1270136683, - - 311, 631, 1277, 2557, 5119, 10243, 20507, 41017, 82037, 164089, 328213, 656429, 1312867, - 2625761, 5251529, 10503061, 21006137, 42012281, 84024581, 168049163, 336098327, 672196673, - 1344393353, - - 3, 7, 17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911, 43853, 87719, 175447, 350899, - 701819, 1403641, 2807303, 5614657, 11229331, 22458671, 44917381, 89834777, 179669557, - 359339171, 718678369, 1437356741, - - 43, 89, 179, 359, 719, 1439, 2879, 5779, 11579, 23159, 46327, 92657, 185323, 370661, 741337, - 1482707, 2965421, 5930887, 11861791, 23723597, 47447201, 94894427, 189788857, 379577741, - 759155483, 1518310967, - - 379, 761, 1523, 3049, 6101, 12203, 24407, 48817, 97649, 195311, 390647, 781301, 1562611, - 3125257, 6250537, 12501169, 25002389, 50004791, 100009607, 200019221, 400038451, 800076929, - 1600153859 - }; - - static { - java.util.Arrays.sort(primeCapacities); - } + private transient long[][] keys; + private transient V[][] values; + private transient int modCount; + private transient int size; public LongObjectHashMap() { - this(defaultCapacity); + initialize(); } - public LongObjectHashMap(int initialCapacity) { - this(initialCapacity, defaultMinLoadFactor, defaultMaxLoadFactor); + public LongObjectHashMap(Map map) { + this(); + putAll(map); } - @SuppressWarnings("unchecked") - public LongObjectHashMap(int initialCapacity, double minLoadFactor, double maxLoadFactor) { - int capacity = initialCapacity; - capacity = nextPrime(capacity); - if (capacity == 0) capacity = 1; + public int size() { + return size; + } - keys = new long[capacity]; - values = (V[]) new Object[capacity]; + public boolean isEmpty() { + return size == 0; + } - this.minLoadFactor = minLoadFactor; - if (maxLoadFactor == largestPrime) { - this.maxLoadFactor = 1.0; - } else { - this.maxLoadFactor = maxLoadFactor; + public boolean containsKey(long key) { + return get(key) != null; + } + + public boolean containsValue(V value) { + for (V val : values()) { + if (val == value || val.equals(value)) { + return true; + } } - elements = 0; - freeEntries = capacity; + return false; + } - lowWaterMark = 0; - highWaterMark = chooseHighWaterMark(capacity, this.maxLoadFactor); + public V get(long key) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] inner = keys[index]; + if (inner == null) return null; + + for (int i = 0; i < inner.length; i++) { + long innerKey = inner[i]; + if (innerKey == EMPTY_KEY) { + return null; + } else if (innerKey == key) { + return values[index][i]; + } + } + + return null; + } + + public V put(long key, V value) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] innerKeys = keys[index]; + V[] innerValues = values[index]; + modCount++; + + if (innerKeys == null) { + // need to make a new chain + keys[index] = innerKeys = new long[8]; + Arrays.fill(innerKeys, EMPTY_KEY); + values[index] = innerValues = (V[]) new Object[8]; + innerKeys[0] = key; + innerValues[0] = value; + size++; + } else { + int i; + for (i = 0; i < innerKeys.length; i++) { + // found an empty spot in the chain to put this + if (innerKeys[i] == EMPTY_KEY) { + size++; + innerKeys[i] = key; + innerValues[i] = value; + return null; + } + + // found an existing entry in the chain with this key, replace it + if (innerKeys[i] == key) { + V oldValue = innerValues[i]; + innerKeys[i] = key; + innerValues[i] = value; + return oldValue; + } + } + + // chain is full, resize it and add our new entry + keys[index] = innerKeys = Arrays_copyOf(innerKeys, i << 1); + Arrays.fill(innerKeys, i, innerKeys.length, EMPTY_KEY); + values[index] = innerValues = Arrays_copyOf(innerValues, i << 1); + innerKeys[i] = key; + innerValues[i] = value; + size++; + } + + return null; + } + + public V remove(long key) { + int index = (int) (keyIndex(key) & (BUCKET_SIZE - 1)); + long[] inner = keys[index]; + if (inner == null) { + return null; + } + + for (int i = 0; i < inner.length; i++) { + // hit the end of the chain, didn't find this entry + if (inner[i] == EMPTY_KEY) { + break; + } + + if (inner[i] == key) { + V value = values[index][i]; + + for (i++; i < inner.length; i++) { + if (inner[i] == EMPTY_KEY) { + break; + } + + inner[i - 1] = inner[i]; + values[index][i - 1] = values[index][i]; + } + + inner[i - 1] = EMPTY_KEY; + values[index][i - 1] = null; + size--; + modCount++; + return value; + } + } + + return null; + } + + public void putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + put((Long) entry.getKey(), (V) entry.getValue()); + } } public void clear() { - Arrays.fill(keys, FREE); - Arrays.fill(values, null); + if (size == 0) { + return; + } - elements = 0; - freeEntries = keys.length; - trimToSize(); + modCount++; + size = 0; + Arrays.fill(keys, null); + Arrays.fill(values, null); } - @SuppressWarnings("unchecked") public Set keySet() { return new KeySet(); } - @SuppressWarnings("unchecked") public Collection values() { return new ValueCollection(); } @@ -141,278 +188,220 @@ public class LongObjectHashMap implements Cloneable { * Returns a Set of Entry objects for the HashMap. This is not how the internal * implementation is laid out so this constructs the entire Set when called. For * this reason it should be avoided if at all possible. - * @deprecated + * * @return Set of Entry objects + * @deprecated */ @Deprecated - public Set> entrySet() { - HashSet> set = new HashSet>(); - - for (int i = 0; i < keys.length; i++) { - if (keys[i] != FREE && keys[i] != REMOVED) { - set.add(new LongObjectEntry(keys[i], values[i])); - } + public Set> entrySet() { + HashSet> set = new HashSet>(); + for (long key : keySet()) { + set.add(new Entry(key, get(key))); } return set; } - public boolean containsKey(int msw, int lsw) { - return containsKey(LongHash.toLong(msw, lsw)); - } - - public boolean containsKey(long key) { - return indexOfKey(key) >= 0; - } - - public boolean containsValue(V value) { - return indexOfValue(value) >= 0; - } - - public int size() { - return elements; - } - - public boolean isEmpty() { - return elements == 0; - } - - public V get(int msw, int lsw) { - return get(LongHash.toLong(msw, lsw)); - } - - public V get(long key) { - int i = indexOfKey(key); - if (i < 0) { - return null; - } - return values[i]; - } - - public V put(int msw, int lsw, V value) { - return put(LongHash.toLong(msw, lsw), value); - } - - public V put(long key, V value) { - int i = indexOfInsertion(key); - if (i < 0) { - i = -i - 1; - V oldValue = values[i]; - values[i] = value; - return oldValue; - } - - if (elements > highWaterMark) { - int newCapacity = chooseGrowCapacity(elements + 1, minLoadFactor, maxLoadFactor); - rehash(newCapacity); - return put(key, value); - } - - if (keys[i] == FREE) { - freeEntries--; - } - - keys[i] = key; - values[i] = value; - elements++; - - if (freeEntries < 1) { - int newCapacity = chooseGrowCapacity(elements + 1, minLoadFactor, maxLoadFactor); - rehash(newCapacity); - } - - return null; - } - - public V remove(int msw, int lsw) { - return remove(LongHash.toLong(msw, lsw)); - } - - public V remove(long key) { - int i = indexOfKey(key); - if (i < 0) { - return null; - } - - V oldValue = values[i]; - keys[i] = REMOVED; - values[i] = null; - elements--; - - if (elements < lowWaterMark) { - int newCapacity = chooseShrinkCapacity(elements, minLoadFactor, maxLoadFactor); - rehash(newCapacity); - } - - return oldValue; - } - - public void ensureCapacity(int minCapacity) { - if (keys.length < minCapacity) { - int newCapacity = nextPrime(minCapacity); - rehash(newCapacity); - } - } - - public void trimToSize() { - int newCapacity = nextPrime((int) (1 + 1.2 * size())); - if (keys.length > newCapacity) { - rehash(newCapacity); - } - } - public Object clone() throws CloneNotSupportedException { - LongObjectHashMap copy = (LongObjectHashMap) super.clone(); - copy.keys = keys.clone(); - copy.values = values.clone(); - return copy; + LongObjectHashMap clone = (LongObjectHashMap) super.clone(); + // Make sure we clear any existing information from the clone + clone.clear(); + // Make sure the clone is properly setup for new entries + clone.initialize(); + + // Iterate through the data normally to do a safe clone + for (long key : keySet()) { + final V value = get(key); + clone.put(key, value); + } + + return clone; } - /** - * @param key the key to be added to the receiver. - * @return the index where the key would need to be inserted, if it is not already contained. - * Returns -index-1 if the key is already contained at slot index. - * Therefore, if the returned index < 0, then it is already contained at slot -index-1. - * If the returned index >= 0, then it is NOT already contained and should be inserted at slot index. - */ - private int indexOfInsertion(long key) { - final int length = keys.length; + private void initialize() { + keys = new long[BUCKET_SIZE][]; + values = (V[][]) new Object[BUCKET_SIZE][]; + } - final int hash = hash(key) & 0x7FFFFFFF; - int i = hash % length; - int decrement = hash % (length - 2); - if (decrement == 0) { - decrement = 1; + private long keyIndex(long key) { + key ^= key >>> 33; + key *= 0xff51afd7ed558ccdL; + key ^= key >>> 33; + key *= 0xc4ceb9fe1a85ec53L; + key ^= key >>> 33; + return key; + } + + private void writeObject(ObjectOutputStream outputStream) throws IOException { + outputStream.defaultWriteObject(); + + for (long key : keySet()) { + V value = get(key); + outputStream.writeLong(key); + outputStream.writeObject(value); } - while ((keys[i] != FREE && keys[i] != REMOVED) && keys[i] != key) { - i -= decrement; - if (i < 0) { - i += length; + outputStream.writeLong(EMPTY_KEY); + outputStream.writeObject(null); + } + + private void readObject(ObjectInputStream inputStream) throws ClassNotFoundException, IOException { + inputStream.defaultReadObject(); + initialize(); + + while (true) { + long key = inputStream.readLong(); + V value = (V) inputStream.readObject(); + if (key == EMPTY_KEY && value == null) { + break; } + + put(key, value); + } + } + + + private class ValueIterator implements Iterator { + private int count; + private int index; + private int innerIndex; + private int expectedModCount; + private long lastReturned = EMPTY_KEY; + + long prevKey = EMPTY_KEY; + V prevValue; + + ValueIterator() { + expectedModCount = LongObjectHashMap.this.modCount; } - if (keys[i] == REMOVED) { - int j = i; - while (keys[i] != FREE && (keys[i] == REMOVED || keys[i] != key)) { - i -= decrement; - if (i < 0) { - i += length; + public boolean hasNext() { + return count < LongObjectHashMap.this.size; + } + + public void remove() { + if (LongObjectHashMap.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (lastReturned == EMPTY_KEY) { + throw new IllegalStateException(); + } + + count--; + LongObjectHashMap.this.remove(lastReturned); + lastReturned = EMPTY_KEY; + expectedModCount = LongObjectHashMap.this.modCount; + } + + public V next() { + if (LongObjectHashMap.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + if (!hasNext()) { + throw new NoSuchElementException(); + } + + long[][] keys = LongObjectHashMap.this.keys; + count++; + + if (prevKey != EMPTY_KEY) { + innerIndex++; + } + + for (; index < keys.length; index++) { + if (keys[index] != null) { + for (; innerIndex < keys[index].length; innerIndex++) { + long key = keys[index][innerIndex]; + V value = values[index][innerIndex]; + if (key == EMPTY_KEY) { + break; + } + + lastReturned = key; + prevKey = key; + prevValue = value; + return prevValue; + } + innerIndex = 0; } } - if (keys[i] == FREE) { - i = j; - } - } - - if (keys[i] != FREE && keys[i] != REMOVED) { - // key already contained at slot i. - // return a negative number identifying the slot. - return -i - 1; - } - // not already contained, should be inserted at slot i. - // return a number >= 0 identifying the slot. - return i; - } - - private int indexOfKey(long key) { - final int length = keys.length; - - final int hash = hash(key) & 0x7FFFFFFF; - int i = hash % length; - int decrement = hash % (length - 2); - if (decrement == 0) decrement = 1; - - while (keys[i] != FREE && (keys[i] == REMOVED || keys[i] != key)) { - i -= decrement; - if (i < 0) i += length; - } - - if (keys[i] == FREE) { - return -1; - } - - return i; - } - - private int indexOfValue(Object value) { - for (int i = keys.length; --i >= 0; ) { - if ((keys[i] != FREE && keys[i] != REMOVED) && values[i] == value) { - return i; - } - } - - return -1; - } - - @SuppressWarnings("unchecked") - private void rehash(int newCapacity) { - int oldCapacity = keys.length; - - long oldKeys[] = keys; - V oldValues[] = values; - - long newKeys[] = new long[newCapacity]; - V newValues[] = (V[]) new Object[newCapacity]; - - lowWaterMark = chooseLowWaterMark(newCapacity, minLoadFactor); - highWaterMark = chooseHighWaterMark(newCapacity, maxLoadFactor); - - keys = newKeys; - values = newValues; - freeEntries = newCapacity - elements; - - for (int i = oldCapacity; i-- > 0; ) { - if (oldKeys[i] != FREE && oldKeys[i] != REMOVED) { - long element = oldKeys[i]; - int index = indexOfInsertion(element); - newKeys[index] = element; - newValues[index] = oldValues[i]; - } + throw new NoSuchElementException(); } } - private static int nextPrime(int desiredCapacity) { - int i = Arrays.binarySearch(primeCapacities, desiredCapacity); - if (i < 0) { - i = -i - 1; + private class KeyIterator implements Iterator { + final ValueIterator iterator; + + public KeyIterator() { + iterator = new ValueIterator(); } - return primeCapacities[i]; + public void remove() { + iterator.remove(); + } + + public boolean hasNext() { + return iterator.hasNext(); + } + + public Long next() { + iterator.next(); + return iterator.prevKey; + } } - private int chooseGrowCapacity(int size, double minLoad, double maxLoad) { - return nextPrime(Math.max(size + 1, (int) ((4 * size / (3 * minLoad + maxLoad))))); + + private class KeySet extends AbstractSet { + public void clear() { + LongObjectHashMap.this.clear(); + } + + public int size() { + return LongObjectHashMap.this.size(); + } + + public boolean contains(Object key) { + return key instanceof Long && LongObjectHashMap.this.containsKey((Long) key); + + } + + public boolean remove(Object key) { + return LongObjectHashMap.this.remove((Long) key) != null; + } + + public Iterator iterator() { + return new KeyIterator(); + } } - private int chooseShrinkCapacity(int size, double minLoad, double maxLoad) { - return nextPrime(Math.max(size + 1, (int) ((4 * size / (minLoad + 3 * maxLoad))))); + + private class ValueCollection extends AbstractCollection { + public void clear() { + LongObjectHashMap.this.clear(); + } + + public int size() { + return LongObjectHashMap.this.size(); + } + + public boolean contains(Object value) { + return LongObjectHashMap.this.containsValue((V) value); + } + + public Iterator iterator() { + return new ValueIterator(); + } } - private int chooseHighWaterMark(int capacity, double maxLoad) { - return Math.min(capacity - 2, (int) (capacity * maxLoad)); - } - private int chooseLowWaterMark(int capacity, double minLoad) { - return (int) (capacity * minLoad); - } + private class Entry implements Map.Entry { + private final Long key; + private V value; - // This method copied from Murmur3, written by Austin Appleby released under Public Domain - private int hash(long value) { - value ^= value >>> 33; - value *= 0xff51afd7ed558ccdL; - value ^= value >>> 33; - value *= 0xc4ceb9fe1a85ec53L; - value ^= value >>> 33; - return (int) value; - } - - private class LongObjectEntry implements Entry { - private final long key; - private final V value; - - LongObjectEntry(long k, V v) { + Entry(long k, V v) { key = k; value = v; } @@ -426,112 +415,10 @@ public class LongObjectHashMap implements Cloneable { } public V setValue(V v) { - // Changes are not reflected in the main HashMap - return value; - } - } - - private class KeySet extends AbstractSet { - public int size() { - return elements; - } - - public boolean contains(long key) { - return containsKey(key); - } - - @SuppressWarnings("unchecked") - public Iterator iterator() { - return new KeyIterator(); - } - } - - private class KeyIterator implements Iterator { - private int ix; - - private KeyIterator() { - for (ix = 0; ix < keys.length; ix++) { - if (values[ix] != null && keys[ix] != REMOVED) { - break; - } - } - } - - public boolean hasNext() { - return ix < keys.length; - } - - public void remove() { - throw new UnsupportedOperationException("Collection is read-only"); - } - - public Long next() { - if (ix >= keys.length) { - throw new NoSuchElementException(); - } - - long key = keys[ix++]; - - for (; ix < keys.length; ix++) { - if (keys[ix] != FREE && keys[ix] != REMOVED) { - break; - } - } - - return key; - } - } - - private class ValueCollection extends AbstractCollection { - public int size() { - return elements; - } - - @SuppressWarnings("unchecked") - public Iterator iterator() { - return new ValueIterator(); - } - - @SuppressWarnings("unchecked") - public boolean contains(Object value) { - return containsValue((V) value); - } - } - - private class ValueIterator implements Iterator { - private int ix; - - private ValueIterator() { - for (ix = 0; ix < values.length; ix++) { - if (values[ix] != null) { - break; - } - } - } - - public boolean hasNext() { - return ix < values.length; - } - - public void remove() { - throw new UnsupportedOperationException("Collection is read-only"); - } - - @SuppressWarnings("unchecked") - public V next() { - if (ix >= values.length) { - throw new NoSuchElementException(); - } - - V value = (V) values[ix++]; - - for (; ix < values.length; ix++) { - if (values[ix] != null) { - break; - } - } - - return value; + V old = value; + value = v; + put(key, v); + return old; } } }