| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 // TODO(alanknight): Replace with proper identity collection. Issue 4161 | |
| 6 library identity_set; | |
| 7 | |
| 8 import 'dart:collection'; | |
| 9 | |
| 10 // Hash map implementation with open addressing and quadratic probing. | |
| 11 class IdentityMap<K, V> implements HashMap<K, V> { | |
| 12 | |
| 13 // The [_keys] list contains the keys inserted in the map. | |
| 14 // The [_keys] list must be a raw list because it | |
| 15 // will contain both elements of type K, and the [_DELETED_KEY] of type | |
| 16 // [_DeletedKeySentinel]. | |
| 17 // The alternative of declaring the [_keys] list as of type Object | |
| 18 // does not work, because the HashSetIterator constructor would fail: | |
| 19 // HashSetIterator(HashSet<E> set) | |
| 20 // : _nextValidIndex = -1, | |
| 21 // _entries = set_._backingMap._keys { | |
| 22 // _advance(); | |
| 23 // } | |
| 24 // With K being type int, for example, it would fail because | |
| 25 // List<Object> is not assignable to type List<int> of entries. | |
| 26 List _keys; | |
| 27 | |
| 28 // The values inserted in the map. For a filled entry index in this | |
| 29 // list, there is always the corresponding key in the [keys_] list | |
| 30 // at the same entry index. | |
| 31 List<V> _values; | |
| 32 | |
| 33 // The load limit is the number of entries we allow until we double | |
| 34 // the size of the lists. | |
| 35 int _loadLimit; | |
| 36 | |
| 37 // The current number of entries in the map. Will never be greater | |
| 38 // than [_loadLimit]. | |
| 39 int _numberOfEntries; | |
| 40 | |
| 41 // The current number of deleted entries in the map. | |
| 42 int _numberOfDeleted; | |
| 43 | |
| 44 // The sentinel when a key is deleted from the map. | |
| 45 static const _DeletedKeySentinel _DELETED_KEY = const _DeletedKeySentinel(); | |
| 46 | |
| 47 // The initial capacity of a hash map. | |
| 48 static const int _INITIAL_CAPACITY = 8; // must be power of 2 | |
| 49 | |
| 50 IdentityMap() { | |
| 51 _numberOfEntries = 0; | |
| 52 _numberOfDeleted = 0; | |
| 53 _loadLimit = _computeLoadLimit(_INITIAL_CAPACITY); | |
| 54 _keys = new List(_INITIAL_CAPACITY); | |
| 55 _values = new List<V>(_INITIAL_CAPACITY); | |
| 56 } | |
| 57 | |
| 58 factory IdentityMap.from(Map<K, V> other) { | |
| 59 Map<K, V> result = new IdentityMap<K, V>(); | |
| 60 other.forEach((K key, V value) { result[key] = value; }); | |
| 61 return result; | |
| 62 } | |
| 63 | |
| 64 static int _computeLoadLimit(int capacity) { | |
| 65 return (capacity * 3) ~/ 4; | |
| 66 } | |
| 67 | |
| 68 static int _firstProbe(int hashCode, int length) { | |
| 69 return hashCode & (length - 1); | |
| 70 } | |
| 71 | |
| 72 static int _nextProbe(int currentProbe, int numberOfProbes, int length) { | |
| 73 return (currentProbe + numberOfProbes) & (length - 1); | |
| 74 } | |
| 75 | |
| 76 int _probeForAdding(K key) { | |
| 77 if (key == null) throw new ArgumentError(null); | |
| 78 int hash = _firstProbe(key.hashCode, _keys.length); | |
| 79 int numberOfProbes = 1; | |
| 80 int initialHash = hash; | |
| 81 // insertionIndex points to a slot where a key was deleted. | |
| 82 int insertionIndex = -1; | |
| 83 while (true) { | |
| 84 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. | |
| 85 Object existingKey = _keys[hash]; | |
| 86 if (existingKey == null) { | |
| 87 // We are sure the key is not already in the set. | |
| 88 // If the current slot is empty and we didn't find any | |
| 89 // insertion slot before, return this slot. | |
| 90 if (insertionIndex < 0) return hash; | |
| 91 // If we did find an insertion slot before, return it. | |
| 92 return insertionIndex; | |
| 93 } else if (identical(existingKey, key)) { | |
| 94 // The key is already in the map. Return its slot. | |
| 95 return hash; | |
| 96 } else if ((insertionIndex < 0) && | |
| 97 (identical(existingKey, _DELETED_KEY))) { | |
| 98 // The slot contains a deleted element. Because previous calls to this | |
| 99 // method may not have had this slot deleted, we must continue iterate | |
| 100 // to find if there is a slot with the given key. | |
| 101 insertionIndex = hash; | |
| 102 } | |
| 103 | |
| 104 // We did not find an insertion slot. Look at the next one. | |
| 105 hash = _nextProbe(hash, numberOfProbes++, _keys.length); | |
| 106 // _ensureCapacity has guaranteed the following cannot happen. | |
| 107 // assert(hash != initialHash); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 int _probeForLookup(K key) { | |
| 112 if (key == null) throw new ArgumentError(null); | |
| 113 int hash = _firstProbe(key.hashCode, _keys.length); | |
| 114 int numberOfProbes = 1; | |
| 115 int initialHash = hash; | |
| 116 while (true) { | |
| 117 // [existingKey] can be either of type [K] or [_DeletedKeySentinel]. | |
| 118 Object existingKey = _keys[hash]; | |
| 119 // If the slot does not contain anything (in particular, it does not | |
| 120 // contain a deleted key), we know the key is not in the map. | |
| 121 if (existingKey == null) return -1; | |
| 122 // The key is in the map, return its index. | |
| 123 if (identical(existingKey, key)) return hash; | |
| 124 // Go to the next probe. | |
| 125 hash = _nextProbe(hash, numberOfProbes++, _keys.length); | |
| 126 // _ensureCapacity has guaranteed the following cannot happen. | |
| 127 // assert(hash != initialHash); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void _ensureCapacity() { | |
| 132 int newNumberOfEntries = _numberOfEntries + 1; | |
| 133 // Test if adding an element will reach the load limit. | |
| 134 if (newNumberOfEntries >= _loadLimit) { | |
| 135 _grow(_keys.length * 2); | |
| 136 return; | |
| 137 } | |
| 138 | |
| 139 // Make sure that we don't have poor performance when a map | |
| 140 // contains lots of deleted entries: we _grow if | |
| 141 // there are more deleted entried than free entries. | |
| 142 int capacity = _keys.length; | |
| 143 int numberOfFreeOrDeleted = capacity - newNumberOfEntries; | |
| 144 int numberOfFree = numberOfFreeOrDeleted - _numberOfDeleted; | |
| 145 // assert(numberOfFree > 0); | |
| 146 if (_numberOfDeleted > numberOfFree) { | |
| 147 _grow(_keys.length); | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 static bool _isPowerOfTwo(int x) { | |
| 152 return ((x & (x - 1)) == 0); | |
| 153 } | |
| 154 | |
| 155 void _grow(int newCapacity) { | |
| 156 assert(_isPowerOfTwo(newCapacity)); | |
| 157 int capacity = _keys.length; | |
| 158 _loadLimit = _computeLoadLimit(newCapacity); | |
| 159 List oldKeys = _keys; | |
| 160 List<V> oldValues = _values; | |
| 161 _keys = new List(newCapacity); | |
| 162 _values = new List<V>(newCapacity); | |
| 163 for (int i = 0; i < capacity; i++) { | |
| 164 // [key] can be either of type [K] or [_DeletedKeySentinel]. | |
| 165 Object key = oldKeys[i]; | |
| 166 // If there is no key, we don't need to deal with the current slot. | |
| 167 if (key == null || identical(key, _DELETED_KEY)) { | |
| 168 continue; | |
| 169 } | |
| 170 V value = oldValues[i]; | |
| 171 // Insert the {key, value} pair in their new slot. | |
| 172 int newIndex = _probeForAdding(key); | |
| 173 _keys[newIndex] = key; | |
| 174 _values[newIndex] = value; | |
| 175 } | |
| 176 _numberOfDeleted = 0; | |
| 177 } | |
| 178 | |
| 179 void clear() { | |
| 180 _numberOfEntries = 0; | |
| 181 _numberOfDeleted = 0; | |
| 182 int length = _keys.length; | |
| 183 for (int i = 0; i < length; i++) { | |
| 184 _keys[i] = null; | |
| 185 _values[i] = null; | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 void operator []=(K key, V value) { | |
| 190 _ensureCapacity(); | |
| 191 int index = _probeForAdding(key); | |
| 192 if ((_keys[index] == null) || (identical(_keys[index], _DELETED_KEY))) { | |
| 193 _numberOfEntries++; | |
| 194 } | |
| 195 _keys[index] = key; | |
| 196 _values[index] = value; | |
| 197 } | |
| 198 | |
| 199 V operator [](K key) { | |
| 200 int index = _probeForLookup(key); | |
| 201 if (index < 0) return null; | |
| 202 return _values[index]; | |
| 203 } | |
| 204 | |
| 205 V putIfAbsent(K key, V ifAbsent()) { | |
| 206 int index = _probeForLookup(key); | |
| 207 if (index >= 0) return _values[index]; | |
| 208 | |
| 209 V value = ifAbsent(); | |
| 210 this[key] = value; | |
| 211 return value; | |
| 212 } | |
| 213 | |
| 214 V remove(K key) { | |
| 215 int index = _probeForLookup(key); | |
| 216 if (index >= 0) { | |
| 217 _numberOfEntries--; | |
| 218 V value = _values[index]; | |
| 219 _values[index] = null; | |
| 220 // Set the key to the sentinel to not break the probing chain. | |
| 221 _keys[index] = _DELETED_KEY; | |
| 222 _numberOfDeleted++; | |
| 223 return value; | |
| 224 } | |
| 225 return null; | |
| 226 } | |
| 227 | |
| 228 bool get isEmpty { | |
| 229 return _numberOfEntries == 0; | |
| 230 } | |
| 231 | |
| 232 int get length { | |
| 233 return _numberOfEntries; | |
| 234 } | |
| 235 | |
| 236 void forEach(void f(K key, V value)) { | |
| 237 int length = _keys.length; | |
| 238 for (int i = 0; i < length; i++) { | |
| 239 var key = _keys[i]; | |
| 240 if ((key != null) && (!identical(key, _DELETED_KEY))) { | |
| 241 f(key, _values[i]); | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 | |
| 247 Collection<K> get keys { | |
| 248 List<K> list = new List<K>(length); | |
| 249 int i = 0; | |
| 250 forEach((K key, V value) { | |
| 251 list[i++] = key; | |
| 252 }); | |
| 253 return list; | |
| 254 } | |
| 255 | |
| 256 Collection<V> get values { | |
| 257 List<V> list = new List<V>(length); | |
| 258 int i = 0; | |
| 259 forEach((K key, V value) { | |
| 260 list[i++] = value; | |
| 261 }); | |
| 262 return list; | |
| 263 } | |
| 264 | |
| 265 bool containsKey(K key) { | |
| 266 return (_probeForLookup(key) != -1); | |
| 267 } | |
| 268 | |
| 269 bool containsValue(V value) { | |
| 270 int length = _values.length; | |
| 271 for (int i = 0; i < length; i++) { | |
| 272 var key = _keys[i]; | |
| 273 if ((key != null) && (!identical(key, _DELETED_KEY))) { | |
| 274 if (_values[i] == value) return true; | |
| 275 } | |
| 276 } | |
| 277 return false; | |
| 278 } | |
| 279 | |
| 280 String toString() { | |
| 281 return Maps.mapToString(this); | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 | |
| 286 /** | |
| 287 * A singleton sentinel used to represent when a key is deleted from the map. | |
| 288 * We can't use [: const Object() :] as a sentinel because it would end up | |
| 289 * canonicalized and then we cannot distinguish the deleted key from the | |
| 290 * canonicalized [: Object() :]. | |
| 291 */ | |
| 292 class _DeletedKeySentinel { | |
| 293 const _DeletedKeySentinel(); | |
| 294 } | |
| 295 | |
| 296 | |
| 297 /** | |
| 298 * This class represents a pair of two objects, used by LinkedHashMap | |
| 299 * to store a {key, value} in a list. | |
| 300 */ | |
| 301 class _KeyValuePair<K, V> { | |
| 302 _KeyValuePair(this.key, this.value) {} | |
| 303 | |
| 304 final K key; | |
| 305 V value; | |
| 306 } | |
| OLD | NEW |