Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 patch class HashMap<K, V> { | 5 patch class HashMap<K, V> { |
|
Søren Gjesse
2013/08/14 07:45:07
Base class for HashMap and LinkedHashMap with comm
Lasse Reichstein Nielsen
2013/08/14 08:38:34
I would love to, but can I do that in a patch file
| |
| 6 final _HashMapTable<K, V> _hashTable = new _HashMapTable<K, V>(); | 6 static const int _INITIAL_CAPACITY = 8; |
| 7 | 7 static const int _MODIFICATION_COUNT_MASK = 0x3fffffff; |
| 8 /* patch */ HashMap() { | 8 |
| 9 _hashTable._container = this; | 9 int _elementCount = 0; |
| 10 } | 10 List<_HashMapEntry> _buckets = new List(_INITIAL_CAPACITY); |
| 11 int _modificationCount = 0; | |
| 12 | |
| 13 /* patch */ HashMap(); | |
| 14 | |
| 15 /* patch */ int get length => _elementCount; | |
| 16 /* patch */ bool get isEmpty => _elementCount == 0; | |
| 17 /* patch */ bool get isNotEmpty => _elementCount != 0; | |
| 18 | |
| 19 /* patch */ Iterable<K> get keys => new _HashMapKeyIterable<K>(this); | |
| 20 /* patch */ Iterable<V> get values => new _HashMapValueIterable<V>(this); | |
| 11 | 21 |
| 12 /* patch */ bool containsKey(Object key) { | 22 /* patch */ bool containsKey(Object key) { |
|
Søren Gjesse
2013/08/14 07:45:07
Maybe have a method (_bucketHead?) for
int hashCo
Lasse Reichstein Nielsen
2013/08/14 08:38:34
In some cases the index and hashCode are reused (a
| |
| 13 return _hashTable._get(key) >= 0; | 23 int hashCode = key.hashCode; |
| 24 List buckets = _buckets; | |
|
Søren Gjesse
2013/08/14 07:45:07
Why the local buckets in all methods?
Lasse Reichstein Nielsen
2013/08/14 08:38:34
Probably an attempt to help the compiler recognize
| |
| 25 int index = hashCode & (buckets.length - 1); | |
| 26 _HashMapEntry entry = buckets[index]; | |
| 27 while (entry != null) { | |
| 28 if (hashCode == entry.hashCode && entry.key == key) return true; | |
| 29 entry = entry.next; | |
| 30 } | |
| 31 return false; | |
| 14 } | 32 } |
| 15 | 33 |
| 16 /* patch */ bool containsValue(Object value) { | 34 /* patch */ bool containsValue(Object value) { |
| 17 List table = _hashTable._table; | 35 List buckets = _buckets; |
| 18 int entrySize = _hashTable._entrySize; | 36 int length = buckets.length; |
| 19 for (int offset = 0; offset < table.length; offset += entrySize) { | 37 for (int i = 0; i < length; i++) { |
| 20 if (!_hashTable._isFree(table[offset]) && | 38 _HashMapEntry entry = buckets[i]; |
| 21 _hashTable._value(offset) == value) { | 39 while (entry != null) { |
| 22 return true; | 40 if (entry.value == value) return true; |
| 41 entry = entry.next; | |
| 23 } | 42 } |
| 24 } | 43 } |
| 25 return false; | 44 return false; |
| 26 } | 45 } |
| 27 | 46 |
| 47 /* patch */ V operator[](Object key) { | |
| 48 int hashCode = key.hashCode; | |
| 49 List buckets = _buckets; | |
| 50 int index = hashCode & (buckets.length - 1); | |
| 51 _HashMapEntry entry = buckets[index]; | |
| 52 while (entry != null) { | |
| 53 if (hashCode == entry.hashCode && entry.key == key) { | |
| 54 return entry.value; | |
| 55 } | |
| 56 entry = entry.next; | |
| 57 } | |
| 58 return null; | |
| 59 } | |
| 60 | |
| 61 /* patch */ void operator []=(K key, V value) { | |
| 62 int hashCode = key.hashCode; | |
| 63 List buckets = _buckets; | |
| 64 int length = buckets.length; | |
| 65 int index = hashCode & (length - 1); | |
| 66 _HashMapEntry entry = buckets[index]; | |
| 67 while (entry != null) { | |
| 68 if (hashCode == entry.hashCode && entry.key == key) { | |
| 69 entry.value = value; | |
| 70 return; | |
| 71 } | |
| 72 entry = entry.next; | |
| 73 } | |
| 74 _addEntry(buckets, index, length, key, value, hashCode); | |
| 75 } | |
| 76 | |
| 77 /* patch */ V putIfAbsent(K key, V ifAbsent()) { | |
| 78 int hashCode = key.hashCode; | |
| 79 List buckets = _buckets; | |
| 80 int length = buckets.length; | |
| 81 int index = hashCode & (length - 1); | |
| 82 _HashMapEntry entry = buckets[index]; | |
| 83 while (entry != null) { | |
| 84 if (hashCode == entry.hashCode && entry.key == key) { | |
| 85 return entry.value; | |
| 86 } | |
| 87 entry = entry.next; | |
| 88 } | |
| 89 int stamp = _modificationCount; | |
| 90 V value = ifAbsent(); | |
| 91 if (stamp == _modificationCount) { | |
| 92 _addEntry(buckets, index, length, key, value, hashCode); | |
| 93 } else { | |
| 94 this[key] = value; | |
| 95 } | |
| 96 return value; | |
| 97 } | |
| 98 | |
| 28 /* patch */ void addAll(Map<K, V> other) { | 99 /* patch */ void addAll(Map<K, V> other) { |
| 29 other.forEach((K key, V value) { | 100 other.forEach((K key, V value) { |
| 30 int offset = _hashTable._put(key); | 101 this[key] = value; |
| 31 _hashTable._setValue(offset, value); | |
| 32 _hashTable._checkCapacity(); | |
| 33 }); | 102 }); |
| 34 } | 103 } |
| 35 | 104 |
| 36 /* patch */ V operator [](Object key) { | 105 /* patch */ void forEach(void action(K key, V value)) { |
| 37 int offset = _hashTable._get(key); | 106 int stamp = _modificationCount; |
| 38 if (offset >= 0) return _hashTable._value(offset); | 107 List buckets = _buckets; |
| 108 int length = buckets.length; | |
| 109 for (int i = 0; i < length; i++) { | |
| 110 _HashMapEntry entry = buckets[i]; | |
| 111 while (entry != null) { | |
| 112 action(entry.key, entry.value); | |
| 113 if (stamp != _modificationCount) { | |
| 114 throw new ConcurrentModificationError(this); | |
| 115 } | |
| 116 entry = entry.next; | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 /* patch */ V remove(Object key) { | |
| 122 int hashCode = key.hashCode; | |
| 123 List buckets = _buckets; | |
| 124 int index = hashCode & (buckets.length - 1); | |
| 125 _HashMapEntry entry = buckets[index]; | |
| 126 _HashMapEntry previous = null; | |
| 127 while (entry != null) { | |
| 128 _HashMapEntry next = entry.next; | |
| 129 if (hashCode == entry.hashCode && entry.key == key) { | |
| 130 if (previous == null) { | |
| 131 buckets[index] = next; | |
| 132 } else { | |
| 133 previous.next = next; | |
| 134 } | |
| 135 _elementCount--; | |
| 136 _modificationCount = | |
| 137 (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; | |
| 138 return entry.value; | |
| 139 } | |
| 140 previous = entry; | |
| 141 entry = next; | |
| 142 } | |
| 39 return null; | 143 return null; |
| 40 } | 144 } |
| 41 | 145 |
| 42 /* patch */ void operator []=(K key, V value) { | |
| 43 int offset = _hashTable._put(key); | |
| 44 _hashTable._setValue(offset, value); | |
| 45 _hashTable._checkCapacity(); | |
| 46 } | |
| 47 | |
| 48 /* patch */ V putIfAbsent(K key, V ifAbsent()) { | |
| 49 int offset = _hashTable._probeForAdd(_hashTable._hashCodeOf(key), key); | |
| 50 Object entry = _hashTable._table[offset]; | |
| 51 if (!_hashTable._isFree(entry)) { | |
| 52 return _hashTable._value(offset); | |
| 53 } | |
| 54 int modificationCount = _hashTable._modificationCount; | |
| 55 V value = ifAbsent(); | |
| 56 if (modificationCount == _hashTable._modificationCount) { | |
| 57 _hashTable._setKey(offset, key); | |
| 58 _hashTable._setValue(offset, value); | |
| 59 if (entry == null) { | |
| 60 _hashTable._entryCount++; | |
| 61 _hashTable._checkCapacity(); | |
| 62 } else { | |
| 63 assert(identical(entry, _TOMBSTONE)); | |
| 64 _hashTable._deletedCount--; | |
| 65 } | |
| 66 _hashTable._recordModification(); | |
| 67 } else { | |
| 68 // The table might have changed, so we can't trust [offset] any more. | |
| 69 // Do another lookup before setting the value. | |
| 70 offset = _hashTable._put(key); | |
| 71 _hashTable._setValue(offset, value); | |
| 72 _hashTable._checkCapacity(); | |
| 73 } | |
| 74 return value; | |
| 75 } | |
| 76 | |
| 77 /* patch */ V remove(Object key) { | |
| 78 int offset = _hashTable._remove(key); | |
| 79 if (offset < 0) return null; | |
| 80 V oldValue = _hashTable._value(offset); | |
| 81 _hashTable._setValue(offset, null); | |
| 82 _hashTable._checkCapacity(); | |
| 83 return oldValue; | |
| 84 } | |
| 85 | |
| 86 /* patch */ void clear() { | 146 /* patch */ void clear() { |
| 87 _hashTable._clear(); | 147 _elementCount = 0; |
| 88 } | 148 _buckets = new List(_INITIAL_CAPACITY); |
| 89 | 149 _modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
|
Søren Gjesse
2013/08/14 07:45:07
method for updating the modification count (duplic
Lasse Reichstein Nielsen
2013/08/14 08:38:34
I guess it's inlined for performance (because we c
| |
| 90 /* patch */ void forEach(void action(K key, V value)) { | 150 } |
| 91 int modificationCount = _hashTable._modificationCount; | 151 |
| 92 List table = _hashTable._table; | 152 void _addEntry(List buckets, int index, int length, |
| 93 int entrySize = _hashTable._entrySize; | 153 K key, V value, int hashCode) { |
| 94 for (int offset = 0; offset < table.length; offset += entrySize) { | 154 _HashMapEntry entry = |
| 95 Object entry = table[offset]; | 155 new _HashMapEntry(key, value, hashCode, buckets[index]); |
| 96 if (!_hashTable._isFree(entry)) { | 156 buckets[index] = entry; |
| 97 K key = identical(entry, _NULL) ? null : entry; | 157 int newElements = _elementCount + 1; |
|
Søren Gjesse
2013/08/14 07:45:07
Why the newElements local?
Lasse Reichstein Nielsen
2013/08/14 08:38:34
Probably for optimization, instead of risking read
| |
| 98 V value = _hashTable._value(offset); | 158 _elementCount = newElements; |
| 99 action(key, value); | 159 // If we end up with more than 75% non-empty entries, we |
| 100 _hashTable._checkModification(modificationCount); | 160 // resize the backing store. |
| 101 } | 161 if ((newElements << 2) > ((length << 1) + length)) _resize(); |
| 102 } | 162 _modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
| 103 } | 163 } |
| 104 | 164 |
| 105 /* patch */ Iterable<K> get keys => new _HashTableKeyIterable<K>(_hashTable); | 165 void _resize() { |
| 106 /* patch */ Iterable<V> get values => | 166 List oldBuckets = _buckets; |
| 107 new _HashTableValueIterable<V>(_hashTable, _HashMapTable._VALUE_INDEX); | 167 int oldLength = oldBuckets.length; |
| 108 | 168 int newLength = oldLength << 1; |
| 109 /* patch */ int get length => _hashTable._elementCount; | 169 List newBuckets = new List(newLength); |
| 110 | 170 for (int i = 0; i < oldLength; i++) { |
| 111 /* patch */ bool get isEmpty => _hashTable._elementCount == 0; | 171 _HashMapEntry entry = oldBuckets[i]; |
| 112 | 172 while (entry != null) { |
| 113 /* patch */ bool get isNotEmpty => !isEmpty; | 173 _HashMapEntry next = entry.next; |
| 174 int hashCode = entry.hashCode; | |
| 175 int index = hashCode & (newLength - 1); | |
| 176 entry.next = newBuckets[index]; | |
| 177 newBuckets[index] = entry; | |
| 178 entry = next; | |
| 179 } | |
| 180 } | |
| 181 _buckets = newBuckets; | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 class _HashMapEntry { | |
| 186 final key; | |
| 187 var value; | |
| 188 final int hashCode; | |
| 189 _HashMapEntry next; | |
| 190 _HashMapEntry(this.key, this.value, this.hashCode, this.next); | |
| 191 } | |
| 192 | |
| 193 abstract class _HashMapIterable<E> extends IterableBase<E> { | |
| 194 final HashMap _map; | |
| 195 _HashMapIterable(this._map); | |
| 196 int get length => _map.length; | |
| 197 bool get isEmpty => _map.isEmpty; | |
| 198 bool get isNotEmpty => _map.isNotEmpty; | |
| 199 } | |
| 200 | |
| 201 class _HashMapKeyIterable<K> extends _HashMapIterable<K> { | |
| 202 _HashMapKeyIterable(HashMap map) : super(map); | |
| 203 Iterator<K> get iterator => new _HashMapKeyIterator<K>(_map); | |
| 204 bool contains(K key) => _map.containsKey(key); | |
| 205 void forEach(void action(K key)) { | |
| 206 _map.forEach((K key, _) { | |
| 207 action(key); | |
| 208 }); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 class _HashMapValueIterable<V> extends _HashMapIterable<V> { | |
| 213 _HashMapValueIterable(HashMap map) : super(map); | |
| 214 Iterator<V> get iterator => new _HashMapValueIterator<V>(_map); | |
| 215 bool contains(V value) => _map.containsValue(value); | |
| 216 void forEach(void action(V value)) { | |
| 217 _map.forEach((_, V value) { | |
| 218 action(value); | |
| 219 }); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 abstract class _HashMapIterator<E> implements Iterator<E> { | |
| 224 final HashMap _map; | |
| 225 final int _stamp; | |
| 226 | |
| 227 int _index = 0; | |
| 228 _HashMapEntry _entry; | |
| 229 | |
| 230 _HashMapIterator(HashMap map) | |
| 231 : _map = map, _stamp = map._modificationCount; | |
| 232 | |
| 233 bool moveNext() { | |
| 234 if (_stamp != _map._modificationCount) { | |
| 235 throw new ConcurrentModificationError(_map); | |
| 236 } | |
| 237 _HashMapEntry entry = _entry; | |
| 238 if (entry != null) { | |
| 239 _HashMapEntry next = entry.next; | |
| 240 if (next != null) { | |
| 241 _entry = next; | |
| 242 return true; | |
| 243 } | |
| 244 _entry = null; | |
| 245 } | |
| 246 List buckets = _map._buckets; | |
| 247 int length = buckets.length; | |
| 248 for (int i = _index; i < length; i++) { | |
| 249 entry = buckets[i]; | |
| 250 if (entry != null) { | |
| 251 _index = i + 1; | |
| 252 _entry = entry; | |
| 253 return true; | |
| 254 } | |
| 255 } | |
| 256 _index = length; | |
| 257 return false; | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 class _HashMapKeyIterator<K> extends _HashMapIterator<K> { | |
| 262 _HashMapKeyIterator(HashMap map) : super(map); | |
| 263 K get current { | |
| 264 _HashMapEntry entry = _entry; | |
| 265 return (entry == null) ? null : entry.key; | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 class _HashMapValueIterator<V> extends _HashMapIterator<V> { | |
| 270 _HashMapValueIterator(HashMap map) : super(map); | |
| 271 V get current { | |
| 272 _HashMapEntry entry = _entry; | |
| 273 return (entry == null) ? null : entry.value; | |
| 274 } | |
| 114 } | 275 } |
| 115 | 276 |
| 116 patch class HashSet<E> { | 277 patch class HashSet<E> { |
| 117 static const int _INITIAL_CAPACITY = 8; | 278 static const int _INITIAL_CAPACITY = 8; |
| 118 final _HashTable<E> _table; | 279 final _HashTable<E> _table; |
| 119 | 280 |
| 120 /* patch */ HashSet() : _table = new _HashTable(_INITIAL_CAPACITY) { | 281 /* patch */ HashSet() : _table = new _HashTable(_INITIAL_CAPACITY) { |
| 121 _table._container = this; | 282 _table._container = this; |
| 122 } | 283 } |
| 123 | 284 |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 | 347 |
| 187 /* patch */ void retainWhere(bool test(E element)) { | 348 /* patch */ void retainWhere(bool test(E element)) { |
| 188 _filterWhere(test, false); | 349 _filterWhere(test, false); |
| 189 } | 350 } |
| 190 | 351 |
| 191 /* patch */ void clear() { | 352 /* patch */ void clear() { |
| 192 _table._clear(); | 353 _table._clear(); |
| 193 } | 354 } |
| 194 } | 355 } |
| 195 | 356 |
| 357 class _LinkedHashMapEntry extends _HashMapEntry { | |
| 358 var _nextEntry; | |
|
Søren Gjesse
2013/08/14 07:45:07
_LinkedHashMapEntry instead of var here?
Lasse Reichstein Nielsen
2013/08/14 08:38:34
No, it may also point to LinkedHashMap.
I can't pu
| |
| 359 var _previousEntry; | |
| 360 _LinkedHashMapEntry(key, value, int hashCode, _LinkedHashMapEntry next, | |
| 361 this._previousEntry, this._nextEntry) | |
| 362 : super(key, value, hashCode, next) { | |
| 363 _previousEntry._nextEntry = this; | |
| 364 _nextEntry._previousEntry = this; | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 class _LinkedHashMapKeyIterable<K> extends IterableBase<K> { | |
| 369 LinkedHashMap<K, dynamic> _map; | |
| 370 _LinkedHashMapKeyIterable(this._map); | |
| 371 Iterator<K> get iterator => new _LinkedHashMapKeyIterator<K>(_map); | |
| 372 bool contains(K key) => _map.containsKey(key); | |
| 373 bool get isEmpty => _map.isEmpty; | |
| 374 bool get isNotEmpty => _map.isNotEmpty; | |
| 375 int get length => _map.length; | |
| 376 } | |
| 377 | |
| 378 class _LinkedHashMapValueIterable<V> extends IterableBase<V> { | |
| 379 LinkedHashMap<dynamic, V> _map; | |
| 380 _LinkedHashMapValueIterable(this._map); | |
| 381 Iterator<K> get iterator => new _LinkedHashMapValueIterator<V>(_map); | |
| 382 bool contains(V value) => _map.containsValue(value); | |
| 383 bool get isEmpty => _map.isEmpty; | |
| 384 bool get isNotEmpty => _map.isNotEmpty; | |
| 385 int get length => _map.length; | |
| 386 } | |
| 387 | |
| 388 abstract class _LinkedHashMapIterator<T> implements Iterator<T> { | |
| 389 final _LinkedHashMap _map; | |
| 390 var _next; | |
| 391 T _current; | |
| 392 int _modificationCount; | |
| 393 _LinkedHashMapIterator(_LinkedHashMap map) | |
| 394 : _map = map, | |
| 395 _current = null, | |
| 396 _next = map._nextEntry, | |
| 397 _modificationCount = map._modificationCount; | |
| 398 | |
| 399 bool moveNext() { | |
| 400 if (_modificationCount != _map._modificationCount) { | |
| 401 throw new ConcurrentModificationError(_map); | |
| 402 } | |
| 403 if (identical(_map, _next)) { | |
| 404 _current = null; | |
| 405 return false; | |
| 406 } | |
| 407 _LinkedHashMapEntry entry = _next; | |
| 408 _next = entry._nextEntry; | |
| 409 _current = _getValue(entry); | |
| 410 return true; | |
| 411 } | |
| 412 | |
| 413 T _getValue(_LinkedHashMapEntry entry); | |
| 414 | |
| 415 T get current => _current; | |
| 416 } | |
| 417 | |
| 418 class _LinkedHashMapKeyIterator<K> extends _LinkedHashMapIterator<K> { | |
| 419 _LinkedHashMapKeyIterator(_LinkedHashMap map) : super(map); | |
| 420 K _getValue(_LinkedHashMapEntry entry) => entry.key; | |
| 421 } | |
| 422 | |
| 423 class _LinkedHashMapValueIterator<V> extends _LinkedHashMapIterator<V> { | |
| 424 _LinkedHashMapValueIterator(_LinkedHashMap map) : super(map); | |
| 425 V _getValue(_LinkedHashMapEntry entry) => entry.value; | |
| 426 } | |
| 427 | |
| 428 | |
| 196 /** | 429 /** |
| 197 * A hash-based map that iterates keys and values in key insertion order. | 430 * A hash-based map that iterates keys and values in key insertion order. |
| 198 */ | 431 */ |
| 199 patch class LinkedHashMap<K, V> { | 432 patch class LinkedHashMap<K, V> { |
| 200 final _LinkedHashMapTable _hashTable; | 433 static const int _INITIAL_CAPACITY = 8; |
| 201 | 434 static const int _MODIFICATION_COUNT_MASK = 0x3fffffff; |
| 202 /* patch */ LinkedHashMap() : _hashTable = new _LinkedHashMapTable<K, V>() { | 435 |
| 203 _hashTable._container = this; | 436 int _elementCount = 0; |
| 204 } | 437 List<_HashMapEntry> _buckets = new List(_INITIAL_CAPACITY); |
| 438 int _modificationCount = 0; | |
| 439 | |
| 440 var _nextEntry; | |
|
Søren Gjesse
2013/08/14 07:45:07
_LinkedHashMapEntry instead of var here?
Søren Gjesse
2013/08/14 07:45:07
_head and _tail instead of _nextEntry and _previou
Lasse Reichstein Nielsen
2013/08/14 08:38:34
It may also point to LinkedHashMap.
Lasse Reichstein Nielsen
2013/08/14 08:38:34
They have the same name as the fields on _LinkedHa
| |
| 441 var _previousEntry; | |
| 442 | |
| 443 /* patch */ LinkedHashMap() { | |
| 444 _nextEntry = _previousEntry = this; | |
|
Søren Gjesse
2013/08/14 07:45:07
Now I see the reason for var and the naming above.
Lasse Reichstein Nielsen
2013/08/14 08:38:34
Ack, will comment.
| |
| 445 } | |
| 446 | |
| 447 /* patch */ int get length => _elementCount; | |
| 448 /* patch */ bool get isEmpty => _elementCount == 0; | |
| 449 /* patch */ bool get isNotEmpty => _elementCount != 0; | |
| 450 | |
| 451 /* patch */ Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this); | |
| 452 /* patch */ Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this) ; | |
| 205 | 453 |
| 206 /* patch */ bool containsKey(Object key) { | 454 /* patch */ bool containsKey(Object key) { |
| 207 return _hashTable._get(key) >= 0; | 455 int hashCode = key.hashCode; |
| 456 List buckets = _buckets; | |
| 457 int index = hashCode & (buckets.length - 1); | |
| 458 _HashMapEntry entry = buckets[index]; | |
| 459 while (entry != null) { | |
| 460 if (hashCode == entry.hashCode && entry.key == key) return true; | |
| 461 entry = entry.next; | |
| 462 } | |
| 463 return false; | |
| 208 } | 464 } |
| 209 | 465 |
| 210 /* patch */ bool containsValue(Object value) { | 466 /* patch */ bool containsValue(Object value) { |
| 211 int modificationCount = _hashTable._modificationCount; | 467 var cursor = _nextEntry; |
| 212 for (int offset = _hashTable._next(_LinkedHashTable._HEAD_OFFSET); | 468 int modificationCount = _modificationCount; |
| 213 offset != _LinkedHashTable._HEAD_OFFSET; | 469 while (!identical(cursor, this)) { |
| 214 offset = _hashTable._next(offset)) { | 470 _HashMapEntry entry = cursor; |
| 215 if (_hashTable._value(offset) == value) { | 471 if (entry.value == value) return true; |
| 216 return true; | 472 cursor = cursor._nextEntry; |
| 217 } | |
| 218 // The == call may modify the table. | |
| 219 _hashTable._checkModification(modificationCount); | |
| 220 } | 473 } |
| 221 return false; | 474 return false; |
| 222 } | 475 } |
| 223 | 476 |
| 477 /* patch */ V operator[](Object key) { | |
| 478 int hashCode = key.hashCode; | |
| 479 List buckets = _buckets; | |
| 480 int index = hashCode & (buckets.length - 1); | |
| 481 _HashMapEntry entry = buckets[index]; | |
| 482 while (entry != null) { | |
| 483 if (hashCode == entry.hashCode && entry.key == key) { | |
| 484 return entry.value; | |
| 485 } | |
| 486 entry = entry.next; | |
| 487 } | |
| 488 return null; | |
| 489 } | |
| 490 | |
| 491 /* patch */ void operator []=(K key, V value) { | |
| 492 int hashCode = key.hashCode; | |
| 493 List buckets = _buckets; | |
| 494 int length = buckets.length; | |
| 495 int index = hashCode & (length - 1); | |
| 496 _HashMapEntry entry = buckets[index]; | |
| 497 while (entry != null) { | |
| 498 if (hashCode == entry.hashCode && entry.key == key) { | |
| 499 entry.value = value; | |
| 500 return; | |
| 501 } | |
| 502 entry = entry.next; | |
| 503 } | |
| 504 _addEntry(buckets, index, length, key, value, hashCode); | |
| 505 } | |
| 506 | |
| 507 /* patch */ V putIfAbsent(K key, V ifAbsent()) { | |
| 508 int hashCode = key.hashCode; | |
| 509 List buckets = _buckets; | |
| 510 int length = buckets.length; | |
| 511 int index = hashCode & (length - 1); | |
| 512 _HashMapEntry entry = buckets[index]; | |
| 513 while (entry != null) { | |
| 514 if (hashCode == entry.hashCode && entry.key == key) { | |
| 515 return entry.value; | |
| 516 } | |
| 517 entry = entry.next; | |
| 518 } | |
| 519 int stamp = _modificationCount; | |
| 520 V value = ifAbsent(); | |
| 521 if (stamp == _modificationCount) { | |
| 522 _addEntry(buckets, index, length, key, value, hashCode); | |
| 523 } else { | |
| 524 this[key] = value; | |
| 525 } | |
| 526 return value; | |
| 527 } | |
| 528 | |
| 224 /* patch */ void addAll(Map<K, V> other) { | 529 /* patch */ void addAll(Map<K, V> other) { |
| 225 other.forEach((K key, V value) { | 530 other.forEach((K key, V value) { |
| 226 int offset = _hashTable._put(key); | 531 this[key] = value; |
| 227 _hashTable._setValue(offset, value); | |
| 228 _hashTable._checkCapacity(); | |
| 229 }); | 532 }); |
| 230 } | 533 } |
| 231 | 534 |
| 232 /* patch */ V operator [](Object key) { | 535 /* patch */ void forEach(void action(K key, V value)) { |
| 233 int offset = _hashTable._get(key); | 536 int stamp = _modificationCount; |
| 234 if (offset >= 0) return _hashTable._value(offset); | 537 var cursor = _nextEntry; |
| 538 while (!identical(cursor, this)) { | |
| 539 _HashMapEntry entry = cursor; | |
| 540 action(entry.key, entry.value); | |
| 541 if (stamp != _modificationCount) { | |
| 542 throw new ConcurrentModificationError(this); | |
| 543 } | |
| 544 cursor = cursor._nextEntry; | |
| 545 } | |
| 546 } | |
| 547 | |
| 548 /* patch */ V remove(Object key) { | |
| 549 int hashCode = key.hashCode; | |
| 550 List buckets = _buckets; | |
| 551 int index = hashCode & (buckets.length - 1); | |
| 552 _LinkedHashMapEntry entry = buckets[index]; | |
| 553 _HashMapEntry previous = null; | |
| 554 while (entry != null) { | |
| 555 _HashMapEntry next = entry.next; | |
| 556 if (hashCode == entry.hashCode && entry.key == key) { | |
| 557 if (previous == null) { | |
| 558 buckets[index] = next; | |
| 559 } else { | |
| 560 previous.next = next; | |
| 561 } | |
| 562 entry._previousEntry._nextEntry = entry._nextEntry; | |
| 563 entry._nextEntry._previousEntry = entry._previousEntry; | |
| 564 entry._nextEntry = entry._previousEntry = null; | |
| 565 _elementCount--; | |
| 566 _modificationCount = | |
| 567 (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; | |
| 568 return entry.value; | |
| 569 } | |
| 570 previous = entry; | |
| 571 entry = next; | |
| 572 } | |
| 235 return null; | 573 return null; |
| 236 } | 574 } |
| 237 | 575 |
| 238 /* patch */ void operator []=(K key, V value) { | |
| 239 int offset = _hashTable._put(key); | |
| 240 _hashTable._setValue(offset, value); | |
| 241 _hashTable._checkCapacity(); | |
| 242 } | |
| 243 | |
| 244 /* patch */ V putIfAbsent(K key, V ifAbsent()) { | |
| 245 int offset = _hashTable._probeForAdd(_hashTable._hashCodeOf(key), key); | |
| 246 Object entry = _hashTable._table[offset]; | |
| 247 if (!_hashTable._isFree(entry)) { | |
| 248 return _hashTable._value(offset); | |
| 249 } | |
| 250 int modificationCount = _hashTable._modificationCount; | |
| 251 V value = ifAbsent(); | |
| 252 if (modificationCount == _hashTable._modificationCount) { | |
| 253 _hashTable._setKey(offset, key); | |
| 254 _hashTable._setValue(offset, value); | |
| 255 _hashTable._linkLast(offset); | |
| 256 if (entry == null) { | |
| 257 _hashTable._entryCount++; | |
| 258 _hashTable._checkCapacity(); | |
| 259 } else { | |
| 260 assert(identical(entry, _TOMBSTONE)); | |
| 261 _hashTable._deletedCount--; | |
| 262 } | |
| 263 _hashTable._recordModification(); | |
| 264 } else { | |
| 265 // The table might have changed, so we can't trust [offset] any more. | |
| 266 // Do another lookup before setting the value. | |
| 267 offset = _hashTable._put(key); | |
| 268 _hashTable._setValue(offset, value); | |
| 269 _hashTable._checkCapacity(); | |
| 270 } | |
| 271 return value; | |
| 272 } | |
| 273 | |
| 274 /* patch */ V remove(Object key) { | |
| 275 int offset = _hashTable._remove(key); | |
| 276 if (offset < 0) return null; | |
| 277 Object oldValue = _hashTable._value(offset); | |
| 278 _hashTable._setValue(offset, null); | |
| 279 _hashTable._checkCapacity(); | |
| 280 return oldValue; | |
| 281 } | |
| 282 | |
| 283 /* patch */ void clear() { | 576 /* patch */ void clear() { |
| 284 _hashTable._clear(); | 577 _elementCount = 0; |
| 285 } | 578 _nextEntry = _previousEntry = this; |
| 286 | 579 _buckets = new List(_INITIAL_CAPACITY); |
| 287 /* patch */ void forEach(void action (K key, V value)) { | 580 _modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
| 288 int modificationCount = _hashTable._modificationCount; | 581 } |
| 289 for (int offset = _hashTable._next(_LinkedHashTable._HEAD_OFFSET); | 582 |
| 290 offset != _LinkedHashTable._HEAD_OFFSET; | 583 void _addEntry(List buckets, int index, int length, |
| 291 offset = _hashTable._next(offset)) { | 584 K key, V value, int hashCode) { |
| 292 action(_hashTable._key(offset), _hashTable._value(offset)); | 585 _HashMapEntry entry = |
| 293 _hashTable._checkModification(modificationCount); | 586 new _LinkedHashMapEntry(key, value, hashCode, buckets[index], |
| 294 } | 587 _previousEntry, this); |
| 295 } | 588 buckets[index] = entry; |
| 296 | 589 int newElements = _elementCount + 1; |
| 297 /* patch */ Iterable<K> get keys => | 590 _elementCount = newElements; |
| 298 new _LinkedHashTableKeyIterable<K>(_hashTable); | 591 // If we end up with more than 75% non-empty entries, we |
| 299 | 592 // resize the backing store. |
| 300 /* patch */ Iterable<V> get values => | 593 if ((newElements << 2) > ((length << 1) + length)) _resize(); |
| 301 new _LinkedHashTableValueIterable<V>(_hashTable, | 594 _modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK; |
| 302 _LinkedHashMapTable._VALUE_INDEX); | 595 } |
| 303 | 596 |
| 304 /* patch */ int get length => _hashTable._elementCount; | 597 void _resize() { |
| 305 | 598 List oldBuckets = _buckets; |
| 306 /* patch */ bool get isEmpty => _hashTable._elementCount == 0; | 599 int oldLength = oldBuckets.length; |
| 307 | 600 int newLength = oldLength << 1; |
| 308 /* patch */ bool get isNotEmpty => !isEmpty; | 601 List newBuckets = new List(newLength); |
| 602 for (int i = 0; i < oldLength; i++) { | |
| 603 _HashMapEntry entry = oldBuckets[i]; | |
| 604 while (entry != null) { | |
| 605 _HashMapEntry next = entry.next; | |
| 606 int hashCode = entry.hashCode; | |
| 607 int index = hashCode & (newLength - 1); | |
| 608 entry.next = newBuckets[index]; | |
| 609 newBuckets[index] = entry; | |
| 610 entry = next; | |
| 611 } | |
| 612 } | |
| 613 _buckets = newBuckets; | |
| 614 } | |
| 309 } | 615 } |
| 310 | 616 |
| 311 patch class LinkedHashSet<E> extends _HashSetBase<E> { | 617 patch class LinkedHashSet<E> extends _HashSetBase<E> { |
| 312 static const int _INITIAL_CAPACITY = 8; | 618 static const int _INITIAL_CAPACITY = 8; |
| 313 _LinkedHashTable<E> _table; | 619 _LinkedHashTable<E> _table; |
| 314 | 620 |
| 315 /* patch */ LinkedHashSet() { | 621 /* patch */ LinkedHashSet() { |
| 316 _table = new _LinkedHashTable(_INITIAL_CAPACITY); | 622 _table = new _LinkedHashTable(_INITIAL_CAPACITY); |
| 317 _table._container = this; | 623 _table._container = this; |
| 318 } | 624 } |
| (...skipping 682 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1001 | 1307 |
| 1002 _LinkedHashMapTable() : super(_INITIAL_CAPACITY); | 1308 _LinkedHashMapTable() : super(_INITIAL_CAPACITY); |
| 1003 | 1309 |
| 1004 V _value(int offset) => _table[offset + _VALUE_INDEX]; | 1310 V _value(int offset) => _table[offset + _VALUE_INDEX]; |
| 1005 void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; } | 1311 void _setValue(int offset, V value) { _table[offset + _VALUE_INDEX] = value; } |
| 1006 | 1312 |
| 1007 _copyEntry(List oldTable, int fromOffset, int toOffset) { | 1313 _copyEntry(List oldTable, int fromOffset, int toOffset) { |
| 1008 _table[toOffset + _VALUE_INDEX] = oldTable[fromOffset + _VALUE_INDEX]; | 1314 _table[toOffset + _VALUE_INDEX] = oldTable[fromOffset + _VALUE_INDEX]; |
| 1009 } | 1315 } |
| 1010 } | 1316 } |
| OLD | NEW |