Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
|
Siggi Cherem (dart-lang)
2011/11/18 01:28:10
I don't plan to submit this file here - there is a
| |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Hash map implementation with open addressing and quadratic probing. | 5 // Hash map implementation with open addressing and quadratic probing. |
| 6 class HashMapImplementation<K extends Hashable, V> implements HashMap<K, V> { | 6 class HashMapImplementation<K extends Hashable, V> implements HashMap<K, V> { |
| 7 | 7 |
| 8 // The [_keys] list contains the keys inserted in the map. | 8 // The [_keys] list contains the keys inserted in the map. |
| 9 // The [_keys] list must be a raw list because it | 9 // The [_keys] list must be a raw list because it |
| 10 // will contain both elements of type K, and the [_deletedKey] of type | 10 // will contain both elements of type K, and the [_DELETED_KEY] of type |
| 11 // Object. | 11 // [_DeletedKeySentinel]. |
| 12 // The alternative of declaring the [_keys] list as of type Object | 12 // The alternative of declaring the [_keys] list as of type Object |
| 13 // does not work, because the HashSetIterator constructor would fail: | 13 // does not work, because the HashSetIterator constructor would fail: |
| 14 // HashSetIterator(HashSet<E> set) | 14 // HashSetIterator(HashSet<E> set) |
| 15 // : _nextValidIndex = -1, | 15 // : _nextValidIndex = -1, |
| 16 // _entries = set_._backingMap._keys { | 16 // _entries = set_._backingMap._keys { |
| 17 // _advance(); | 17 // _advance(); |
| 18 // } | 18 // } |
| 19 // With K being type int, for example, it would fail because | 19 // With K being type int, for example, it would fail because |
| 20 // List<Object> is not assignable to type List<int> of entries. | 20 // List<Object> is not assignable to type List<int> of entries. |
| 21 List _keys; | 21 List _keys; |
| 22 | 22 |
| 23 // The values inserted in the map. For a filled entry index in this | 23 // The values inserted in the map. For a filled entry index in this |
| 24 // list, there is always the corresponding key in the [keys_] list | 24 // list, there is always the corresponding key in the [keys_] list |
| 25 // at the same entry index. | 25 // at the same entry index. |
| 26 List<V> _values; | 26 List<V> _values; |
| 27 | 27 |
| 28 // The load limit is the number of entries we allow until we double | 28 // The load limit is the number of entries we allow until we double |
| 29 // the size of the lists. | 29 // the size of the lists. |
| 30 int _loadLimit; | 30 int _loadLimit; |
| 31 | 31 |
| 32 // The current number of entries in the map. Will never be greater | 32 // The current number of entries in the map. Will never be greater |
| 33 // than [_loadLimit]. | 33 // than [_loadLimit]. |
| 34 int _numberOfEntries; | 34 int _numberOfEntries; |
| 35 | 35 |
| 36 // The current number of deleted entries in the map. | 36 // The current number of deleted entries in the map. |
| 37 int _numberOfDeleted; | 37 int _numberOfDeleted; |
| 38 | 38 |
| 39 // The sentinel when a key is deleted from the map. We cannot use static | 39 // The sentinel when a key is deleted from the map. |
| 40 // const here because we would need to allocate a "const Object()" which | 40 static final _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel(); |
|
jimhug
2011/11/18 17:49:17
Nice!
| |
| 41 // would end up canonicalized and then we cannot distinguish the deleted | |
| 42 // key from the canonicalized Object(). | |
| 43 static Object _deletedKey; | |
| 44 | 41 |
| 45 // The initial capacity of a hash map. | 42 // The initial capacity of a hash map. |
| 46 static final int _INITIAL_CAPACITY = 8; // must be power of 2 | 43 static final int _INITIAL_CAPACITY = 8; // must be power of 2 |
| 47 | 44 |
| 48 HashMapImplementation() { | 45 HashMapImplementation() { |
| 49 if (_deletedKey === null) { | |
| 50 _deletedKey = new Object(); | |
| 51 } | |
| 52 _numberOfEntries = 0; | 46 _numberOfEntries = 0; |
| 53 _numberOfDeleted = 0; | 47 _numberOfDeleted = 0; |
| 54 _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY); | 48 _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY); |
| 55 _keys = new List(_INITIAL_CAPACITY); | 49 _keys = new List(_INITIAL_CAPACITY); |
| 56 _values = new List<V>(_INITIAL_CAPACITY); | 50 _values = new List<V>(_INITIAL_CAPACITY); |
| 57 } | 51 } |
| 58 | 52 |
| 59 // See bug 5257789. | 53 // See bug 5257789. Works in the vm, fails in dartc |
| 60 factory HashMapImplementation/* <K, V> */.from(Map/* <K, V> */other) { | 54 factory HashMapImplementation/* <K, V> */.from(Map/* <K, V> */other) { |
| 61 Map/* <K, V> */ result = new HashMapImplementation/* <K, V> */(); | 55 Map/* <K, V> */ result = new HashMapImplementation/* <K, V> */(); |
| 62 other.forEach((/* K */ key, /* V */ value) { result[key] = value; }); | 56 other.forEach((/* K */ key, /* V */ value) { result[key] = value; }); |
| 63 return result; | 57 return result; |
| 64 } | 58 } |
| 65 | 59 |
| 66 static int _computeLoadLimit(int capacity) { | 60 static int _computeLoadLimit(int capacity) { |
| 67 return (capacity * 3) ~/ 4; | 61 return (capacity * 3) ~/ 4; |
| 68 } | 62 } |
| 69 | 63 |
| 70 static int _firstProbe(int hashCode, int length) { | 64 static int _firstProbe(int hashCode, int length) { |
| 71 return hashCode & (length - 1); | 65 return hashCode & (length - 1); |
| 72 } | 66 } |
| 73 | 67 |
| 74 static int _nextProbe(int currentProbe, int numberOfProbes, int length) { | 68 static int _nextProbe(int currentProbe, int numberOfProbes, int length) { |
| 75 return (currentProbe + numberOfProbes) & (length - 1); | 69 return (currentProbe + numberOfProbes) & (length - 1); |
| 76 } | 70 } |
| 77 | 71 |
| 78 int _probeForAdding(K key) { | 72 int _probeForAdding(K key) { |
| 79 int hash = _firstProbe(key.hashCode(), _keys.length); | 73 int hash = _firstProbe(key.hashCode(), _keys.length); |
| 80 int numberOfProbes = 1; | 74 int numberOfProbes = 1; |
| 81 int initialHash = hash; | 75 int initialHash = hash; |
| 82 // insertionIndex points to a slot where a key was deleted. | 76 // insertionIndex points to a slot where a key was deleted. |
| 83 int insertionIndex = -1; | 77 int insertionIndex = -1; |
| 84 while (true) { | 78 while (true) { |
| 79 // Keys can be either of type [K] or [_DeletedKeySentinel]. | |
| 85 Object existingKey = _keys[hash]; | 80 Object existingKey = _keys[hash]; |
| 86 if (existingKey === null) { | 81 if (existingKey === null) { |
| 87 // We are sure the key is not already in the set. | 82 // We are sure the key is not already in the set. |
| 88 // If the current slot is empty and we didn't find any | 83 // If the current slot is empty and we didn't find any |
| 89 // insertion slot before, return this slot. | 84 // insertion slot before, return this slot. |
| 90 if (insertionIndex < 0) return hash; | 85 if (insertionIndex < 0) return hash; |
| 91 // If we did find an insertion slot before, return it. | 86 // If we did find an insertion slot before, return it. |
| 92 return insertionIndex; | 87 return insertionIndex; |
| 93 } else if (existingKey == key) { | 88 } else if (existingKey == key) { |
| 94 // The key is already in the map. Return its slot. | 89 // The key is already in the map. Return its slot. |
| 95 return hash; | 90 return hash; |
| 96 } else if ((insertionIndex < 0) && (_deletedKey === existingKey)) { | 91 } else if ((insertionIndex < 0) && (_DELETED_KEY === existingKey)) { |
| 97 // The slot contains a deleted element. Because previous calls to this | 92 // The slot contains a deleted element. Because previous calls to this |
| 98 // method may not have had this slot deleted, we must continue iterate | 93 // method may not have had this slot deleted, we must continue iterate |
| 99 // to find if there is a slot with the given key. | 94 // to find if there is a slot with the given key. |
| 100 insertionIndex = hash; | 95 insertionIndex = hash; |
| 101 } | 96 } |
| 102 | 97 |
| 103 // We did not find an insertion slot. Look at the next one. | 98 // We did not find an insertion slot. Look at the next one. |
| 104 hash = _nextProbe(hash, numberOfProbes++, _keys.length); | 99 hash = _nextProbe(hash, numberOfProbes++, _keys.length); |
| 105 // _ensureCapacity has guaranteed the following cannot happen. | 100 // _ensureCapacity has guaranteed the following cannot happen. |
| 106 // assert(hash != initialHash); | 101 // assert(hash != initialHash); |
| 107 } | 102 } |
| 108 } | 103 } |
| 109 | 104 |
| 110 int _probeForLookup(K key) { | 105 int _probeForLookup(K key) { |
| 111 int hash = _firstProbe(key.hashCode(), _keys.length); | 106 int hash = _firstProbe(key.hashCode(), _keys.length); |
| 112 int numberOfProbes = 1; | 107 int numberOfProbes = 1; |
| 113 int initialHash = hash; | 108 int initialHash = hash; |
| 114 while (true) { | 109 while (true) { |
| 110 // Keys can be either of type [K] or [_DeletedKeySentinel]. | |
| 115 Object existingKey = _keys[hash]; | 111 Object existingKey = _keys[hash]; |
| 116 // If the slot does not contain anything (in particular, it does not | 112 // If the slot does not contain anything (in particular, it does not |
| 117 // contain a deleted key), we know the key is not in the map. | 113 // contain a deleted key), we know the key is not in the map. |
| 118 if (existingKey === null) return -1; | 114 if (existingKey === null) return -1; |
| 119 // The key is in the map, return its index. | 115 // The key is in the map, return its index. |
| 120 if (existingKey == key) return hash; | 116 if (existingKey == key) return hash; |
| 121 // Go to the next probe. | 117 // Go to the next probe. |
| 122 hash = _nextProbe(hash, numberOfProbes++, _keys.length); | 118 hash = _nextProbe(hash, numberOfProbes++, _keys.length); |
| 123 // _ensureCapacity has guaranteed the following cannot happen. | 119 // _ensureCapacity has guaranteed the following cannot happen. |
| 124 // assert(hash != initialHash); | 120 // assert(hash != initialHash); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 151 | 147 |
| 152 void _grow(int newCapacity) { | 148 void _grow(int newCapacity) { |
| 153 assert(_isPowerOfTwo(newCapacity)); | 149 assert(_isPowerOfTwo(newCapacity)); |
| 154 int capacity = _keys.length; | 150 int capacity = _keys.length; |
| 155 _loadLimit = _computeLoadLimit(newCapacity); | 151 _loadLimit = _computeLoadLimit(newCapacity); |
| 156 List oldKeys = _keys; | 152 List oldKeys = _keys; |
| 157 List<V> oldValues = _values; | 153 List<V> oldValues = _values; |
| 158 _keys = new List(newCapacity); | 154 _keys = new List(newCapacity); |
| 159 _values = new List<V>(newCapacity); | 155 _values = new List<V>(newCapacity); |
| 160 for (int i = 0; i < capacity; i++) { | 156 for (int i = 0; i < capacity; i++) { |
| 157 // Keys can be either of type [K] or [_DeletedKeySentinel]. | |
| 161 Object key = oldKeys[i]; | 158 Object key = oldKeys[i]; |
| 162 // If there is no key, we don't need to deal with the current slot. | 159 // If there is no key, we don't need to deal with the current slot. |
| 163 if (key === null || key === _deletedKey) { | 160 if (key === null || key === _DELETED_KEY) { |
| 164 continue; | 161 continue; |
| 165 } | 162 } |
| 166 V value = oldValues[i]; | 163 V value = oldValues[i]; |
| 167 // Insert the {key, value} pair in their new slot. | 164 // Insert the {key, value} pair in their new slot. |
| 168 int newIndex = _probeForAdding(key); | 165 int newIndex = _probeForAdding(key); |
| 169 _keys[newIndex] = key; | 166 _keys[newIndex] = key; |
| 170 _values[newIndex] = value; | 167 _values[newIndex] = value; |
| 171 } | 168 } |
| 172 _numberOfDeleted = 0; | 169 _numberOfDeleted = 0; |
| 173 } | 170 } |
| 174 | 171 |
| 175 void clear() { | 172 void clear() { |
| 176 _numberOfEntries = 0; | 173 _numberOfEntries = 0; |
| 177 _numberOfDeleted = 0; | 174 _numberOfDeleted = 0; |
| 178 int length = _keys.length; | 175 int length = _keys.length; |
| 179 for (int i = 0; i < length; i++) { | 176 for (int i = 0; i < length; i++) { |
| 180 _keys[i] = null; | 177 _keys[i] = null; |
| 181 _values[i] = null; | 178 _values[i] = null; |
| 182 } | 179 } |
| 183 } | 180 } |
| 184 | 181 |
| 185 void operator []=(K key, V value) { | 182 void operator []=(K key, V value) { |
| 186 _ensureCapacity(); | 183 _ensureCapacity(); |
| 187 int index = _probeForAdding(key); | 184 int index = _probeForAdding(key); |
| 188 if ((_keys[index] === null) || (_keys[index] === _deletedKey)) { | 185 if ((_keys[index] === null) || (_keys[index] === _DELETED_KEY)) { |
| 189 _numberOfEntries++; | 186 _numberOfEntries++; |
| 190 } | 187 } |
| 191 _keys[index] = key; | 188 _keys[index] = key; |
| 192 _values[index] = value; | 189 _values[index] = value; |
| 193 } | 190 } |
| 194 | 191 |
| 195 V operator [](K key) { | 192 V operator [](K key) { |
| 196 int index = _probeForLookup(key); | 193 int index = _probeForLookup(key); |
| 197 if (index < 0) return null; | 194 if (index < 0) return null; |
| 198 return _values[index]; | 195 return _values[index]; |
| 199 } | 196 } |
| 200 | 197 |
| 201 V putIfAbsent(K key, V ifAbsent()) { | 198 V putIfAbsent(K key, V ifAbsent()) { |
| 202 int index = _probeForLookup(key); | 199 int index = _probeForLookup(key); |
| 203 if (index >=0) return _values[index]; | 200 if (index >=0) return _values[index]; |
| 204 | 201 |
| 205 V value = ifAbsent(); | 202 V value = ifAbsent(); |
| 206 this[key] = value; | 203 this[key] = value; |
| 207 return value; | 204 return value; |
| 208 } | 205 } |
| 209 | 206 |
| 210 V remove(K key) { | 207 V remove(K key) { |
| 211 int index = _probeForLookup(key); | 208 int index = _probeForLookup(key); |
| 212 if (index >= 0) { | 209 if (index >= 0) { |
| 213 _numberOfEntries--; | 210 _numberOfEntries--; |
| 214 V value = _values[index]; | 211 V value = _values[index]; |
| 215 _values[index] = null; | 212 _values[index] = null; |
| 216 // Set the key to the sentinel to not break the probing chain. | 213 // Set the key to the sentinel to not break the probing chain. |
| 217 _keys[index] = _deletedKey; | 214 _keys[index] = _DELETED_KEY; |
| 218 _numberOfDeleted++; | 215 _numberOfDeleted++; |
| 219 return value; | 216 return value; |
| 220 } | 217 } |
| 221 return null; | 218 return null; |
| 222 } | 219 } |
| 223 | 220 |
| 224 bool isEmpty() { | 221 bool isEmpty() { |
| 225 return _numberOfEntries == 0; | 222 return _numberOfEntries == 0; |
| 226 } | 223 } |
| 227 | 224 |
| 228 int get length() { | 225 int get length() { |
| 229 return _numberOfEntries; | 226 return _numberOfEntries; |
| 230 } | 227 } |
| 231 | 228 |
| 232 void forEach(void f(K key, V value)) { | 229 void forEach(void f(K key, V value)) { |
| 233 int length = _keys.length; | 230 int length = _keys.length; |
| 234 for (int i = 0; i < length; i++) { | 231 for (int i = 0; i < length; i++) { |
| 235 if ((_keys[i] !== null) && (_keys[i] !== _deletedKey)) { | 232 if ((_keys[i] !== null) && (_keys[i] !== _DELETED_KEY)) { |
| 236 f(_keys[i], _values[i]); | 233 f(_keys[i], _values[i]); |
| 237 } | 234 } |
| 238 } | 235 } |
| 239 } | 236 } |
| 240 | 237 |
| 241 | 238 |
| 242 Collection<K> getKeys() { | 239 Collection<K> getKeys() { |
| 243 List<K> list = new List<K>(length); | 240 List<K> list = new List<K>(length); |
| 244 int i = 0; | 241 int i = 0; |
| 245 forEach(void _(K key, V value) { | 242 forEach(void _(K key, V value) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 257 return list; | 254 return list; |
| 258 } | 255 } |
| 259 | 256 |
| 260 bool containsKey(K key) { | 257 bool containsKey(K key) { |
| 261 return (_probeForLookup(key) != -1); | 258 return (_probeForLookup(key) != -1); |
| 262 } | 259 } |
| 263 | 260 |
| 264 bool containsValue(V value) { | 261 bool containsValue(V value) { |
| 265 int length = _values.length; | 262 int length = _values.length; |
| 266 for (int i = 0; i < length; i++) { | 263 for (int i = 0; i < length; i++) { |
| 267 if ((_keys[i] !== null) && (_keys[i] !== _deletedKey)) { | 264 if ((_keys[i] !== null) && (_keys[i] !== _DELETED_KEY)) { |
| 268 if (_values[i] == value) return true; | 265 if (_values[i] == value) return true; |
| 269 } | 266 } |
| 270 } | 267 } |
| 271 return false; | 268 return false; |
| 272 } | 269 } |
| 273 } | 270 } |
| 274 | 271 |
| 275 class HashSetImplementation<E extends Hashable> implements HashSet<E> { | 272 class HashSetImplementation<E extends Hashable> implements HashSet<E> { |
| 276 | 273 |
| 277 HashSetImplementation() { | 274 HashSetImplementation() { |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 381 | 378 |
| 382 // TODO(4504458): Replace set_ with set. | 379 // TODO(4504458): Replace set_ with set. |
| 383 HashSetIterator(HashSetImplementation<E> set_) | 380 HashSetIterator(HashSetImplementation<E> set_) |
| 384 : _nextValidIndex = -1, | 381 : _nextValidIndex = -1, |
| 385 _entries = set_._backingMap._keys { | 382 _entries = set_._backingMap._keys { |
| 386 _advance(); | 383 _advance(); |
| 387 } | 384 } |
| 388 | 385 |
| 389 bool hasNext() { | 386 bool hasNext() { |
| 390 if (_nextValidIndex >= _entries.length) return false; | 387 if (_nextValidIndex >= _entries.length) return false; |
| 391 if (_entries[_nextValidIndex] === HashMapImplementation._deletedKey) { | 388 if (_entries[_nextValidIndex] === HashMapImplementation._DELETED_KEY) { |
| 392 // This happens in case the set was modified in the meantime. | 389 // This happens in case the set was modified in the meantime. |
| 393 // A modification on the set may make this iterator misbehave, | 390 // A modification on the set may make this iterator misbehave, |
| 394 // but we should never return the sentinel. | 391 // but we should never return the sentinel. |
| 395 _advance(); | 392 _advance(); |
| 396 } | 393 } |
| 397 return _nextValidIndex < _entries.length; | 394 return _nextValidIndex < _entries.length; |
| 398 } | 395 } |
| 399 | 396 |
| 400 E next() { | 397 E next() { |
| 401 if (!hasNext()) { | 398 if (!hasNext()) { |
| 402 throw const NoMoreElementsException(); | 399 throw const NoMoreElementsException(); |
| 403 } | 400 } |
| 404 E res = _entries[_nextValidIndex]; | 401 E res = _entries[_nextValidIndex]; |
| 405 _advance(); | 402 _advance(); |
| 406 return res; | 403 return res; |
| 407 } | 404 } |
| 408 | 405 |
| 409 void _advance() { | 406 void _advance() { |
| 410 int length = _entries.length; | 407 int length = _entries.length; |
| 411 var entry; | 408 var entry; |
| 412 Object deletedKey = HashMapImplementation._deletedKey; | 409 final deletedKey = HashMapImplementation._DELETED_KEY; |
| 413 do { | 410 do { |
| 414 if (++_nextValidIndex >= length) break; | 411 if (++_nextValidIndex >= length) break; |
| 415 entry = _entries[_nextValidIndex]; | 412 entry = _entries[_nextValidIndex]; |
| 416 } while ((entry === null) || (entry === deletedKey)); | 413 } while ((entry === null) || (entry === deletedKey)); |
| 417 } | 414 } |
| 418 | 415 |
| 419 // The entries in the set. May contain null or the sentinel value. | 416 // The entries in the set. May contain null or the sentinel value. |
| 420 List<E> _entries; | 417 List<E> _entries; |
| 421 | 418 |
| 422 // The next valid index in [_entries] or the length of [entries_]. | 419 // The next valid index in [_entries] or the length of [entries_]. |
| 423 // If it is the length of [_entries], calling [hasNext] on the | 420 // If it is the length of [_entries], calling [hasNext] on the |
| 424 // iterator will return false. | 421 // iterator will return false. |
| 425 int _nextValidIndex; | 422 int _nextValidIndex; |
| 426 } | 423 } |
| 424 | |
| 425 /** | |
| 426 * A singleton sentinel used to represent when a key is deleted from the map. | |
| 427 * We can't use [: const Object() :] as a sentinel because it would end up | |
| 428 * canonicalized and then we cannot distinguish the deleted key from the | |
| 429 * canonicalized Object(). | |
| 430 */ | |
| 431 class _DeletedKeySentinel { | |
| 432 const _DeletedKeySentinel(); | |
| 433 } | |
| OLD | NEW |