OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 /** | |
6 * Base implementations of [Set]. | |
7 */ | |
8 part of dart.collection; | |
9 | |
10 /** | |
11 * Mixin implementation of [Set]. | |
12 * | |
13 * This class provides a base implementation of a `Set` that depends only | |
14 * on the abstract members: [add], [contains], [lookup], [remove], | |
15 * [iterator], [length] and [toSet]. | |
16 * | |
17 * Some of the methods assume that `toSet` creates a modifiable set. | |
18 * If using this mixin for an unmodifiable set, | |
19 * where `toSet` should return an unmodifiable set, | |
20 * it's necessary to reimplement | |
21 * [retainAll], [union], [intersection] and [difference]. | |
22 * | |
23 * Implementations of `Set` using this mixin should consider also implementing | |
24 * `clear` in constant time. The default implementation works by removing every | |
25 * element. | |
26 */ | |
27 abstract class SetMixin<E> implements Set<E> { | |
28 // This class reimplements all of [IterableMixin]. | |
29 // If/when Dart mixins get more powerful, we should just create a single | |
30 // Mixin class from IterableMixin and the new methods of thisclass. | |
31 | |
32 bool add(E element); | |
33 | |
34 bool contains(Object element); | |
35 | |
36 E lookup(Object element); | |
37 | |
38 bool remove(Object element); | |
39 | |
40 Iterator<E> get iterator; | |
41 | |
42 Set<E> toSet(); | |
43 | |
44 int get length; | |
45 | |
46 bool get isEmpty => length == 0; | |
47 | |
48 bool get isNotEmpty => length != 0; | |
49 | |
50 void clear() { | |
51 removeAll(toList()); | |
52 } | |
53 | |
54 void addAll(Iterable<E> elements) { | |
55 for (E element in elements) add(element); | |
56 } | |
57 | |
58 void removeAll(Iterable<Object> elements) { | |
59 for (Object element in elements) remove(element); | |
60 } | |
61 | |
62 void retainAll(Iterable<Object> elements) { | |
63 // Create a copy of the set, remove all of elements from the copy, | |
64 // then remove all remaining elements in copy from this. | |
65 Set<E> toRemove = toSet(); | |
66 for (Object o in elements) { | |
67 toRemove.remove(o); | |
68 } | |
69 removeAll(toRemove); | |
70 } | |
71 | |
72 void removeWhere(bool test(E element)) { | |
73 List toRemove = []; | |
74 for (E element in this) { | |
75 if (test(element)) toRemove.add(element); | |
76 } | |
77 removeAll(toRemove); | |
78 } | |
79 | |
80 void retainWhere(bool test(E element)) { | |
81 List toRemove = []; | |
82 for (E element in this) { | |
83 if (!test(element)) toRemove.add(element); | |
84 } | |
85 removeAll(toRemove); | |
86 } | |
87 | |
88 bool containsAll(Iterable<Object> other) { | |
89 for (Object o in other) { | |
90 if (!contains(o)) return false; | |
91 } | |
92 return true; | |
93 } | |
94 | |
95 Set<E> union(Set<E> other) { | |
96 return toSet()..addAll(other); | |
97 } | |
98 | |
99 Set<E> intersection(Set<Object> other) { | |
100 Set<E> result = toSet(); | |
101 for (E element in this) { | |
102 if (!other.contains(element)) result.remove(element); | |
103 } | |
104 return result; | |
105 } | |
106 | |
107 Set<E> difference(Set<Object> other) { | |
108 Set<E> result = toSet(); | |
109 for (E element in this) { | |
110 if (other.contains(element)) result.remove(element); | |
111 } | |
112 return result; | |
113 } | |
114 | |
115 List<E> toList({bool growable: true}) { | |
116 List<E> result = growable ? (new List<E>()..length = length) | |
117 : new List<E>(length); | |
118 int i = 0; | |
119 for (E element in this) result[i++] = element; | |
120 return result; | |
121 } | |
122 | |
123 Iterable/*<T>*/ map/*<T>*/(/*=T*/f(E element)) => | |
124 new EfficientLengthMappedIterable<E, dynamic/*=T*/>(this, f); | |
125 | |
126 E get single { | |
127 if (length > 1) throw IterableElementError.tooMany(); | |
128 Iterator<E> it = iterator; | |
129 if (!it.moveNext()) throw IterableElementError.noElement(); | |
130 E result = it.current; | |
131 return result; | |
132 } | |
133 | |
134 String toString() => IterableBase.iterableToFullString(this, '{', '}'); | |
135 | |
136 // Copied from IterableMixin. | |
137 // Should be inherited if we had multi-level mixins. | |
138 | |
139 Iterable<E> where(bool f(E element)) => new WhereIterable<E>(this, f); | |
140 | |
141 Iterable/*<T>*/ expand/*<T>*/(Iterable/*<T>*/ f(E element)) => | |
142 new ExpandIterable<E, dynamic/*=T*/>(this, f); | |
143 | |
144 void forEach(void f(E element)) { | |
145 for (E element in this) f(element); | |
146 } | |
147 | |
148 E reduce(E combine(E value, E element)) { | |
149 Iterator<E> iterator = this.iterator; | |
150 if (!iterator.moveNext()) { | |
151 throw IterableElementError.noElement(); | |
152 } | |
153 E value = iterator.current; | |
154 while (iterator.moveNext()) { | |
155 value = combine(value, iterator.current); | |
156 } | |
157 return value; | |
158 } | |
159 | |
160 dynamic/*=T*/ fold/*<T>*/(var/*=T*/ initialValue, | |
161 dynamic/*=T*/ combine(var/*=T*/ previousValue, E element)) { | |
162 var value = initialValue; | |
163 for (E element in this) value = combine(value, element); | |
164 return value; | |
165 } | |
166 | |
167 bool every(bool f(E element)) { | |
168 for (E element in this) { | |
169 if (!f(element)) return false; | |
170 } | |
171 return true; | |
172 } | |
173 | |
174 String join([String separator = ""]) { | |
175 Iterator<E> iterator = this.iterator; | |
176 if (!iterator.moveNext()) return ""; | |
177 StringBuffer buffer = new StringBuffer(); | |
178 if (separator == null || separator == "") { | |
179 do { | |
180 buffer.write("${iterator.current}"); | |
181 } while (iterator.moveNext()); | |
182 } else { | |
183 buffer.write("${iterator.current}"); | |
184 while (iterator.moveNext()) { | |
185 buffer.write(separator); | |
186 buffer.write("${iterator.current}"); | |
187 } | |
188 } | |
189 return buffer.toString(); | |
190 } | |
191 | |
192 bool any(bool test(E element)) { | |
193 for (E element in this) { | |
194 if (test(element)) return true; | |
195 } | |
196 return false; | |
197 } | |
198 | |
199 Iterable<E> take(int n) { | |
200 return new TakeIterable<E>(this, n); | |
201 } | |
202 | |
203 Iterable<E> takeWhile(bool test(E value)) { | |
204 return new TakeWhileIterable<E>(this, test); | |
205 } | |
206 | |
207 Iterable<E> skip(int n) { | |
208 return new SkipIterable<E>(this, n); | |
209 } | |
210 | |
211 Iterable<E> skipWhile(bool test(E value)) { | |
212 return new SkipWhileIterable<E>(this, test); | |
213 } | |
214 | |
215 E get first { | |
216 Iterator<E> it = iterator; | |
217 if (!it.moveNext()) { | |
218 throw IterableElementError.noElement(); | |
219 } | |
220 return it.current; | |
221 } | |
222 | |
223 E get last { | |
224 Iterator<E> it = iterator; | |
225 if (!it.moveNext()) { | |
226 throw IterableElementError.noElement(); | |
227 } | |
228 E result; | |
229 do { | |
230 result = it.current; | |
231 } while(it.moveNext()); | |
232 return result; | |
233 } | |
234 | |
235 E firstWhere(bool test(E value), { E orElse() }) { | |
236 for (E element in this) { | |
237 if (test(element)) return element; | |
238 } | |
239 if (orElse != null) return orElse(); | |
240 throw IterableElementError.noElement(); | |
241 } | |
242 | |
243 E lastWhere(bool test(E value), { E orElse() }) { | |
244 E result = null; | |
245 bool foundMatching = false; | |
246 for (E element in this) { | |
247 if (test(element)) { | |
248 result = element; | |
249 foundMatching = true; | |
250 } | |
251 } | |
252 if (foundMatching) return result; | |
253 if (orElse != null) return orElse(); | |
254 throw IterableElementError.noElement(); | |
255 } | |
256 | |
257 E singleWhere(bool test(E value)) { | |
258 E result = null; | |
259 bool foundMatching = false; | |
260 for (E element in this) { | |
261 if (test(element)) { | |
262 if (foundMatching) { | |
263 throw IterableElementError.tooMany(); | |
264 } | |
265 result = element; | |
266 foundMatching = true; | |
267 } | |
268 } | |
269 if (foundMatching) return result; | |
270 throw IterableElementError.noElement(); | |
271 } | |
272 | |
273 E elementAt(int index) { | |
274 if (index is! int) throw new ArgumentError.notNull("index"); | |
275 RangeError.checkNotNegative(index, "index"); | |
276 int elementIndex = 0; | |
277 for (E element in this) { | |
278 if (index == elementIndex) return element; | |
279 elementIndex++; | |
280 } | |
281 throw new RangeError.index(index, this, "index", null, elementIndex); | |
282 } | |
283 } | |
284 | |
285 /** | |
286 * Base implementation of [Set]. | |
287 * | |
288 * This class provides a base implementation of a `Set` that depends only | |
289 * on the abstract members: [add], [contains], [lookup], [remove], | |
290 * [iterator], [length] and [toSet]. | |
291 * | |
292 * Some of the methods assume that `toSet` creates a modifiable set. | |
293 * If using this base class for an unmodifiable set, | |
294 * where `toSet` should return an unmodifiable set, | |
295 * it's necessary to reimplement | |
296 * [retainAll], [union], [intersection] and [difference]. | |
297 * | |
298 * Implementations of `Set` using this base should consider also implementing | |
299 * `clear` in constant time. The default implementation works by removing every | |
300 * element. | |
301 */ | |
302 abstract class SetBase<E> extends SetMixin<E> { | |
303 /** | |
304 * Convert a `Set` to a string as `{each, element, as, string}`. | |
305 * | |
306 * Handles circular references where converting one of the elements | |
307 * to a string ends up converting [set] to a string again. | |
308 */ | |
309 static String setToString(Set set) => | |
310 IterableBase.iterableToFullString(set, '{', '}'); | |
311 } | |
OLD | NEW |