/** Skips checks for existing keys. */ private void addResize(T key) { // Check for empty buckets. int hashCode = key.hashCode(); int index1 = hashCode & mask; T key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = key; if (size++ >= threshold) resize(capacity << 1); return; } int index2 = hash2(hashCode); T key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = key; if (size++ >= threshold) resize(capacity << 1); return; } int index3 = hash3(hashCode); T key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = key; if (size++ >= threshold) resize(capacity << 1); return; } push(key, index1, key1, index2, key2, index3, key3); }
private void push(T insertKey, int index1, T key1, int index2, T key2, int index3, T key3) { T[] keyTable = this.keyTable; int mask = this.mask; // Push keys until an empty bucket is found. T evictedKey; int i = 0, pushIterations = this.pushIterations; do { // Replace the key and value for one of the hashes. switch (MathUtils.random(2)) { case 0: evictedKey = key1; keyTable[index1] = insertKey; break; case 1: evictedKey = key2; keyTable[index2] = insertKey; break; default: evictedKey = key3; keyTable[index3] = insertKey; break; } // If the evicted key hashes to an empty bucket, put it there and stop. int hashCode = evictedKey.hashCode(); index1 = hashCode & mask; key1 = keyTable[index1]; if (key1 == null) { keyTable[index1] = evictedKey; if (size++ >= threshold) resize(capacity << 1); return; } index2 = hash2(hashCode); key2 = keyTable[index2]; if (key2 == null) { keyTable[index2] = evictedKey; if (size++ >= threshold) resize(capacity << 1); return; } index3 = hash3(hashCode); key3 = keyTable[index3]; if (key3 == null) { keyTable[index3] = evictedKey; if (size++ >= threshold) resize(capacity << 1); return; } if (++i == pushIterations) break; insertKey = evictedKey; } while (true); addStash(evictedKey); }
/** * Clears the map and reduces the size of the backing arrays to be the specified capacity if they * are larger. */ public void clear(int maximumCapacity) { if (capacity <= maximumCapacity) { clear(); return; } size = 0; resize(maximumCapacity); }
/** * Reduces the size of the backing arrays to be the specified capacity or less. If the capacity is * already less, nothing is done. If the map contains more items than the specified capacity, the * next highest power of two capacity is used instead. */ public void shrink(int maximumCapacity) { if (maximumCapacity < 0) throw new IllegalArgumentException("maximumCapacity must be >= 0: " + maximumCapacity); if (size > maximumCapacity) maximumCapacity = size; if (capacity <= maximumCapacity) return; maximumCapacity = MathUtils.nextPowerOfTwo(maximumCapacity); resize(maximumCapacity); }
/** Returns true if the key was not already in the set. */ public boolean add(T key) { if (key == null) throw new IllegalArgumentException("key cannot be null."); T[] keyTable = this.keyTable; // Check for existing keys. int hashCode = key.hashCode(); int index1 = hashCode & mask; T key1 = keyTable[index1]; if (key.equals(key1)) return false; int index2 = hash2(hashCode); T key2 = keyTable[index2]; if (key.equals(key2)) return false; int index3 = hash3(hashCode); T key3 = keyTable[index3]; if (key.equals(key3)) return false; // Find key in the stash. for (int i = capacity, n = i + stashSize; i < n; i++) if (key.equals(keyTable[i])) return false; // Check for empty buckets. if (key1 == null) { keyTable[index1] = key; if (size++ >= threshold) resize(capacity << 1); return true; } if (key2 == null) { keyTable[index2] = key; if (size++ >= threshold) resize(capacity << 1); return true; } if (key3 == null) { keyTable[index3] = key; if (size++ >= threshold) resize(capacity << 1); return true; } push(key, index1, key1, index2, key2, index3, key3); return true; }
private void addStash(T key) { if (stashSize == stashCapacity) { // Too many pushes occurred and the stash is full, increase the table size. resize(capacity << 1); add(key); return; } // Store key in the stash. int index = capacity + stashSize; keyTable[index] = key; stashSize++; size++; }
/** * Increases the size of the backing array to acommodate the specified number of additional items. * Useful before adding many items to avoid multiple backing array resizes. */ public void ensureCapacity(int additionalCapacity) { int sizeNeeded = size + additionalCapacity; if (sizeNeeded >= threshold) resize(MathUtils.nextPowerOfTwo((int) (sizeNeeded / loadFactor))); }