blob: cb723a553eb2b33d08072bed7edc232d70cc549e [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Hash map implementation with open addressing and quadratic probing.
class HashMapImplementation<K, V> implements HashMap<K, V> {
// The [_keys] list contains the keys inserted in the map.
// The [_keys] list must be a raw list because it
// will contain both elements of type K, and the [_DELETED_KEY] of type
// [_DeletedKeySentinel].
// The alternative of declaring the [_keys] list as of type Object
// does not work, because the HashSetIterator constructor would fail:
// HashSetIterator(HashSet<E> set)
// : _nextValidIndex = -1,
// _entries = set_._backingMap._keys {
// _advance();
// }
// With K being type int, for example, it would fail because
// List<Object> is not assignable to type List<int> of entries.
List _keys;
// The values inserted in the map. For a filled entry index in this
// list, there is always the corresponding key in the [keys_] list
// at the same entry index.
List<V> _values;
// The load limit is the number of entries we allow until we double
// the size of the lists.
int _loadLimit;
// The current number of entries in the map. Will never be greater
// than [_loadLimit].
int _numberOfEntries;
// The current number of deleted entries in the map.
int _numberOfDeleted;
// The sentinel when a key is deleted from the map.
static const _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel();
// The initial capacity of a hash map.
static const int _INITIAL_CAPACITY = 8; // must be power of 2
HashMapImplementation() {
_numberOfEntries = 0;
_numberOfDeleted = 0;
_loadLimit = _computeLoadLimit(_INITIAL_CAPACITY);
_keys = new List(_INITIAL_CAPACITY);
_values = new List<V>(_INITIAL_CAPACITY);
}
factory HashMapImplementation.from(Map<K, V> other) {
Map<K, V> result = new HashMapImplementation<K, V>();
other.forEach((K key, V value) { result[key] = value; });
return result;
}
static int _computeLoadLimit(int capacity) {
return (capacity * 3) ~/ 4;
}
static int _firstProbe(int hashCode, int length) {
return hashCode & (length - 1);
}
static int _nextProbe(int currentProbe, int numberOfProbes, int length) {
return (currentProbe + numberOfProbes) & (length - 1);
}
int _probeForAdding(K key) {
if (key == null) throw const NullPointerException();
int hash = _firstProbe(key.hashCode(), _keys.length);
int numberOfProbes = 1;
int initialHash = hash;
// insertionIndex points to a slot where a key was deleted.
int insertionIndex = -1;
while (true) {
// [existingKey] can be either of type [K] or [_DeletedKeySentinel].
Object existingKey = _keys[hash];
if (existingKey === null) {
// We are sure the key is not already in the set.
// If the current slot is empty and we didn't find any
// insertion slot before, return this slot.
if (insertionIndex < 0) return hash;
// If we did find an insertion slot before, return it.
return insertionIndex;
} else if (existingKey == key) {
// The key is already in the map. Return its slot.
return hash;
} else if ((insertionIndex < 0) && (_DELETED_KEY === existingKey)) {
// The slot contains a deleted element. Because previous calls to this
// method may not have had this slot deleted, we must continue iterate
// to find if there is a slot with the given key.
insertionIndex = hash;
}
// We did not find an insertion slot. Look at the next one.
hash = _nextProbe(hash, numberOfProbes++, _keys.length);
// _ensureCapacity has guaranteed the following cannot happen.
// assert(hash != initialHash);
}
}
int _probeForLookup(K key) {
if (key == null) throw const NullPointerException();
int hash = _firstProbe(key.hashCode(), _keys.length);
int numberOfProbes = 1;
int initialHash = hash;
while (true) {
// [existingKey] can be either of type [K] or [_DeletedKeySentinel].
Object existingKey = _keys[hash];
// If the slot does not contain anything (in particular, it does not
// contain a deleted key), we know the key is not in the map.
if (existingKey === null) return -1;
// The key is in the map, return its index.
if (existingKey == key) return hash;
// Go to the next probe.
hash = _nextProbe(hash, numberOfProbes++, _keys.length);
// _ensureCapacity has guaranteed the following cannot happen.
// assert(hash != initialHash);
}
}
void _ensureCapacity() {
int newNumberOfEntries = _numberOfEntries + 1;
// Test if adding an element will reach the load limit.
if (newNumberOfEntries >= _loadLimit) {
_grow(_keys.length * 2);
return;
}
// Make sure that we don't have poor performance when a map
// contains lots of deleted entries: we _grow if
// there are more deleted entried than free entries.
int capacity = _keys.length;
int numberOfFreeOrDeleted = capacity - newNumberOfEntries;
int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted;
// assert(numberOfFree > 0);
if (_numberOfDeleted > numberOfFree) {
_grow(_keys.length);
}
}
static bool _isPowerOfTwo(int x) {
return ((x & (x - 1)) == 0);
}
void _grow(int newCapacity) {
assert(_isPowerOfTwo(newCapacity));
int capacity = _keys.length;
_loadLimit = _computeLoadLimit(newCapacity);
List oldKeys = _keys;
List<V> oldValues = _values;
_keys = new List(newCapacity);
_values = new List<V>(newCapacity);
for (int i = 0; i < capacity; i++) {
// [key] can be either of type [K] or [_DeletedKeySentinel].
Object key = oldKeys[i];
// If there is no key, we don't need to deal with the current slot.
if (key === null || key === _DELETED_KEY) {
continue;
}
V value = oldValues[i];
// Insert the {key, value} pair in their new slot.
int newIndex = _probeForAdding(key);
_keys[newIndex] = key;
_values[newIndex] = value;
}
_numberOfDeleted = 0;
}
void clear() {
_numberOfEntries = 0;
_numberOfDeleted = 0;
int length = _keys.length;
for (int i = 0; i < length; i++) {
_keys[i] = null;
_values[i] = null;
}
}
void operator []=(K key, V value) {
_ensureCapacity();
int index = _probeForAdding(key);
if ((_keys[index] === null) || (_keys[index] === _DELETED_KEY)) {
_numberOfEntries++;
}
_keys[index] = key;
_values[index] = value;
}
V operator [](K key) {
int index = _probeForLookup(key);
if (index < 0) return null;
return _values[index];
}
V putIfAbsent(K key, V ifAbsent()) {
int index = _probeForLookup(key);
if (index >= 0) return _values[index];
V value = ifAbsent();
this[key] = value;
return value;
}
V remove(K key) {
int index = _probeForLookup(key);
if (index >= 0) {
_numberOfEntries--;
V value = _values[index];
_values[index] = null;
// Set the key to the sentinel to not break the probing chain.
_keys[index] = _DELETED_KEY;
_numberOfDeleted++;
return value;
}
return null;
}
bool isEmpty() {
return _numberOfEntries == 0;
}
int get length {
return _numberOfEntries;
}
void forEach(void f(K key, V value)) {
int length = _keys.length;
for (int i = 0; i < length; i++) {
var key = _keys[i];
if ((key !== null) && (key !== _DELETED_KEY)) {
f(key, _values[i]);
}
}
}
Collection<K> getKeys() {
List<K> list = new List<K>(length);
int i = 0;
forEach(void _(K key, V value) {
list[i++] = key;
});
return list;
}
Collection<V> getValues() {
List<V> list = new List<V>(length);
int i = 0;
forEach(void _(K key, V value) {
list[i++] = value;
});
return list;
}
bool containsKey(K key) {
return (_probeForLookup(key) != -1);
}
bool containsValue(V value) {
int length = _values.length;
for (int i = 0; i < length; i++) {
var key = _keys[i];
if ((key !== null) && (key !== _DELETED_KEY)) {
if (_values[i] == value) return true;
}
}
return false;
}
String toString() {
return Maps.mapToString(this);
}
}
class HashSetImplementation<E > implements HashSet<E> {
HashSetImplementation() {
_backingMap = new HashMapImplementation<E, E>();
}
factory HashSetImplementation.from(Iterable<E> other) {
Set<E> set = new HashSetImplementation<E>();
for (final e in other) {
set.add(e);
}
return set;
}
void clear() {
_backingMap.clear();
}
void add(E value) {
_backingMap[value] = value;
}
bool contains(E value) {
return _backingMap.containsKey(value);
}
bool remove(E value) {
if (!_backingMap.containsKey(value)) return false;
_backingMap.remove(value);
return true;
}
void addAll(Collection<E> collection) {
collection.forEach(void _(E value) {
add(value);
});
}
Set<E> intersection(Collection<E> collection) {
Set<E> result = new Set<E>();
collection.forEach(void _(E value) {
if (contains(value)) result.add(value);
});
return result;
}
bool isSubsetOf(Collection<E> other) {
return new Set<E>.from(other).containsAll(this);
}
void removeAll(Collection<E> collection) {
collection.forEach(void _(E value) {
remove(value);
});
}
bool containsAll(Collection<E> collection) {
return collection.every(bool _(E value) {
return contains(value);
});
}
void forEach(void f(E element)) {
_backingMap.forEach(void _(E key, E value) {
f(key);
});
}
Set map(f(E element)) {
Set result = new Set();
_backingMap.forEach(void _(E key, E value) {
result.add(f(key));
});
return result;
}
Dynamic reduce(Dynamic initialValue,
Dynamic combine(Dynamic previousValue, E element)) {
return Collections.reduce(this, initialValue, combine);
}
Set<E> filter(bool f(E element)) {
Set<E> result = new Set<E>();
_backingMap.forEach(void _(E key, E value) {
if (f(key)) result.add(key);
});
return result;
}
bool every(bool f(E element)) {
Collection<E> keys = _backingMap.getKeys();
return keys.every(f);
}
bool some(bool f(E element)) {
Collection<E> keys = _backingMap.getKeys();
return keys.some(f);
}
bool isEmpty() {
return _backingMap.isEmpty();
}
int get length {
return _backingMap.length;
}
Iterator<E> iterator() {
return new HashSetIterator<E>(this);
}
String toString() {
return Collections.collectionToString(this);
}
// The map backing this set. The associations in this map are all
// of the form element -> element. If a value is not in the map,
// then it is not in the set.
HashMapImplementation<E, E> _backingMap;
}
class HashSetIterator<E> implements Iterator<E> {
// TODO(4504458): Replace set_ with set.
HashSetIterator(HashSetImplementation<E> set_)
: _nextValidIndex = -1,
_entries = set_._backingMap._keys {
_advance();
}
bool hasNext() {
if (_nextValidIndex >= _entries.length) return false;
if (_entries[_nextValidIndex] === HashMapImplementation._DELETED_KEY) {
// This happens in case the set was modified in the meantime.
// A modification on the set may make this iterator misbehave,
// but we should never return the sentinel.
_advance();
}
return _nextValidIndex < _entries.length;
}
E next() {
if (!hasNext()) {
throw const NoMoreElementsException();
}
E res = _entries[_nextValidIndex];
_advance();
return res;
}
void _advance() {
int length = _entries.length;
var entry;
final deletedKey = HashMapImplementation._DELETED_KEY;
do {
if (++_nextValidIndex >= length) break;
entry = _entries[_nextValidIndex];
} while ((entry === null) || (entry === deletedKey));
}
// The entries in the set. May contain null or the sentinel value.
List<E> _entries;
// The next valid index in [_entries] or the length of [entries_].
// If it is the length of [_entries], calling [hasNext] on the
// iterator will return false.
int _nextValidIndex;
}
/**
* A singleton sentinel used to represent when a key is deleted from the map.
* We can't use [: const Object() :] as a sentinel because it would end up
* canonicalized and then we cannot distinguish the deleted key from the
* canonicalized [: Object() :].
*/
class _DeletedKeySentinel {
const _DeletedKeySentinel();
}