static <K, V> ImmutableBiMap<K, V> fromEntryArray(int n, Entry<K, V>[] entryArray) { checkPositionIndex(n, entryArray.length); int tableSize = Hashing.closedTableSize(n, MAX_LOAD_FACTOR); int mask = tableSize - 1; ImmutableMapEntry<K, V>[] keyTable = createEntryArray(tableSize); int keyHash = key.hashCode(); int valueHash = value.hashCode(); int keyBucket = Hashing.smear(keyHash) & mask; int valueBucket = Hashing.smear(valueHash) & mask;
private void rehashIfNecessary() { BiEntry<K, V>[] oldKToV = hashTableKToV; if (Hashing.needsResizing(size, oldKToV.length, LOAD_FACTOR)) { int newTableSize = oldKToV.length * 2; this.hashTableKToV = createTable(newTableSize); this.hashTableVToK = createTable(newTableSize); this.mask = newTableSize - 1; this.size = 0; for (BiEntry<K, V> entry = firstInKeyInsertionOrder; entry != null; entry = entry.nextInKeyInsertionOrder) { insert(entry, entry); } this.modCount++; } }
private V put(@Nullable K key, @Nullable V value, boolean force) { int keyHash = smearedHash(key); int valueHash = smearedHash(value); BiEntry<K, V> oldEntryForKey = seekByKey(key, keyHash); if (oldEntryForKey != null && valueHash == oldEntryForKey.valueHash && Objects.equal(value, oldEntryForKey.value)) { return value; } BiEntry<K, V> oldEntryForValue = seekByValue(value, valueHash); if (oldEntryForValue != null) { if (force) { delete(oldEntryForValue); } else { throw new IllegalArgumentException("value already present: " + value); } } BiEntry<K, V> newEntry = new BiEntry<>(key, keyHash, value, valueHash); if (oldEntryForKey != null) { delete(oldEntryForKey); insert(newEntry, oldEntryForKey); oldEntryForKey.prevInKeyInsertionOrder = null; oldEntryForKey.nextInKeyInsertionOrder = null; return oldEntryForKey.value; } else { insert(newEntry, null); rehashIfNecessary(); return null; } }
@Override public boolean containsKey(@Nullable Object key) { return seekByKey(key, smearedHash(key)) != null; }
@Override public void remove() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } checkRemove(toRemove != null); delete(toRemove); expectedModCount = modCount; toRemove = null; }
@Override public boolean containsValue(@Nullable Object value) { return seekByValue(value, smearedHash(value)) != null; }
@Override public int count(@Nullable Object element) { Multisets.ImmutableEntry<E>[] hashTable = this.hashTable; if (element == null || hashTable == null) { return 0; } int hash = Hashing.smearedHash(element); int mask = hashTable.length - 1; for (Multisets.ImmutableEntry<E> entry = hashTable[hash & mask]; entry != null; entry = entry.nextInBucket()) { if (Objects.equal(element, entry.getElement())) { return entry.getCount(); } } return 0; }
/** Builds a new open-addressed hash table from the first n objects in elements. */ static Object[] rebuildHashTable(int newTableSize, Object[] elements, int n) { Object[] hashTable = new Object[newTableSize]; int mask = hashTable.length - 1; for (int i = 0; i < n; i++) { Object e = elements[i]; int j0 = Hashing.smear(e.hashCode()); for (int j = j0; ; j++) { int index = j & mask; if (hashTable[index] == null) { hashTable[index] = e; break; } } } return hashTable; }
RegularImmutableMap(Entry<?, ?>... immutableEntries) { // each of our 6 callers carefully put only Entry<K, V>s into the array! @SuppressWarnings("unchecked") Entry<K, V>[] tmp = (Entry<K, V>[]) immutableEntries; this.entries = tmp; int tableSize = Hashing.chooseTableSize(immutableEntries.length); table = new Object[tableSize * 2]; mask = tableSize - 1; int keySetHashCodeMutable = 0; for (Entry<K, V> entry : this.entries) { K key = entry.getKey(); int keyHashCode = key.hashCode(); for (int i = Hashing.smear(keyHashCode); true; i++) { int index = (i & mask) * 2; Object existing = table[index]; if (existing == null) { V value = entry.getValue(); table[index] = key; table[index + 1] = value; keySetHashCodeMutable += keyHashCode; break; } else if (existing.equals(key)) { throw new IllegalArgumentException("duplicate key: " + key); } } } keySetHashCode = keySetHashCodeMutable; }
@NullableDecl V put(@NullableDecl K key, @NullableDecl V value, boolean force) { int keyHash = Hashing.smearedHash(key); int entryForKey = findEntryByKey(key, keyHash); if (entryForKey != ABSENT) { int valueHash = Hashing.smearedHash(value); int valueEntry = findEntryByValue(value, valueHash); if (force) {
private void init(int expectedSize) { checkNonnegative(expectedSize, "expectedSize"); int tableSize = Hashing.closedTableSize(expectedSize, LOAD_FACTOR); this.hashTableKToV = createTable(tableSize); this.hashTableVToK = createTable(tableSize); this.firstInKeyInsertionOrder = null; this.lastInKeyInsertionOrder = null; this.size = 0; this.mask = tableSize - 1; this.modCount = 0; }
@Override public void remove() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } checkRemove(toRemove != null); delete(toRemove); expectedModCount = modCount; toRemove = null; }
/** Pseudoconstructor for serialization support. */ void init(int expectedSize, float loadFactor) { Preconditions.checkArgument(expectedSize >= 0, "Initial capacity must be non-negative"); Preconditions.checkArgument(loadFactor > 0, "Illegal load factor"); int buckets = Hashing.closedTableSize(expectedSize, loadFactor); this.table = newTable(buckets); this.loadFactor = loadFactor; this.keys = new Object[expectedSize]; this.values = new Object[expectedSize]; this.entries = newEntries(expectedSize); this.threshold = Math.max(1, (int) (buckets * loadFactor)); }
@Override public boolean containsKey(@Nullable Object key) { return seekByKey(key, smearedHash(key)) != null; }
@Override public boolean containsValue(@Nullable Object value) { return seekByValue(value, smearedHash(value)) != null; }
@Override public boolean contains(@Nullable Object target) { Object[] table = this.table; if (target == null || table == null) { return false; } for (int i = Hashing.smearedHash(target); ; i++) { i &= mask; Object candidate = table[i]; if (candidate == null) { return false; } else if (candidate.equals(target)) { return true; } } }
public void testValueSetHashTableExpansion() { LinkedHashMultimap<String, Integer> multimap = LinkedHashMultimap.create(); for (int z = 1; z <= 100; z++) { multimap.put("a", z); // The Eclipse compiler (and hence GWT) rejects a parameterized cast. @SuppressWarnings("unchecked") LinkedHashMultimap<String, Integer>.ValueSet valueSet = (LinkedHashMultimap.ValueSet) multimap.backingMap().get("a"); assertEquals(z, valueSet.size()); assertFalse( Hashing.needsResizing( valueSet.size(), valueSet.hashTable.length, LinkedHashMultimap.VALUE_SET_LOAD_FACTOR)); } }
@Nullable @Override public V get(@Nullable Object key) { return Maps.valueOrNull(seekByKey(key, smearedHash(key))); }