Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Side by Side Diff: corelib/src/implementation/hash_map_set.dart

Issue 8591022: Make HashMap's delete key a const expression. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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();
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
ngeoffray 2011/11/18 08:31:01 End comment with '.'. What about frog?
Siggi Cherem (dart-lang) 2011/11/18 17:23:57 Done. Just double checked, frog also fails.
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].
ngeoffray 2011/11/18 08:31:01 I wouldn't duplicate this comment all around (I gu
Siggi Cherem (dart-lang) 2011/11/18 17:23:57 Thx. Made the comment more specific to [existingKe
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
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
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
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().
ngeoffray 2011/11/18 08:31:01 [: Object() :]
Siggi Cherem (dart-lang) 2011/11/18 17:23:57 Done.
430 */
431 class _DeletedKeySentinel {
432 const _DeletedKeySentinel();
433 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698