| 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 |