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

Side by Side Diff: lib/equality.dart

Issue 1638163002: Modernize the package's style. (Closed) Base URL: git@github.com:dart-lang/collection@master
Patch Set: Code review changes Created 4 years, 10 months 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
« no previous file with comments | « lib/collection.dart ('k') | lib/iterable_zip.dart » ('j') | 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) 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 /** 5 /// Import `collection.dart` instead.
6 * Defines equality relations on collections. 6 @Deprecated("Will be removed in collection 2.0.0.")
7 */
8 library dart.pkg.collection.equality; 7 library dart.pkg.collection.equality;
9 8
10 import "dart:collection"; 9 export "src/equality.dart";
11
12 const int _HASH_MASK = 0x7fffffff;
13
14 /**
15 * A generic equality relation on objects.
16 */
17 abstract class Equality<E> {
18 const factory Equality() = DefaultEquality;
19
20 /**
21 * Compare two elements for being equal.
22 *
23 * This should be a proper equality relation.
24 */
25 bool equals(E e1, E e2);
26
27 /**
28 * Get a hashcode of an element.
29 *
30 * The hashcode should be compatible with [equals], so that if
31 * `equals(a, b)` then `hash(a) == hash(b)`.
32 */
33 int hash(E e);
34
35 /**
36 * Test whether an object is a valid argument to [equals] and [hash].
37 *
38 * Some implementations may be restricted to only work on specific types
39 * of objects.
40 */
41 bool isValidKey(Object o);
42 }
43
44 /**
45 * Equality of objects that compares only the natural equality of the objects.
46 *
47 * This equality uses the objects' own [Object.==] and [Object.hashCode] for
48 * the equality.
49 */
50 class DefaultEquality implements Equality {
51 const DefaultEquality();
52 bool equals(Object e1, Object e2) => e1 == e2;
53 int hash(Object e) => e.hashCode;
54 bool isValidKey(Object o) => true;
55 }
56
57 /**
58 * Equality of objects that compares only the identity of the objects.
59 */
60 class IdentityEquality implements Equality {
61 const IdentityEquality();
62 bool equals(Object e1, Object e2) => identical(e1, e2);
63 int hash(Object e) => identityHashCode(e);
64 bool isValidKey(Object o) => true;
65 }
66
67 /**
68 * Equality on iterables.
69 *
70 * Two iterables are equal if they have the same elements in the same order.
71 */
72 class IterableEquality<E> implements Equality<Iterable<E>> {
73 final Equality<E> _elementEquality;
74 const IterableEquality([Equality<E> elementEquality =
75 const DefaultEquality()])
76 : _elementEquality = elementEquality;
77
78 bool equals(Iterable<E> elements1, Iterable<E> elements2) {
79 if (identical(elements1, elements2)) return true;
80 if (elements1 == null || elements2 == null) return false;
81 Iterator it1 = elements1.iterator;
82 Iterator it2 = elements2.iterator;
83 while (true) {
84 bool hasNext = it1.moveNext();
85 if (hasNext != it2.moveNext()) return false;
86 if (!hasNext) return true;
87 if (!_elementEquality.equals(it1.current, it2.current)) return false;
88 }
89 }
90
91 int hash(Iterable<E> elements) {
92 // Jenkins's one-at-a-time hash function.
93 int hash = 0;
94 for (E element in elements) {
95 int c = _elementEquality.hash(element);
96 hash = (hash + c) & _HASH_MASK;
97 hash = (hash + (hash << 10)) & _HASH_MASK;
98 hash ^= (hash >> 6);
99 }
100 hash = (hash + (hash << 3)) & _HASH_MASK;
101 hash ^= (hash >> 11);
102 hash = (hash + (hash << 15)) & _HASH_MASK;
103 return hash;
104 }
105
106 bool isValidKey(Object o) => o is Iterable<E>;
107 }
108
109 /**
110 * Equality on lists.
111 *
112 * Two lists are equal if they have the same length and their elements
113 * at each index are equal.
114 *
115 * This is effectively the same as [IterableEquality] except that it
116 * accesses elements by index instead of through iteration.
117 */
118 class ListEquality<E> implements Equality<List<E>> {
119 final Equality<E> _elementEquality;
120 const ListEquality([Equality<E> elementEquality = const DefaultEquality()])
121 : _elementEquality = elementEquality;
122
123 bool equals(List<E> e1, List<E> e2) {
124 if (identical(e1, e2)) return true;
125 if (e1 == null || e2 == null) return false;
126 int length = e1.length;
127 if (length != e2.length) return false;
128 for (int i = 0; i < length; i++) {
129 if (!_elementEquality.equals(e1[i], e2[i])) return false;
130 }
131 return true;
132 }
133
134 int hash(List<E> e) {
135 // Jenkins's one-at-a-time hash function.
136 // This code is almost identical to the one in IterableEquality, except
137 // that it uses indexing instead of iterating to get the elements.
138 int hash = 0;
139 for (int i = 0; i < e.length; i++) {
140 int c = _elementEquality.hash(e[i]);
141 hash = (hash + c) & _HASH_MASK;
142 hash = (hash + (hash << 10)) & _HASH_MASK;
143 hash ^= (hash >> 6);
144 }
145 hash = (hash + (hash << 3)) & _HASH_MASK;
146 hash ^= (hash >> 11);
147 hash = (hash + (hash << 15)) & _HASH_MASK;
148 return hash;
149 }
150
151 bool isValidKey(Object o) => o is List<E>;
152 }
153
154 abstract class _UnorderedEquality<E, T extends Iterable<E>>
155 implements Equality<T> {
156 final Equality<E> _elementEquality;
157
158 const _UnorderedEquality(this._elementEquality);
159
160 bool equals(T e1, T e2) {
161 if (identical(e1, e2)) return true;
162 if (e1 == null || e2 == null) return false;
163 HashMap<E, int> counts = new HashMap(
164 equals: _elementEquality.equals,
165 hashCode: _elementEquality.hash,
166 isValidKey: _elementEquality.isValidKey);
167 int length = 0;
168 for (var e in e1) {
169 int count = counts[e];
170 if (count == null) count = 0;
171 counts[e] = count + 1;
172 length++;
173 }
174 for (var e in e2) {
175 int count = counts[e];
176 if (count == null || count == 0) return false;
177 counts[e] = count - 1;
178 length--;
179 }
180 return length == 0;
181 }
182
183 int hash(T e) {
184 int hash = 0;
185 for (E element in e) {
186 int c = _elementEquality.hash(element);
187 hash = (hash + c) & _HASH_MASK;
188 }
189 hash = (hash + (hash << 3)) & _HASH_MASK;
190 hash ^= (hash >> 11);
191 hash = (hash + (hash << 15)) & _HASH_MASK;
192 return hash;
193 }
194 }
195
196 /**
197 * Equality of the elements of two iterables without considering order.
198 *
199 * Two iterables are considered equal if they have the same number of elements,
200 * and the elements of one set can be paired with the elements
201 * of the other iterable, so that each pair are equal.
202 */
203 class UnorderedIterableEquality<E> extends _UnorderedEquality<E, Iterable<E>> {
204 const UnorderedIterableEquality(
205 [Equality<E> elementEquality = const DefaultEquality()])
206 : super(elementEquality);
207
208 bool isValidKey(Object o) => o is Iterable<E>;
209 }
210
211 /**
212 * Equality of sets.
213 *
214 * Two sets are considered equal if they have the same number of elements,
215 * and the elements of one set can be paired with the elements
216 * of the other set, so that each pair are equal.
217 *
218 * This equality behaves the same as [UnorderedIterableEquality] except that
219 * it expects sets instead of iterables as arguments.
220 */
221 class SetEquality<E> extends _UnorderedEquality<E, Set<E>> {
222 const SetEquality(
223 [Equality<E> elementEquality = const DefaultEquality()])
224 : super(elementEquality);
225
226 bool isValidKey(Object o) => o is Set<E>;
227 }
228
229 /**
230 * Internal class used by [MapEquality].
231 *
232 * The class represents a map entry as a single object,
233 * using a combined hashCode and equality of the key and value.
234 */
235 class _MapEntry {
236 final MapEquality equality;
237 final key;
238 final value;
239 _MapEntry(this.equality, this.key, this.value);
240
241 int get hashCode =>
242 (3 * equality._keyEquality.hash(key) +
243 7 * equality._valueEquality.hash(value)) & _HASH_MASK;
244
245 bool operator==(Object other) {
246 if (other is! _MapEntry) return false;
247 _MapEntry otherEntry = other;
248 return equality._keyEquality.equals(key, otherEntry.key) &&
249 equality._valueEquality.equals(value, otherEntry.value);
250
251 }
252 }
253
254 /**
255 * Equality on maps.
256 *
257 * Two maps are equal if they have the same number of entries, and if the
258 * entries of the two maps are pairwise equal on both key and value.
259 */
260 class MapEquality<K, V> implements Equality<Map<K, V>> {
261 final Equality<K> _keyEquality;
262 final Equality<V> _valueEquality;
263 const MapEquality({ Equality<K> keys : const DefaultEquality(),
264 Equality<V> values : const DefaultEquality() })
265 : _keyEquality = keys, _valueEquality = values;
266
267 bool equals(Map<K, V> e1, Map<K, V> e2) {
268 if (identical(e1, e2)) return true;
269 if (e1 == null || e2 == null) return false;
270 int length = e1.length;
271 if (length != e2.length) return false;
272 Map<_MapEntry, int> equalElementCounts = new HashMap();
273 for (K key in e1.keys) {
274 _MapEntry entry = new _MapEntry(this, key, e1[key]);
275 int count = equalElementCounts[entry];
276 if (count == null) count = 0;
277 equalElementCounts[entry] = count + 1;
278 }
279 for (K key in e2.keys) {
280 _MapEntry entry = new _MapEntry(this, key, e2[key]);
281 int count = equalElementCounts[entry];
282 if (count == null || count == 0) return false;
283 equalElementCounts[entry] = count - 1;
284 }
285 return true;
286 }
287
288 int hash(Map<K, V> map) {
289 int hash = 0;
290 for (K key in map.keys) {
291 int keyHash = _keyEquality.hash(key);
292 int valueHash = _valueEquality.hash(map[key]);
293 hash = (hash + 3 * keyHash + 7 * valueHash) & _HASH_MASK;
294 }
295 hash = (hash + (hash << 3)) & _HASH_MASK;
296 hash ^= (hash >> 11);
297 hash = (hash + (hash << 15)) & _HASH_MASK;
298 return hash;
299 }
300
301 bool isValidKey(Object o) => o is Map<K, V>;
302 }
303
304 /**
305 * Combines several equalities into a single equality.
306 *
307 * Tries each equality in order, using [Equality.isValidKey], and returns
308 * the result of the first equality that applies to the argument or arguments.
309 *
310 * For `equals`, the first equality that matches the first argument is used,
311 * and if the second argument of `equals` is not valid for that equality,
312 * it returns false.
313 *
314 * Because the equalities are tried in order, they should generally work on
315 * disjoint types. Otherwise the multi-equality may give inconsistent results
316 * for `equals(e1, e2)` and `equals(e2, e1)`. This can happen if one equality
317 * considers only `e1` a valid key, and not `e2`, but an equality which is
318 * checked later, allows both.
319 */
320 class MultiEquality<E> implements Equality<E> {
321 final Iterable<Equality<E>> _equalities;
322
323 const MultiEquality(Iterable<Equality<E>> equalities)
324 : _equalities = equalities;
325
326 bool equals(E e1, E e2) {
327 for (Equality<E> eq in _equalities) {
328 if (eq.isValidKey(e1)) return eq.isValidKey(e2) && eq.equals(e1, e2);
329 }
330 return false;
331 }
332
333 int hash(E e) {
334 for (Equality<E> eq in _equalities) {
335 if (eq.isValidKey(e)) return eq.hash(e);
336 }
337 return -1;
338 }
339
340 bool isValidKey(Object o) {
341 for (Equality<E> eq in _equalities) {
342 if (eq.isValidKey(o)) return true;
343 }
344 return false;
345 }
346 }
347
348 /**
349 * Deep equality on collections.
350 *
351 * Recognizes lists, sets, iterables and maps and compares their elements using
352 * deep equality as well.
353 *
354 * Non-iterable/map objects are compared using a configurable base equality.
355 *
356 * Works in one of two modes: ordered or unordered.
357 *
358 * In ordered mode, lists and iterables are required to have equal elements
359 * in the same order. In unordered mode, the order of elements in iterables
360 * and lists are not important.
361 *
362 * A list is only equal to another list, likewise for sets and maps. All other
363 * iterables are compared as iterables only.
364 */
365 class DeepCollectionEquality implements Equality {
366 final Equality _base;
367 final bool _unordered;
368 const DeepCollectionEquality([Equality base = const DefaultEquality()])
369 : _base = base, _unordered = false;
370
371 /**
372 * Creates a deep equality on collections where the order of lists and
373 * iterables are not considered important. That is, lists and iterables are
374 * treated as unordered iterables.
375 */
376 const DeepCollectionEquality.unordered(
377 [Equality base = const DefaultEquality()])
378 : _base = base, _unordered = true;
379
380 bool equals(e1, e2) {
381 if (e1 is Set) {
382 if (e2 is! Set) return false;
383 return new SetEquality(this).equals(e1, e2);
384 }
385 if (e1 is Map) {
386 if (e2 is! Map) return false;
387 return new MapEquality(keys: this, values: this).equals(e1, e2);
388 }
389 if (!_unordered) {
390 if (e1 is List) {
391 if (e2 is! List) return false;
392 return new ListEquality(this).equals(e1, e2);
393 }
394 if (e1 is Iterable) {
395 if (e2 is! Iterable) return false;
396 return new IterableEquality(this).equals(e1, e2);
397 }
398 } else if (e1 is Iterable) {
399 if (e2 is! Iterable) return false;
400 if (e1 is List != e2 is List) return false;
401 return new UnorderedIterableEquality(this).equals(e1, e2);
402 }
403 return _base.equals(e1, e2);
404 }
405
406 int hash(Object o) {
407 if (o is Set) return new SetEquality(this).hash(o);
408 if (o is Map) return new MapEquality(keys: this, values: this).hash(o);
409 if (!_unordered) {
410 if (o is List) return new ListEquality(this).hash(o);
411 if (o is Iterable) return new IterableEquality(this).hash(o);
412 } else if (o is Iterable) {
413 return new UnorderedIterableEquality(this).hash(o);
414 }
415 return _base.hash(o);
416 }
417
418 bool isValidKey(Object o) => o is Iterable || o is Map || _base.isValidKey(o);
419 }
OLDNEW
« no previous file with comments | « lib/collection.dart ('k') | lib/iterable_zip.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698