OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 part of dart.core; | 5 part of dart.core; |
6 | 6 |
7 /** | 7 /** |
8 * An indexable collection of objects with a length. | 8 * An indexable collection of objects with a length. |
9 * | 9 * |
10 * Subclasses of this class implement different kinds of lists. | 10 * Subclasses of this class implement different kinds of lists. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
44 * temporarily and is restored before continuing the iteration, the iterator | 44 * temporarily and is restored before continuing the iteration, the iterator |
45 * does not detect it. | 45 * does not detect it. |
46 * | 46 * |
47 * It is generally not allowed to modify the list's length (adding or removing | 47 * It is generally not allowed to modify the list's length (adding or removing |
48 * elements) while an operation on the list is being performed, | 48 * elements) while an operation on the list is being performed, |
49 * for example during a call to [forEach] or [sort]. | 49 * for example during a call to [forEach] or [sort]. |
50 * Changing the list's length while it is being iterated, either by iterating it | 50 * Changing the list's length while it is being iterated, either by iterating it |
51 * directly or through iterating an [Iterable] that is backed by the list, will | 51 * directly or through iterating an [Iterable] that is backed by the list, will |
52 * break the iteration. | 52 * break the iteration. |
53 */ | 53 */ |
54 @SupportJsExtensionMethod() | 54 abstract class List<E> implements Iterable<E> { |
55 @JsPeerInterface(name: 'Array') | |
56 abstract class List<E> implements Iterable<E>, EfficientLength { | |
57 /** | 55 /** |
58 * Creates a list of the given length. | 56 * Creates a list of the given length. |
59 * | 57 * |
60 * The created list is fixed-length if [length] is provided. | 58 * The created list is fixed-length if [length] is provided. |
61 * | 59 * |
62 * List fixedLengthList = new List(3); | 60 * List fixedLengthList = new List(3); |
63 * fixedLengthList.length; // 3 | 61 * fixedLengthList.length; // 3 |
64 * fixedLengthList.length = 1; // Error | 62 * fixedLengthList.length = 1; // Error |
65 * | 63 * |
66 * The list has length 0 and is growable if [length] is omitted. | 64 * The list has length 0 and is growable if [length] is omitted. |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
103 * | 101 * |
104 * Creates a list with [length] positions and fills it with values created by | 102 * Creates a list with [length] positions and fills it with values created by |
105 * calling [generator] for each index in the range `0` .. `length - 1` | 103 * calling [generator] for each index in the range `0` .. `length - 1` |
106 * in increasing order. | 104 * in increasing order. |
107 * | 105 * |
108 * new List<int>.generate(3, (int index) => index * index); // [0, 1, 4] | 106 * new List<int>.generate(3, (int index) => index * index); // [0, 1, 4] |
109 * | 107 * |
110 * The created list is fixed-length unless [growable] is true. | 108 * The created list is fixed-length unless [growable] is true. |
111 */ | 109 */ |
112 factory List.generate(int length, E generator(int index), | 110 factory List.generate(int length, E generator(int index), |
113 { bool growable: true }) { | 111 { bool growable: true }) { |
114 List<E> result; | 112 List<E> result; |
115 if (growable) { | 113 if (growable) { |
116 result = <E>[]..length = length; | 114 result = <E>[]..length = length; |
117 } else { | 115 } else { |
118 result = new List<E>(length); | 116 result = new List<E>(length); |
119 } | 117 } |
120 for (int i = 0; i < length; i++) { | 118 for (int i = 0; i < length; i++) { |
121 result[i] = generator(i); | 119 result[i] = generator(i); |
122 } | 120 } |
123 return result; | 121 return result; |
124 } | 122 } |
125 | 123 |
126 checkMutable(reason) { | |
127 /* TODO(jacobr): implement. | |
128 if (this is !JSMutableArray) { | |
129 throw new UnsupportedError(reason); | |
130 } | |
131 * */ | |
132 } | |
133 | |
134 checkGrowable(reason) { | |
135 /* TODO(jacobr): implement | |
136 if (this is !JSExtendableArray) { | |
137 throw new UnsupportedError(reason); | |
138 } | |
139 * */ | |
140 } | |
141 | |
142 Iterable<E> where(bool f(E element)) { | |
143 return new IterableMixinWorkaround<E>().where(this, f); | |
144 } | |
145 | |
146 Iterable expand(Iterable f(E element)) { | |
147 return IterableMixinWorkaround.expand(this, f); | |
148 } | |
149 | |
150 void forEach(void f(E element)) { | |
151 int length = this.length; | |
152 for (int i = 0; i < length; i++) { | |
153 f(JS('', '#[#]', this, i)); | |
154 if (length != this.length) { | |
155 throw new ConcurrentModificationError(this); | |
156 } | |
157 } | |
158 } | |
159 | |
160 Iterable map(f(E element)) { | |
161 return IterableMixinWorkaround.mapList(this, f); | |
162 } | |
163 | |
164 String join([String separator = ""]) { | |
165 var list = new List(this.length); | |
166 for (int i = 0; i < this.length; i++) { | |
167 list[i] = "${this[i]}"; | |
168 } | |
169 return JS('String', "#.join(#)", list, separator); | |
170 } | |
171 | |
172 Iterable<E> take(int n) { | |
173 return new IterableMixinWorkaround<E>().takeList(this, n); | |
174 } | |
175 | |
176 Iterable<E> takeWhile(bool test(E value)) { | |
177 return new IterableMixinWorkaround<E>().takeWhile(this, test); | |
178 } | |
179 | |
180 Iterable<E> skip(int n) { | |
181 return new IterableMixinWorkaround<E>().skipList(this, n); | |
182 } | |
183 | |
184 Iterable<E> skipWhile(bool test(E value)) { | |
185 return new IterableMixinWorkaround<E>().skipWhile(this, test); | |
186 } | |
187 | |
188 E reduce(E combine(E value, E element)) { | |
189 return IterableMixinWorkaround.reduce(this, combine); | |
190 } | |
191 | |
192 fold(initialValue, combine(previousValue, E element)) { | |
193 return IterableMixinWorkaround.fold(this, initialValue, combine); | |
194 } | |
195 | |
196 E firstWhere(bool test(E value), {E orElse()}) { | |
197 return IterableMixinWorkaround.firstWhere(this, test, orElse); | |
198 } | |
199 | |
200 E lastWhere(bool test(E value), {E orElse()}) { | |
201 return IterableMixinWorkaround.lastWhereList(this, test, orElse); | |
202 } | |
203 | |
204 E singleWhere(bool test(E value)) { | |
205 return IterableMixinWorkaround.singleWhere(this, test); | |
206 } | |
207 | |
208 E elementAt(int index) { | |
209 return this[index]; | |
210 } | |
211 | |
212 E get first { | |
213 if (length > 0) return this[0]; | |
214 throw new StateError("No elements"); | |
215 } | |
216 | |
217 E get last { | |
218 if (length > 0) return this[length - 1]; | |
219 throw new StateError("No elements"); | |
220 } | |
221 | |
222 E get single { | |
223 if (length == 1) return this[0]; | |
224 if (length == 0) throw new StateError("No elements"); | |
225 throw new StateError("More than one element"); | |
226 } | |
227 | |
228 bool any(bool f(E element)) => IterableMixinWorkaround.any(this, f); | |
229 | |
230 bool every(bool f(E element)) => IterableMixinWorkaround.every(this, f); | |
231 | |
232 bool contains(Object other) { | |
233 for (int i = 0; i < length; i++) { | |
234 if (this[i] == other) return true; | |
235 } | |
236 return false; | |
237 } | |
238 | |
239 bool get isEmpty => length == 0; | |
240 | |
241 bool get isNotEmpty => !isEmpty; | |
242 | |
243 String toString() => ListBase.listToString(this); | |
244 | |
245 List<E> toList({ bool growable: true }) { | |
246 // TODO(vsm): Enforce growable / non-growable. | |
247 // See: https://github.com/dart-lang/dev_compiler/issues/175 | |
248 return JS('', 'dart.setType(#.slice(), core.List\$(#))', this, E); | |
249 } | |
250 | |
251 Set<E> toSet() => new Set<E>.from(this); | |
252 | |
253 Iterator<E> get iterator => new ListIterator<E>(this); | |
254 | |
255 int get hashCode => Primitives.objectHashCode(this); | |
256 | |
257 // BORDER XXXX | |
258 | |
259 /** | 124 /** |
260 * Returns the object at the given [index] in the list | 125 * Returns the object at the given [index] in the list |
261 * or throws a [RangeError] if [index] is out of bounds. | 126 * or throws a [RangeError] if [index] is out of bounds. |
262 */ | 127 */ |
263 E operator [](int index) { | 128 E operator [](int index); |
264 if (index is !int) throw new ArgumentError(index); | |
265 if (index >= length || index < 0) throw new RangeError.value(index); | |
266 return JS('var', '#[#]', this, index); | |
267 } | |
268 | 129 |
269 /** | 130 /** |
270 * Sets the value at the given [index] in the list to [value] | 131 * Sets the value at the given [index] in the list to [value] |
271 * or throws a [RangeError] if [index] is out of bounds. | 132 * or throws a [RangeError] if [index] is out of bounds. |
272 */ | 133 */ |
273 void operator []=(int index, E value) { | 134 void operator []=(int index, E value); |
274 checkMutable('indexed set'); | |
275 if (index is !int) throw new ArgumentError(index); | |
276 if (index >= length || index < 0) throw new RangeError.value(index); | |
277 JS('void', r'#[#] = #', this, index, value); | |
278 } | |
279 | 135 |
280 /** | 136 /** |
281 * Returns the number of objects in this list. | 137 * Returns the number of objects in this list. |
282 * | 138 * |
283 * The valid indices for a list are `0` through `length - 1`. | 139 * The valid indices for a list are `0` through `length - 1`. |
284 */ | 140 */ |
285 int get length => JS('JSUInt32', r'#.length', this); | 141 int get length; |
286 | 142 |
287 /** | 143 /** |
288 * Changes the length of this list. | 144 * Changes the length of this list. |
289 * | 145 * |
290 * If [newLength] is greater than | 146 * If [newLength] is greater than |
291 * the current length, entries are initialized to [:null:]. | 147 * the current length, entries are initialized to [:null:]. |
292 * | 148 * |
293 * Throws an [UnsupportedError] if the list is fixed-length. | 149 * Throws an [UnsupportedError] if the list is fixed-length. |
294 */ | 150 */ |
295 void set length(int newLength) { | 151 void set length(int newLength); |
296 if (newLength is !int) throw new ArgumentError(newLength); | |
297 if (newLength < 0) throw new RangeError.value(newLength); | |
298 checkGrowable('set length'); | |
299 JS('void', r'#.length = #', this, newLength); | |
300 } | |
301 | 152 |
302 /** | 153 /** |
303 * Adds [value] to the end of this list, | 154 * Adds [value] to the end of this list, |
304 * extending the length by one. | 155 * extending the length by one. |
305 * | 156 * |
306 * Throws an [UnsupportedError] if the list is fixed-length. | 157 * Throws an [UnsupportedError] if the list is fixed-length. |
307 */ | 158 */ |
308 void add(E value) { | 159 void add(E value); |
309 checkGrowable('add'); | |
310 JS('void', r'#.push(#)', this, value); | |
311 } | |
312 | 160 |
313 /** | 161 /** |
314 * Appends all objects of [iterable] to the end of this list. | 162 * Appends all objects of [iterable] to the end of this list. |
315 * | 163 * |
316 * Extends the length of the list by the number of objects in [iterable]. | 164 * Extends the length of the list by the number of objects in [iterable]. |
317 * Throws an [UnsupportedError] if this list is fixed-length. | 165 * Throws an [UnsupportedError] if this list is fixed-length. |
318 */ | 166 */ |
319 void addAll(Iterable<E> iterable) { | 167 void addAll(Iterable<E> iterable); |
320 for (E e in iterable) { | |
321 this.add(e); | |
322 } | |
323 } | |
324 | 168 |
325 /** | 169 /** |
326 * Returns an [Iterable] of the objects in this list in reverse order. | 170 * Returns an [Iterable] of the objects in this list in reverse order. |
327 */ | 171 */ |
328 Iterable<E> get reversed => | 172 Iterable<E> get reversed; |
329 new IterableMixinWorkaround<E>().reversedList(this); | |
330 | 173 |
331 /** | 174 /** |
332 * Sorts this list according to the order specified by the [compare] function. | 175 * Sorts this list according to the order specified by the [compare] function. |
333 * | 176 * |
334 * The [compare] function must act as a [Comparator]. | 177 * The [compare] function must act as a [Comparator]. |
335 | |
336 * List<String> numbers = ['one', 'two', 'three', 'four']; | 178 * List<String> numbers = ['one', 'two', 'three', 'four']; |
337 * // Sort from shortest to longest. | 179 * // Sort from shortest to longest. |
338 * numbers.sort((x, y) => x.length.compareTo(y.length)); | 180 * numbers.sort((x, y) => x.length.compareTo(y.length)); |
339 * numbers.join(', '); // 'one, two, four, three' | 181 * numbers.join(', '); // 'one, two, four, three' |
340 * | 182 * |
341 * The default List implementations use [Comparable.compare] if | 183 * The default List implementations use [Comparable.compare] if |
342 * [compare] is omitted. | 184 * [compare] is omitted. |
343 * | 185 * |
344 * List<int> nums = [13, 2, -11]; | 186 * List<int> nums = [13, 2, -11]; |
345 * nums.sort(); | 187 * nums.sort(); |
346 nums.join(', '); // '-11, 2, 13' | 188 nums.join(', '); // '-11, 2, 13' |
347 */ | 189 */ |
348 void sort([int compare(E a, E b)]) { | 190 void sort([int compare(E a, E b)]); |
349 checkMutable('sort'); | |
350 IterableMixinWorkaround.sortList(this, compare); | |
351 } | |
352 | 191 |
353 /** | 192 /** |
354 * Shuffles the elements of this list randomly. | 193 * Shuffles the elements of this list randomly. |
355 */ | 194 */ |
356 void shuffle([Random random]) { | 195 void shuffle([Random random]); |
357 IterableMixinWorkaround.shuffleList(this, random); | |
358 } | |
359 | 196 |
360 /** | 197 /** |
361 * Returns the first index of [element] in this list. | 198 * Returns the first index of [element] in this list. |
362 * | 199 * |
363 * Searches the list from index [start] to the end of the list. | 200 * Searches the list from index [start] to the end of the list. |
364 * The first time an object [:o:] is encountered so that [:o == element:], | 201 * The first time an object [:o:] is encountered so that [:o == element:], |
365 * the index of [:o:] is returned. | 202 * the index of [:o:] is returned. |
366 * | 203 * |
367 * List<String> notes = ['do', 're', 'mi', 're']; | 204 * List<String> notes = ['do', 're', 'mi', 're']; |
368 * notes.indexOf('re'); // 1 | 205 * notes.indexOf('re'); // 1 |
369 * notes.indexOf('re', 2); // 3 | 206 * notes.indexOf('re', 2); // 3 |
370 * | 207 * |
371 * Returns -1 if [element] is not found. | 208 * Returns -1 if [element] is not found. |
372 * | 209 * |
373 * notes.indexOf('fa'); // -1 | 210 * notes.indexOf('fa'); // -1 |
374 */ | 211 */ |
375 int indexOf(E element, [int start = 0]) { | 212 int indexOf(E element, [int start = 0]); |
376 return IterableMixinWorkaround.indexOfList(this, element, start); | |
377 } | |
378 | 213 |
379 /** | 214 /** |
380 * Returns the last index of [element] in this list. | 215 * Returns the last index of [element] in this list. |
381 * | 216 * |
382 * Searches the list backwards from index [start] to 0. | 217 * Searches the list backwards from index [start] to 0. |
383 * | 218 * |
384 * The first time an object [:o:] is encountered so that [:o == element:], | 219 * The first time an object [:o:] is encountered so that [:o == element:], |
385 * the index of [:o:] is returned. | 220 * the index of [:o:] is returned. |
386 * | 221 * |
387 * List<String> notes = ['do', 're', 'mi', 're']; | 222 * List<String> notes = ['do', 're', 'mi', 're']; |
388 * notes.lastIndexOf('re', 2); // 1 | 223 * notes.lastIndexOf('re', 2); // 1 |
389 * | 224 * |
390 * If [start] is not provided, this method searches from the end of the | 225 * If [start] is not provided, this method searches from the end of the |
391 * list./Returns | 226 * list./Returns |
392 * | 227 * |
393 * notes.lastIndexOf('re'); // 3 | 228 * notes.lastIndexOf('re'); // 3 |
394 * | 229 * |
395 * Returns -1 if [element] is not found. | 230 * Returns -1 if [element] is not found. |
396 * | 231 * |
397 * notes.lastIndexOf('fa'); // -1 | 232 * notes.lastIndexOf('fa'); // -1 |
398 */ | 233 */ |
399 int lastIndexOf(E element, [int start]) { | 234 int lastIndexOf(E element, [int start]); |
400 return IterableMixinWorkaround.lastIndexOfList(this, element, start); | |
401 } | |
402 | 235 |
403 /** | 236 /** |
404 * Removes all objects from this list; | 237 * Removes all objects from this list; |
405 * the length of the list becomes zero. | 238 * the length of the list becomes zero. |
406 * | 239 * |
407 * Throws an [UnsupportedError], and retains all objects, if this | 240 * Throws an [UnsupportedError], and retains all objects, if this |
408 * is a fixed-length list. | 241 * is a fixed-length list. |
409 */ | 242 */ |
410 void clear() { | 243 void clear(); |
411 length = 0; | |
412 } | |
413 | 244 |
414 /** | 245 /** |
415 * Inserts the object at position [index] in this list. | 246 * Inserts the object at position [index] in this list. |
416 * | 247 * |
417 * This increases the length of the list by one and shifts all objects | 248 * This increases the length of the list by one and shifts all objects |
418 * at or after the index towards the end of the list. | 249 * at or after the index towards the end of the list. |
419 * | 250 * |
420 * An error occurs if the [index] is less than 0 or greater than length. | 251 * An error occurs if the [index] is less than 0 or greater than length. |
421 * An [UnsupportedError] occurs if the list is fixed-length. | 252 * An [UnsupportedError] occurs if the list is fixed-length. |
422 */ | 253 */ |
423 void insert(int index, E element) { | 254 void insert(int index, E element); |
424 if (index is !int) throw new ArgumentError(index); | |
425 if (index < 0 || index > length) { | |
426 throw new RangeError.value(index); | |
427 } | |
428 checkGrowable('insert'); | |
429 JS('void', r'#.splice(#, 0, #)', this, index, element); | |
430 } | |
431 | |
432 | 255 |
433 /** | 256 /** |
434 * Inserts all objects of [iterable] at position [index] in this list. | 257 * Inserts all objects of [iterable] at position [index] in this list. |
435 * | 258 * |
436 * This increases the length of the list by the length of [iterable] and | 259 * This increases the length of the list by the length of [iterable] and |
437 * shifts all later objects towards the end of the list. | 260 * shifts all later objects towards the end of the list. |
438 * | 261 * |
439 * An error occurs if the [index] is less than 0 or greater than length. | 262 * An error occurs if the [index] is less than 0 or greater than length. |
440 * An [UnsupportedError] occurs if the list is fixed-length. | 263 * An [UnsupportedError] occurs if the list is fixed-length. |
441 */ | 264 */ |
442 void insertAll(int index, Iterable<E> iterable) { | 265 void insertAll(int index, Iterable<E> iterable); |
443 checkGrowable('insertAll'); | |
444 IterableMixinWorkaround.insertAllList(this, index, iterable); | |
445 } | |
446 | 266 |
447 /** | 267 /** |
448 * Overwrites objects of `this` with the objects of [iterable], starting | 268 * Overwrites objects of `this` with the objects of [iterable], starting |
449 * at position [index] in this list. | 269 * at position [index] in this list. |
450 * | 270 * |
451 * List<String> list = ['a', 'b', 'c']; | 271 * List<String> list = ['a', 'b', 'c']; |
452 * list.setAll(1, ['bee', 'sea']); | 272 * list.setAll(1, ['bee', 'sea']); |
453 * list.join(', '); // 'a, bee, sea' | 273 * list.join(', '); // 'a, bee, sea' |
454 * | 274 * |
455 * This operation does not increase the length of `this`. | 275 * This operation does not increase the length of `this`. |
456 * | 276 * |
457 * The [index] must be non-negative and no greater than [length]. | 277 * The [index] must be non-negative and no greater than [length]. |
458 * | 278 * |
459 * The [iterable] must not have more elements than what can fit from [index] | 279 * The [iterable] must not have more elements than what can fit from [index] |
460 * to [length]. | 280 * to [length]. |
461 * | 281 * |
462 * If `iterable` is based on this list, its values may change /during/ the | 282 * If `iterable` is based on this list, its values may change /during/ the |
463 * `setAll` operation. | 283 * `setAll` operation. |
464 */ | 284 */ |
465 void setAll(int index, Iterable<E> iterable) { | 285 void setAll(int index, Iterable<E> iterable); |
466 checkMutable('setAll'); | |
467 IterableMixinWorkaround.setAllList(this, index, iterable); | |
468 } | |
469 | 286 |
470 /** | 287 /** |
471 * Removes the first occurence of [value] from this list. | 288 * Removes the first occurence of [value] from this list. |
472 * | 289 * |
473 * Returns true if [value] was in the list, false otherwise. | 290 * Returns true if [value] was in the list, false otherwise. |
474 * | 291 * |
475 * List<String> parts = ['head', 'shoulders', 'knees', 'toes']; | 292 * List<String> parts = ['head', 'shoulders', 'knees', 'toes']; |
476 * parts.remove('head'); // true | 293 * parts.remove('head'); // true |
477 * parts.join(', '); // 'shoulders, knees, toes' | 294 * parts.join(', '); // 'shoulders, knees, toes' |
478 * | 295 * |
479 * The method has no effect if [value] was not in the list. | 296 * The method has no effect if [value] was not in the list. |
480 * | 297 * |
481 * // Note: 'head' has already been removed. | 298 * // Note: 'head' has already been removed. |
482 * parts.remove('head'); // false | 299 * parts.remove('head'); // false |
483 * parts.join(', '); // 'shoulders, knees, toes' | 300 * parts.join(', '); // 'shoulders, knees, toes' |
484 * | 301 * |
485 * An [UnsupportedError] occurs if the list is fixed-length. | 302 * An [UnsupportedError] occurs if the list is fixed-length. |
486 */ | 303 */ |
487 bool remove(Object element) { | 304 bool remove(Object value); |
488 checkGrowable('remove'); | |
489 for (int i = 0; i < this.length; i++) { | |
490 if (this[i] == value) { | |
491 JS('var', r'#.splice(#, 1)', this, i); | |
492 return true; | |
493 } | |
494 } | |
495 return false; | |
496 } | |
497 | 305 |
498 /** | 306 /** |
499 * Removes the object at position [index] from this list. | 307 * Removes the object at position [index] from this list. |
500 * | 308 * |
501 * This method reduces the length of `this` by one and moves all later objects | 309 * This method reduces the length of `this` by one and moves all later objects |
502 * down by one position. | 310 * down by one position. |
503 * | 311 * |
504 * Returns the removed object. | 312 * Returns the removed object. |
505 * | 313 * |
506 * The [index] must be in the range `0 ≤ index < length`. | 314 * The [index] must be in the range `0 ≤ index < length`. |
507 * | 315 * |
508 * Throws an [UnsupportedError] if this is a fixed-length list. In that case | 316 * Throws an [UnsupportedError] if this is a fixed-length list. In that case |
509 * the list is not modified. | 317 * the list is not modified. |
510 */ | 318 */ |
511 E removeAt(int index) { | 319 E removeAt(int index); |
512 if (index is !int) throw new ArgumentError(index); | |
513 if (index < 0 || index >= length) { | |
514 throw new RangeError.value(index); | |
515 } | |
516 checkGrowable('removeAt'); | |
517 return JS('var', r'#.splice(#, 1)[0]', this, index); | |
518 } | |
519 | 320 |
520 /** | 321 /** |
521 * Pops and returns the last object in this list. | 322 * Pops and returns the last object in this list. |
522 * | 323 * |
523 * Throws an [UnsupportedError] if this is a fixed-length list. | 324 * Throws an [UnsupportedError] if this is a fixed-length list. |
524 */ | 325 */ |
525 E removeLast() { | 326 E removeLast(); |
526 checkGrowable('removeLast'); | |
527 if (length == 0) throw new RangeError.value(-1); | |
528 return JS('var', r'#.pop()', this); | |
529 } | |
530 | 327 |
531 /** | 328 /** |
532 * Removes all objects from this list that satisfy [test]. | 329 * Removes all objects from this list that satisfy [test]. |
533 * | 330 * |
534 * An object [:o:] satisfies [test] if [:test(o):] is true. | 331 * An object [:o:] satisfies [test] if [:test(o):] is true. |
535 * | 332 * |
536 * List<String> numbers = ['one', 'two', 'three', 'four']; | 333 * List<String> numbers = ['one', 'two', 'three', 'four']; |
537 * numbers.removeWhere((item) => item.length == 3); | 334 * numbers.removeWhere((item) => item.length == 3); |
538 * numbers.join(', '); // 'three, four' | 335 * numbers.join(', '); // 'three, four' |
539 * | 336 * |
540 * Throws an [UnsupportedError] if this is a fixed-length list. | 337 * Throws an [UnsupportedError] if this is a fixed-length list. |
541 */ | 338 */ |
542 void removeWhere(bool test(E element)) { | 339 void removeWhere(bool test(E element)); |
543 // This could, and should, be optimized. | |
544 IterableMixinWorkaround.removeWhereList(this, test); | |
545 } | |
546 | |
547 | 340 |
548 /** | 341 /** |
549 * Removes all objects from this list that fail to satisfy [test]. | 342 * Removes all objects from this list that fail to satisfy [test]. |
550 * | 343 * |
551 * An object [:o:] satisfies [test] if [:test(o):] is true. | 344 * An object [:o:] satisfies [test] if [:test(o):] is true. |
552 * | 345 * |
553 * List<String> numbers = ['one', 'two', 'three', 'four']; | 346 * List<String> numbers = ['one', 'two', 'three', 'four']; |
554 * numbers.retainWhere((item) => item.length == 3); | 347 * numbers.retainWhere((item) => item.length == 3); |
555 * numbers.join(', '); // 'one, two' | 348 * numbers.join(', '); // 'one, two' |
556 * | 349 * |
557 * Throws an [UnsupportedError] if this is a fixed-length list. | 350 * Throws an [UnsupportedError] if this is a fixed-length list. |
558 */ | 351 */ |
559 void retainWhere(bool test(E element)) { | 352 void retainWhere(bool test(E element)); |
560 IterableMixinWorkaround.removeWhereList(this, | |
561 (E element) => !test(element)); | |
562 } | |
563 | 353 |
564 /** | 354 /** |
565 * Returns a new list containing the objects from [start] inclusive to [end] | 355 * Returns a new list containing the objects from [start] inclusive to [end] |
566 * exclusive. | 356 * exclusive. |
567 * | 357 * |
568 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; | 358 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; |
569 * colors.sublist(1, 3); // ['green', 'blue'] | 359 * colors.sublist(1, 3); // ['green', 'blue'] |
570 * | 360 * |
571 * If [end] is omitted, the [length] of `this` is used. | 361 * If [end] is omitted, the [length] of `this` is used. |
572 * | 362 * |
573 * colors.sublist(1); // ['green', 'blue', 'orange', 'pink'] | 363 * colors.sublist(1); // ['green', 'blue', 'orange', 'pink'] |
574 * | 364 * |
575 * An error occurs if [start] is outside the range `0` .. `length` or if | 365 * An error occurs if [start] is outside the range `0` .. `length` or if |
576 * [end] is outside the range `start` .. `length`. | 366 * [end] is outside the range `start` .. `length`. |
577 */ | 367 */ |
578 List<E> sublist(int start, [int end]) { | 368 List<E> sublist(int start, [int end]); |
579 checkNull(start); // TODO(ahe): This is not specified but co19 tests it. | |
580 if (start is !int) throw new ArgumentError(start); | |
581 if (start < 0 || start > length) { | |
582 throw new RangeError.range(start, 0, length); | |
583 } | |
584 if (end == null) { | |
585 end = length; | |
586 } else { | |
587 if (end is !int) throw new ArgumentError(end); | |
588 if (end < start || end > length) { | |
589 throw new RangeError.range(end, start, length); | |
590 } | |
591 } | |
592 if (start == end) return <E>[]; | |
593 return new JSArray<E>.markGrowable( | |
594 JS('', r'#.slice(#, #)', this, start, end)); | |
595 } | |
596 | 369 |
597 /** | 370 /** |
598 * Returns an [Iterable] that iterates over the objects in the range | 371 * Returns an [Iterable] that iterates over the objects in the range |
599 * [start] inclusive to [end] exclusive. | 372 * [start] inclusive to [end] exclusive. |
600 * | 373 * |
601 * An error occurs if [end] is before [start]. | 374 * An error occurs if [end] is before [start]. |
602 * | 375 * |
603 * An error occurs if the [start] and [end] are not valid ranges at the time | 376 * An error occurs if the [start] and [end] are not valid ranges at the time |
604 * of the call to this method. The returned [Iterable] behaves like | 377 * of the call to this method. The returned [Iterable] behaves like |
605 * `skip(start).take(end - start)`. That is, it does not throw exceptions | 378 * `skip(start).take(end - start)`. That is, it does not throw exceptions |
606 * if `this` changes size. | 379 * if `this` changes size. |
607 * | 380 * |
608 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; | 381 * List<String> colors = ['red', 'green', 'blue', 'orange', 'pink']; |
609 * Iterable<String> range = colors.getRange(1, 4); | 382 * Iterable<String> range = colors.getRange(1, 4); |
610 * range.join(', '); // 'green, blue, orange' | 383 * range.join(', '); // 'green, blue, orange' |
611 * colors.length = 3; | 384 * colors.length = 3; |
612 * range.join(', '); // 'green, blue' | 385 * range.join(', '); // 'green, blue' |
613 */ | 386 */ |
614 Iterable<E> getRange(int start, int end) { | 387 Iterable<E> getRange(int start, int end); |
615 return new IterableMixinWorkaround<E>().getRangeList(this, start, end); | |
616 } | |
617 | |
618 | 388 |
619 /** | 389 /** |
620 * Copies the objects of [iterable], skipping [skipCount] objects first, | 390 * Copies the objects of [iterable], skipping [skipCount] objects first, |
621 * into the range [start], inclusive, to [end], exclusive, of the list. | 391 * into the range [start], inclusive, to [end], exclusive, of the list. |
622 * | 392 * |
623 * List<int> list1 = [1, 2, 3, 4]; | 393 * List<int> list1 = [1, 2, 3, 4]; |
624 * List<int> list2 = [5, 6, 7, 8, 9]; | 394 * List<int> list2 = [5, 6, 7, 8, 9]; |
625 * // Copies the 4th and 5th items in list2 as the 2nd and 3rd items | 395 * // Copies the 4th and 5th items in list2 as the 2nd and 3rd items |
626 * // of list1. | 396 * // of list1. |
627 * list1.setRange(1, 3, list2, 3); | 397 * list1.setRange(1, 3, list2, 3); |
628 * list1.join(', '); // '1, 8, 9, 4' | 398 * list1.join(', '); // '1, 8, 9, 4' |
629 * | 399 * |
630 * The [start] and [end] indices must satisfy `0 ≤ start ≤ end ≤ length`. | 400 * The [start] and [end] indices must satisfy `0 ≤ start ≤ end ≤ length`. |
631 * If [start] equals [end], this method has no effect. | 401 * If [start] equals [end], this method has no effect. |
632 * | 402 * |
633 * The [iterable] must have enough objects to fill the range from `start` | 403 * The [iterable] must have enough objects to fill the range from `start` |
634 * to `end` after skipping [skipCount] objects. | 404 * to `end` after skipping [skipCount] objects. |
635 * | 405 * |
636 * If `iterable` is this list, the operation will copy the elements originally | 406 * If `iterable` is this list, the operation will copy the elements originally |
637 * in the range from `skipCount` to `skipCount + (end - start)` to the | 407 * in the range from `skipCount` to `skipCount + (end - start)` to the |
638 * range `start` to `end`, even if the two ranges overlap. | 408 * range `start` to `end`, even if the two ranges overlap. |
639 * | 409 * |
640 * If `iterable` depends on this list in some other way, no guarantees are | 410 * If `iterable` depends on this list in some other way, no guarantees are |
641 * made. | 411 * made. |
642 */ | 412 */ |
643 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { | 413 void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]); |
644 checkMutable('set range'); | |
645 IterableMixinWorkaround.setRangeList(this, start, end, iterable, skipCount); | |
646 } | |
647 | 414 |
648 /** | 415 /** |
649 * Removes the objects in the range [start] inclusive to [end] exclusive. | 416 * Removes the objects in the range [start] inclusive to [end] exclusive. |
650 * | 417 * |
651 * The [start] and [end] indices must be in the range | 418 * The [start] and [end] indices must be in the range |
652 * `0 ≤ index ≤ length`, and `start ≤ end`. | 419 * `0 ≤ index ≤ length`, and `start ≤ end`. |
653 * | 420 * |
654 * Throws an [UnsupportedError] if this is a fixed-length list. In that case | 421 * Throws an [UnsupportedError] if this is a fixed-length list. In that case |
655 * the list is not modified. | 422 * the list is not modified. |
656 */ | 423 */ |
657 void removeRange(int start, int end) { | 424 void removeRange(int start, int end); |
658 checkGrowable('removeRange'); | |
659 int receiverLength = this.length; | |
660 if (start < 0 || start > receiverLength) { | |
661 throw new RangeError.range(start, 0, receiverLength); | |
662 } | |
663 if (end < start || end > receiverLength) { | |
664 throw new RangeError.range(end, start, receiverLength); | |
665 } | |
666 Lists.copy(this, | |
667 end, | |
668 this, | |
669 start, | |
670 receiverLength - end); | |
671 this.length = receiverLength - (end - start); | |
672 } | |
673 | 425 |
674 /** | 426 /** |
675 * Sets the objects in the range [start] inclusive to [end] exclusive | 427 * Sets the objects in the range [start] inclusive to [end] exclusive |
676 * to the given [fillValue]. | 428 * to the given [fillValue]. |
677 * | 429 * |
678 * An error occurs if [start]..[end] is not a valid range for `this`. | 430 * An error occurs if [start]..[end] is not a valid range for `this`. |
679 */ | 431 */ |
680 void fillRange(int start, int end, [E fillValue]) { | 432 void fillRange(int start, int end, [E fillValue]); |
681 checkMutable('fill range'); | |
682 IterableMixinWorkaround.fillRangeList(this, start, end, fillValue); | |
683 } | |
684 | 433 |
685 /** | 434 /** |
686 * Removes the objects in the range [start] inclusive to [end] exclusive | 435 * Removes the objects in the range [start] inclusive to [end] exclusive |
687 * and inserts the contents of [replacement] in its place. | 436 * and inserts the contents of [replacement] in its place. |
688 * | 437 * |
689 * List<int> list = [1, 2, 3, 4, 5]; | 438 * List<int> list = [1, 2, 3, 4, 5]; |
690 * list.replaceRange(1, 4, [6, 7]); | 439 * list.replaceRange(1, 4, [6, 7]); |
691 * list.join(', '); // '1, 6, 7, 5' | 440 * list.join(', '); // '1, 6, 7, 5' |
692 * | 441 * |
693 * An error occurs if [start]..[end] is not a valid range for `this`. | 442 * An error occurs if [start]..[end] is not a valid range for `this`. |
| 443 * |
| 444 * This method does not work on fixed-length lists, even when [replacement] |
| 445 * has the same number of elements as the replaced range. In that case use |
| 446 * [setRange] instead. |
694 */ | 447 */ |
695 void replaceRange(int start, int end, Iterable<E> replacement) { | 448 void replaceRange(int start, int end, Iterable<E> replacement); |
696 checkGrowable('removeRange'); | |
697 IterableMixinWorkaround.replaceRangeList(this, start, end, replacement); | |
698 } | |
699 | 449 |
700 /** | 450 /** |
701 * Returns an unmodifiable [Map] view of `this`. | 451 * Returns an unmodifiable [Map] view of `this`. |
702 * | 452 * |
703 * The map uses the indices of this list as keys and the corresponding objects | 453 * The map uses the indices of this list as keys and the corresponding objects |
704 * as values. The `Map.keys` [Iterable] iterates the indices of this list | 454 * as values. The `Map.keys` [Iterable] iterates the indices of this list |
705 * in numerical order. | 455 * in numerical order. |
706 * | 456 * |
707 * List<String> words = ['fee', 'fi', 'fo', 'fum']; | 457 * List<String> words = ['fee', 'fi', 'fo', 'fum']; |
708 * Map<int, String> map = words.asMap(); | 458 * Map<int, String> map = words.asMap(); |
709 * map[0] + map[1]; // 'feefi'; | 459 * map[0] + map[1]; // 'feefi'; |
710 * map.keys.toList(); // [0, 1, 2, 3] | 460 * map.keys.toList(); // [0, 1, 2, 3] |
711 */ | 461 */ |
712 Map<int, E> asMap() { | 462 Map<int, E> asMap(); |
713 return new IterableMixinWorkaround<E>().asMapList(this); | |
714 } | |
715 } | 463 } |
OLD | NEW |