Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 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, V> implements HashMap<K, V> { | 6 class HashMapImplementation<K, 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 [_DELETED_KEY] of type | 10 // will contain both elements of type K, and the [_DELETED_KEY] of type |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 71 int _probeForAdding(K key) { | 71 int _probeForAdding(K key) { |
| 72 if (key == null) throw const NullPointerException(); | 72 if (key == null) throw const NullPointerException(); |
| 73 int hash = _firstProbe(key.hashCode(), _keys.length); | 73 int hash = _firstProbe(key.hashCode(), _keys.length); |
| 74 int numberOfProbes = 1; | 74 int numberOfProbes = 1; |
| 75 int initialHash = hash; | 75 int initialHash = hash; |
| 76 // insertionIndex points to a slot where a key was deleted. | 76 // insertionIndex points to a slot where a key was deleted. |
| 77 int insertionIndex = -1; | 77 int insertionIndex = -1; |
| 78 while (true) { | 78 while (true) { |
| 79 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. | 79 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. |
| 80 Object existingKey = _keys[hash]; | 80 Object existingKey = _keys[hash]; |
| 81 if (existingKey === null) { | 81 if (existingKey == null) { |
| 82 // We are sure the key is not already in the set. | 82 // We are sure the key is not already in the set. |
| 83 // 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 |
| 84 // insertion slot before, return this slot. | 84 // insertion slot before, return this slot. |
| 85 if (insertionIndex < 0) return hash; | 85 if (insertionIndex < 0) return hash; |
| 86 // If we did find an insertion slot before, return it. | 86 // If we did find an insertion slot before, return it. |
| 87 return insertionIndex; | 87 return insertionIndex; |
| 88 } else if (existingKey == key) { | 88 } else if (existingKey == key) { |
| 89 // The key is already in the map. Return its slot. | 89 // The key is already in the map. Return its slot. |
| 90 return hash; | 90 return hash; |
| 91 } else if ((insertionIndex < 0) && (_DELETED_KEY === existingKey)) { | 91 } else if ((insertionIndex < 0) && (identical(_DELETED_KEY, existingKey))) { |
|
ahe
2012/10/22 09:05:21
_DELETED_KEY == existingKey
floitsch
2012/10/22 12:07:37
I prefer identical too.
Please make it fit on 80ch
danrubel
2012/10/22 13:35:48
Done.
| |
| 92 // The slot contains a deleted element. Because previous calls to this | 92 // The slot contains a deleted element. Because previous calls to this |
| 93 // 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 |
| 94 // to find if there is a slot with the given key. | 94 // to find if there is a slot with the given key. |
| 95 insertionIndex = hash; | 95 insertionIndex = hash; |
| 96 } | 96 } |
| 97 | 97 |
| 98 // 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. |
| 99 hash = _nextProbe(hash, numberOfProbes++, _keys.length); | 99 hash = _nextProbe(hash, numberOfProbes++, _keys.length); |
| 100 // _ensureCapacity has guaranteed the following cannot happen. | 100 // _ensureCapacity has guaranteed the following cannot happen. |
| 101 // assert(hash != initialHash); | 101 // assert(hash != initialHash); |
| 102 } | 102 } |
| 103 } | 103 } |
| 104 | 104 |
| 105 int _probeForLookup(K key) { | 105 int _probeForLookup(K key) { |
| 106 if (key == null) throw const NullPointerException(); | 106 if (key == null) throw const NullPointerException(); |
| 107 int hash = _firstProbe(key.hashCode(), _keys.length); | 107 int hash = _firstProbe(key.hashCode(), _keys.length); |
| 108 int numberOfProbes = 1; | 108 int numberOfProbes = 1; |
| 109 int initialHash = hash; | 109 int initialHash = hash; |
| 110 while (true) { | 110 while (true) { |
| 111 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. | 111 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. |
| 112 Object existingKey = _keys[hash]; | 112 Object existingKey = _keys[hash]; |
| 113 // If the slot does not contain anything (in particular, it does not | 113 // If the slot does not contain anything (in particular, it does not |
| 114 // contain a deleted key), we know the key is not in the map. | 114 // contain a deleted key), we know the key is not in the map. |
| 115 if (existingKey === null) return -1; | 115 if (existingKey == null) return -1; |
| 116 // The key is in the map, return its index. | 116 // The key is in the map, return its index. |
| 117 if (existingKey == key) return hash; | 117 if (existingKey == key) return hash; |
| 118 // Go to the next probe. | 118 // Go to the next probe. |
| 119 hash = _nextProbe(hash, numberOfProbes++, _keys.length); | 119 hash = _nextProbe(hash, numberOfProbes++, _keys.length); |
| 120 // _ensureCapacity has guaranteed the following cannot happen. | 120 // _ensureCapacity has guaranteed the following cannot happen. |
| 121 // assert(hash != initialHash); | 121 // assert(hash != initialHash); |
| 122 } | 122 } |
| 123 } | 123 } |
| 124 | 124 |
| 125 void _ensureCapacity() { | 125 void _ensureCapacity() { |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 151 int capacity = _keys.length; | 151 int capacity = _keys.length; |
| 152 _loadLimit = _computeLoadLimit(newCapacity); | 152 _loadLimit = _computeLoadLimit(newCapacity); |
| 153 List oldKeys = _keys; | 153 List oldKeys = _keys; |
| 154 List<V> oldValues = _values; | 154 List<V> oldValues = _values; |
| 155 _keys = new List(newCapacity); | 155 _keys = new List(newCapacity); |
| 156 _values = new List<V>(newCapacity); | 156 _values = new List<V>(newCapacity); |
| 157 for (int i = 0; i < capacity; i++) { | 157 for (int i = 0; i < capacity; i++) { |
| 158 // [key] can be either of type [K] or [_DeletedKeySentinel]. | 158 // [key] can be either of type [K] or [_DeletedKeySentinel]. |
| 159 Object key = oldKeys[i]; | 159 Object key = oldKeys[i]; |
| 160 // If there is no key, we don't need to deal with the current slot. | 160 // If there is no key, we don't need to deal with the current slot. |
| 161 if (key === null || key === _DELETED_KEY) { | 161 if (key == null || identical(key, _DELETED_KEY)) { |
|
ahe
2012/10/22 09:05:21
DELETED_KEY == key
floitsch
2012/10/22 12:07:37
ditto.
| |
| 162 continue; | 162 continue; |
| 163 } | 163 } |
| 164 V value = oldValues[i]; | 164 V value = oldValues[i]; |
| 165 // Insert the {key, value} pair in their new slot. | 165 // Insert the {key, value} pair in their new slot. |
| 166 int newIndex = _probeForAdding(key); | 166 int newIndex = _probeForAdding(key); |
| 167 _keys[newIndex] = key; | 167 _keys[newIndex] = key; |
| 168 _values[newIndex] = value; | 168 _values[newIndex] = value; |
| 169 } | 169 } |
| 170 _numberOfDeleted = 0; | 170 _numberOfDeleted = 0; |
| 171 } | 171 } |
| 172 | 172 |
| 173 void clear() { | 173 void clear() { |
| 174 _numberOfEntries = 0; | 174 _numberOfEntries = 0; |
| 175 _numberOfDeleted = 0; | 175 _numberOfDeleted = 0; |
| 176 int length = _keys.length; | 176 int length = _keys.length; |
| 177 for (int i = 0; i < length; i++) { | 177 for (int i = 0; i < length; i++) { |
| 178 _keys[i] = null; | 178 _keys[i] = null; |
| 179 _values[i] = null; | 179 _values[i] = null; |
| 180 } | 180 } |
| 181 } | 181 } |
| 182 | 182 |
| 183 void operator []=(K key, V value) { | 183 void operator []=(K key, V value) { |
| 184 _ensureCapacity(); | 184 _ensureCapacity(); |
| 185 int index = _probeForAdding(key); | 185 int index = _probeForAdding(key); |
| 186 if ((_keys[index] === null) || (_keys[index] === _DELETED_KEY)) { | 186 if ((_keys[index] == null) || (identical(_keys[index], _DELETED_KEY))) { |
|
ahe
2012/10/22 09:05:21
_DELETED_KEY == _keys[index]
floitsch
2012/10/22 12:07:37
ditto.
| |
| 187 _numberOfEntries++; | 187 _numberOfEntries++; |
| 188 } | 188 } |
| 189 _keys[index] = key; | 189 _keys[index] = key; |
| 190 _values[index] = value; | 190 _values[index] = value; |
| 191 } | 191 } |
| 192 | 192 |
| 193 V operator [](K key) { | 193 V operator [](K key) { |
| 194 int index = _probeForLookup(key); | 194 int index = _probeForLookup(key); |
| 195 if (index < 0) return null; | 195 if (index < 0) return null; |
| 196 return _values[index]; | 196 return _values[index]; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 224 } | 224 } |
| 225 | 225 |
| 226 int get length { | 226 int get length { |
| 227 return _numberOfEntries; | 227 return _numberOfEntries; |
| 228 } | 228 } |
| 229 | 229 |
| 230 void forEach(void f(K key, V value)) { | 230 void forEach(void f(K key, V value)) { |
| 231 int length = _keys.length; | 231 int length = _keys.length; |
| 232 for (int i = 0; i < length; i++) { | 232 for (int i = 0; i < length; i++) { |
| 233 var key = _keys[i]; | 233 var key = _keys[i]; |
| 234 if ((key !== null) && (key !== _DELETED_KEY)) { | 234 if ((key != null) && (!identical(key, _DELETED_KEY))) { |
|
ahe
2012/10/22 09:05:21
_DELETED_KEY == key
floitsch
2012/10/22 12:07:37
ditto.
| |
| 235 f(key, _values[i]); | 235 f(key, _values[i]); |
| 236 } | 236 } |
| 237 } | 237 } |
| 238 } | 238 } |
| 239 | 239 |
| 240 | 240 |
| 241 Collection<K> getKeys() { | 241 Collection<K> getKeys() { |
| 242 List<K> list = new List<K>(length); | 242 List<K> list = new List<K>(length); |
| 243 int i = 0; | 243 int i = 0; |
| 244 forEach(void _(K key, V value) { | 244 forEach(void _(K key, V value) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 257 } | 257 } |
| 258 | 258 |
| 259 bool containsKey(K key) { | 259 bool containsKey(K key) { |
| 260 return (_probeForLookup(key) != -1); | 260 return (_probeForLookup(key) != -1); |
| 261 } | 261 } |
| 262 | 262 |
| 263 bool containsValue(V value) { | 263 bool containsValue(V value) { |
| 264 int length = _values.length; | 264 int length = _values.length; |
| 265 for (int i = 0; i < length; i++) { | 265 for (int i = 0; i < length; i++) { |
| 266 var key = _keys[i]; | 266 var key = _keys[i]; |
| 267 if ((key !== null) && (key !== _DELETED_KEY)) { | 267 if ((key != null) && (!identical(key, _DELETED_KEY))) { |
|
ahe
2012/10/22 09:05:21
Ditto
floitsch
2012/10/22 12:07:37
ditto.
| |
| 268 if (_values[i] == value) return true; | 268 if (_values[i] == value) return true; |
| 269 } | 269 } |
| 270 } | 270 } |
| 271 return false; | 271 return false; |
| 272 } | 272 } |
| 273 | 273 |
| 274 String toString() { | 274 String toString() { |
| 275 return Maps.mapToString(this); | 275 return Maps.mapToString(this); |
| 276 } | 276 } |
| 277 } | 277 } |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 401 | 401 |
| 402 // TODO(4504458): Replace set_ with set. | 402 // TODO(4504458): Replace set_ with set. |
| 403 HashSetIterator(HashSetImplementation<E> set_) | 403 HashSetIterator(HashSetImplementation<E> set_) |
| 404 : _nextValidIndex = -1, | 404 : _nextValidIndex = -1, |
| 405 _entries = set_._backingMap._keys { | 405 _entries = set_._backingMap._keys { |
| 406 _advance(); | 406 _advance(); |
| 407 } | 407 } |
| 408 | 408 |
| 409 bool hasNext() { | 409 bool hasNext() { |
| 410 if (_nextValidIndex >= _entries.length) return false; | 410 if (_nextValidIndex >= _entries.length) return false; |
| 411 if (_entries[_nextValidIndex] === HashMapImplementation._DELETED_KEY) { | 411 if (identical(_entries[_nextValidIndex], HashMapImplementation._DELETED_KEY) ) { |
|
ahe
2012/10/22 09:05:21
HashMapImplementation._DELETED_KEY == identical(_e
floitsch
2012/10/22 12:07:37
ditto. Please make it fit on 80chars.
danrubel
2012/10/22 13:35:48
Done.
| |
| 412 // This happens in case the set was modified in the meantime. | 412 // This happens in case the set was modified in the meantime. |
| 413 // A modification on the set may make this iterator misbehave, | 413 // A modification on the set may make this iterator misbehave, |
| 414 // but we should never return the sentinel. | 414 // but we should never return the sentinel. |
| 415 _advance(); | 415 _advance(); |
| 416 } | 416 } |
| 417 return _nextValidIndex < _entries.length; | 417 return _nextValidIndex < _entries.length; |
| 418 } | 418 } |
| 419 | 419 |
| 420 E next() { | 420 E next() { |
| 421 if (!hasNext()) { | 421 if (!hasNext()) { |
| 422 throw const NoMoreElementsException(); | 422 throw const NoMoreElementsException(); |
| 423 } | 423 } |
| 424 E res = _entries[_nextValidIndex]; | 424 E res = _entries[_nextValidIndex]; |
| 425 _advance(); | 425 _advance(); |
| 426 return res; | 426 return res; |
| 427 } | 427 } |
| 428 | 428 |
| 429 void _advance() { | 429 void _advance() { |
| 430 int length = _entries.length; | 430 int length = _entries.length; |
| 431 var entry; | 431 var entry; |
| 432 final deletedKey = HashMapImplementation._DELETED_KEY; | 432 final deletedKey = HashMapImplementation._DELETED_KEY; |
| 433 do { | 433 do { |
| 434 if (++_nextValidIndex >= length) break; | 434 if (++_nextValidIndex >= length) break; |
| 435 entry = _entries[_nextValidIndex]; | 435 entry = _entries[_nextValidIndex]; |
| 436 } while ((entry === null) || (entry === deletedKey)); | 436 } while ((entry == null) || (identical(entry, deletedKey))); |
|
ahe
2012/10/22 09:05:21
deletedKey == entry
floitsch
2012/10/22 12:07:37
ditto.
| |
| 437 } | 437 } |
| 438 | 438 |
| 439 // The entries in the set. May contain null or the sentinel value. | 439 // The entries in the set. May contain null or the sentinel value. |
| 440 List<E> _entries; | 440 List<E> _entries; |
| 441 | 441 |
| 442 // The next valid index in [_entries] or the length of [entries_]. | 442 // The next valid index in [_entries] or the length of [entries_]. |
| 443 // If it is the length of [_entries], calling [hasNext] on the | 443 // If it is the length of [_entries], calling [hasNext] on the |
| 444 // iterator will return false. | 444 // iterator will return false. |
| 445 int _nextValidIndex; | 445 int _nextValidIndex; |
| 446 } | 446 } |
| 447 | 447 |
| 448 /** | 448 /** |
| 449 * A singleton sentinel used to represent when a key is deleted from the map. | 449 * A singleton sentinel used to represent when a key is deleted from the map. |
| 450 * We can't use [: const Object() :] as a sentinel because it would end up | 450 * We can't use [: const Object() :] as a sentinel because it would end up |
| 451 * canonicalized and then we cannot distinguish the deleted key from the | 451 * canonicalized and then we cannot distinguish the deleted key from the |
| 452 * canonicalized [: Object() :]. | 452 * canonicalized [: Object() :]. |
| 453 */ | 453 */ |
| 454 class _DeletedKeySentinel { | 454 class _DeletedKeySentinel { |
| 455 const _DeletedKeySentinel(); | 455 const _DeletedKeySentinel(); |
| 456 } | 456 } |
| OLD | NEW |