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

Side by Side Diff: sdk/lib/collection/queue.dart

Issue 12217061: Add ListQueue and make it the default Queue. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed comments. Created 7 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/corelib/queue_test.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) 2011, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.collection; 5 part of dart.collection;
6 6
7 /** 7 /**
8 * A [Queue] is a collection that can be manipulated at both ends. One 8 * A [Queue] is a collection that can be manipulated at both ends. One
9 * can iterate over the elements of a queue through [forEach] or with 9 * can iterate over the elements of a queue through [forEach] or with
10 * an [Iterator]. 10 * an [Iterator].
11 */ 11 */
12 abstract class Queue<E> extends Collection<E> { 12 abstract class Queue<E> implements Collection<E> {
13 13
14 /** 14 /**
15 * Creates a queue. 15 * Creates a queue.
16 */ 16 */
17 factory Queue() => new DoubleLinkedQueue<E>(); 17 factory Queue() => new ListQueue<E>();
18 18
19 /** 19 /**
20 * Creates a queue with the elements of [other]. The order in 20 * Creates a queue with the elements of [other]. The order in
21 * the queue will be the order provided by the iterator of [other]. 21 * the queue will be the order provided by the iterator of [other].
22 */ 22 */
23 factory Queue.from(Iterable<E> other) => new DoubleLinkedQueue<E>.from(other); 23 factory Queue.from(Iterable<E> other) => new ListQueue<E>.from(other);
24 24
25 /** 25 /**
26 * Removes and returns the first element of this queue. Throws an 26 * Removes and returns the first element of this queue. Throws an
27 * [StateError] exception if this queue is empty. 27 * [StateError] exception if this queue is empty.
28 */ 28 */
29 E removeFirst(); 29 E removeFirst();
30 30
31 /** 31 /**
32 * Removes and returns the last element of the queue. Throws an 32 * Removes and returns the last element of the queue. Throws an
33 * [StateError] exception if this queue is empty. 33 * [StateError] exception if this queue is empty.
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 // This setter is unreachable. 147 // This setter is unreachable.
148 assert(false); 148 assert(false);
149 } 149 }
150 150
151 E get element { 151 E get element {
152 throw new StateError("Empty queue"); 152 throw new StateError("Empty queue");
153 } 153 }
154 } 154 }
155 155
156 /** 156 /**
157 * Implementation of a double linked list that box list elements into 157 * A [Queue] implementation based on a double-linked list.
158 * DoubleLinkedQueueEntry objects.
159 * 158 *
160 * WARNING: This class is temporary located in dart:core. It'll be removed 159 * Allows constant time add, remove-at-ends and peek operations.
161 * at some point in the near future. 160 *
161 * Can do [removeAll] and [retainAll] in linear time.
162 */ 162 */
163 class DoubleLinkedQueue<E> extends Collection<E> implements Queue<E> { 163 class DoubleLinkedQueue<E> extends Collection<E> implements Queue<E> {
164 _DoubleLinkedQueueEntrySentinel<E> _sentinel; 164 _DoubleLinkedQueueEntrySentinel<E> _sentinel;
165 165
166 DoubleLinkedQueue() { 166 DoubleLinkedQueue() {
167 _sentinel = new _DoubleLinkedQueueEntrySentinel<E>(); 167 _sentinel = new _DoubleLinkedQueueEntrySentinel<E>();
168 } 168 }
169 169
170 factory DoubleLinkedQueue.from(Iterable<E> other) { 170 factory DoubleLinkedQueue.from(Iterable<E> other) {
171 Queue<E> list = new DoubleLinkedQueue(); 171 Queue<E> list = new DoubleLinkedQueue();
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 _current = null; 310 _current = null;
311 _sentinel = null; 311 _sentinel = null;
312 return false; 312 return false;
313 } 313 }
314 _current = _currentEntry.element; 314 _current = _currentEntry.element;
315 return true; 315 return true;
316 } 316 }
317 317
318 E get current => _current; 318 E get current => _current;
319 } 319 }
320
321 /**
322 * List based [Queue].
323 *
324 * Keeps a cyclic buffer of elements, and grows to a larger buffer when
325 * it fills up. This guarantees constant time peek and remove operations, and
326 * amortized constant time add operations.
327 *
328 * The structure is efficient for any queue or stack usage.
329 *
330 * Collection operations like [removeAll] and [removeMatching] are very
331 * inefficient. If those are needed, use a [DoubleLinkedQueue] instead.
332 */
333 class ListQueue<E> extends Collection<E> implements Queue<E>{
334 static const int _INITIAL_CAPACITY = 8;
335 List<E> _table;
336 int _head;
337 int _tail;
338 int _modificationCount = 0;
339
340 /**
341 * Create an empty queue.
342 *
343 * If [initialCapacity] is given, prepare the queue for at least that many
344 * elements.
345 */
346 ListQueue([int initialCapacity]) : _head = 0, _tail = 0 {
347 if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {
348 initialCapacity = _INITIAL_CAPACITY;
349 } else if (!_isPowerOf2(initialCapacity)) {
350 initialCapacity = _nextPowerOf2(initialCapacity);
351 }
352 assert(_isPowerOf2(initialCapacity));
353 _table = new List<E>.fixedLength(initialCapacity);
354 }
355
356 /**
357 * Create a queue initially containing the elements of [source].
358 */
359 factory ListQueue.from(Iterable<E> source) {
360 if (source is List) {
361 int length = source.length;
362 ListQueue<E> queue = new ListQueue(length);
363 List sourceList = source;
364 queue._table.setRange(0, length, sourceList, 0);
365 queue._tail = length;
366 return queue;
367 } else {
368 return new ListQueue<E>()..addAll(source);
369 }
370 }
371
372 // Iterable interface.
373
374 Iterator<E> get iterator => new _ListQueueIterator(this);
375
376 void forEach(void action (E element)) {
377 int modificationCount = _modificationCount;
378 for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
379 action(_table[i]);
380 _checkModification(modificationCount);
381 }
382 }
383
384 bool get isEmpty => _head == _tail;
385
386 int get length => (_tail - _head) & (_table.length - 1);
387
388 E get first {
389 if (_head == _tail) throw new StateError("No elements");
390 return _table[_head];
391 }
392
393 E get last {
394 if (_head == _tail) throw new StateError("No elements");
395 return _table[(_tail - 1) & (_table.length - 1)];
396 }
397
398 E get single {
399 if (_head == _tail) throw new StateError("No elements");
400 if (length > 1) throw new StateError("Too many elements");
401 return _table[_head];
402 }
403
404 E elementAt(int index) {
405 if (index < 0 || index > length) {
406 throw new RangeError.range(index, 0, length);
407 }
408 return _table[(_head + index) & (_table.length - 1)];
409 }
410
411 List<E> toList() {
412 List<E> list = new List<E>(length);
413 _writeToList(list);
414 return list;
415 }
416
417 // Collection interface.
418
419 void add(E element) {
420 _add(element);
421 }
422
423 void addAll(Iterable<E> elements) {
424 if (elements is List) {
425 List list = elements;
426 int addCount = list.length;
427 int length = this.length;
428 if (length + addCount >= _table.length) {
429 _preGrow(length + addCount);
430 // After preGrow, all elements are at the start of the list.
431 _table.setRange(length, addCount, list, 0);
432 _tail += addCount;
433 } else {
434 // Adding addCount elements won't reach _head.
435 int endSpace = _table.length - _tail;
436 if (addCount < endSpace) {
437 _table.setRange(_tail, addCount, list, 0);
438 _tail += addCount;
439 } else {
440 int preSpace = addCount - endSpace;
441 _table.setRange(_tail, endSpace, list, 0);
442 _table.setRange(0, preSpace, list, endSpace);
443 _tail = preSpace;
444 }
445 }
446 _modificationCount++;
447 } else {
448 for (E element in elements) _add(element);
449 }
450 }
451
452 void remove(Object object) {
453 for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
454 E element = _table[i];
455 if (element == object) {
456 _remove(i);
457 return;
458 }
459 }
460 _modificationCount++;
461 }
462
463 void removeAll(Iterable objectsToRemove) {
464 IterableMixinWorkaround.removeAllList(this, objectsToRemove);
465 }
466
467 void retainAll(Iterable objectsToRetain) {
468 IterableMixinWorkaround.retainAll(this, objectsToRetain);
469 }
470
471 void _filterMatching(bool test(E element), bool removeMatching) {
472 int index = _head;
473 int modificationCount = _modificationCount;
474 int i = _head;
475 while (i != _tail) {
476 E element = _table[i];
477 bool remove = (test(element) == removeMatching);
478 _checkModification(modificationCount);
479 if (remove) {
480 i = _remove(i);
481 modificationCount = ++_modificationCount;
482 } else {
483 i = (i + 1) & (_table.length - 1);
484 }
485 }
486 }
487
488 /**
489 * Remove all elements matched by [test].
490 *
491 * This method is inefficient since it works by repeatedly removing single
492 * elements, each of which can take linear time.
493 */
494 void removeMatching(bool test(E element)) {
495 _filterMatching(test, true);
496 }
497
498 /**
499 * Remove all elements not matched by [test].
500 *
501 * This method is inefficient since it works by repeatedly removing single
502 * elements, each of which can take linear time.
503 */
504 void retainMatching(bool test(E element)) {
505 _filterMatching(test, false);
506 }
507
508 void clear() {
509 if (_head != _tail) {
510 for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
511 _table[i] = null;
512 }
513 _head = _tail = 0;
514 _modificationCount++;
515 }
516 }
517
518 String toString() {
519 return Collections.collectionToString(this);
520 }
521
522 // Queue interface.
523
524 void addLast(E element) { _add(element); }
525
526 void addFirst(E element) {
527 _head = (_head - 1) & (_table.length - 1);
528 _table[_head] = element;
529 if (_head == _tail) _grow();
530 _modificationCount++;
531 }
532
533 E removeFirst() {
534 if (_head == _tail) throw new StateError("No elements");
535 _modificationCount++;
536 E result = _table[_head];
537 _head = (_head + 1) & (_table.length - 1);
538 return result;
539 }
540
541 E removeLast() {
542 if (_head == _tail) throw new StateError("No elements");
543 _modificationCount++;
544 _tail = (_tail - 1) & (_table.length - 1);
545 return _table[_tail];
546 }
547
548 // Internal helper functions.
549
550 /**
551 * Whether [number] is a power of two.
552 *
553 * Only works for positive numbers.
554 */
555 static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
556
557 /**
558 * Rounds [number] up to the nearest power of 2.
559 *
560 * If [number] is a power of 2 already, it is returned.
561 *
562 * Only works for positive numbers.
563 */
564 static int _nextPowerOf2(int number) {
sra1 2013/02/12 01:09:56 This function might be easier to write correctly i
565 assert(number > 0);
566 number = (number << 2) - 1;
floitsch 2013/02/11 15:15:11 Still not correct. This returns 16 for 7. First gu
567 for(;;) {
568 int nextNumber = number & (number - 1);
569 if (nextNumber == 0) return number;
570 number = nextNumber;
571 }
572 }
573
574 /** Check if the queue has been modified during iteration. */
575 void _checkModification(int expectedModificationCount) {
576 if (expectedModificationCount != _modificationCount) {
577 throw new ConcurrentModificationError(this);
578 }
579 }
580
581 /** Adds element at end of queue. Used by both [add] and [addAll]. */
582 void _add(E element) {
583 _table[_tail] = element;
584 _tail = (_tail + 1) & (_table.length - 1);
585 if (_head == _tail) _grow();
586 _modificationCount++;
587 }
588
589 /**
590 * Removes the element at [offset] into [_table].
591 *
592 * Removal is performed by linerarly moving elements either before or after
593 * [offset] by one position.
594 *
595 * Returns the new offset of the following element. This may be the same
596 * offset or the following offset depending on how elements are moved
597 * to fill the hole.
598 */
599 int _remove(int offset) {
600 int mask = _table.length - 1;
601 int startDistance = (offset - _head) & mask;
602 int endDistance = (_tail - offset) & mask;
603 if (startDistance < endDistance) {
604 // Closest to start.
605 int i = offset;
606 while (i != _head) {
607 int prevOffset = (i - 1) & mask;
608 _table[i] = _table[prevOffset];
609 i = prevOffset;
610 }
611 _table[_head] = null;
612 _head = (_head + 1) & mask;
613 return (offset + 1) & mask;
614 } else {
615 _tail = (_tail - 1) & mask;
616 int i = offset;
617 while (i != _tail) {
618 int nextOffset = (i + 1) & mask;
619 _table[i] = _table[nextOffset];
620 i = nextOffset;
621 }
622 _table[_tail] = null;
623 return offset;
624 }
625 }
626
627 /**
628 * Grow the table when full.
629 */
630 void _grow() {
631 List<E> newTable = new List<E>.fixedLength(_table.length * 2);
632 int split = _table.length - _head;
633 newTable.setRange(0, split, _table, _head);
634 newTable.setRange(split, _head, _table, 0);
635 _head = 0;
636 _tail = _table.length;
637 _table = newTable;
638 }
639
640 int _writeToList(List<E> target) {
641 assert(target.length >= length);
642 if (_head <= _tail) {
643 int length = _tail - _head;
644 target.setRange(0, length, _table, _head);
645 return length;
646 } else {
647 int firstPartSize = _table.length - _head;
648 target.setRange(0, firstPartSize, _table, _head);
649 target.setRange(firstPartSize, _tail, _table, 0);
650 return _tail + firstPartSize;
651 }
652 }
653
654 /** Grows the table even if it is not full. */
655 void _preGrow(int newElementCount) {
656 assert(newElementCount >= length);
657 int newCapacity = _nextPowerOf2(newElementCount);
658 List<E> newTable = new List<E>.fixedLength(newCapacity);
659 _tail = _writeToList(newTable);
660 _table = newTable;
661 _head = 0;
662 }
663 }
664
665 /**
666 * Iterator for a [ListQueue].
667 *
668 * Considers any add or remove operation a concurrent modification.
669 */
670 class _ListQueueIterator<E> implements Iterator<E> {
671 final ListQueue _queue;
672 final int _end;
673 final int _modificationCount;
674 int _position;
675 E _current;
676
677 _ListQueueIterator(ListQueue queue)
678 : _queue = queue,
679 _end = queue._tail,
680 _modificationCount = queue._modificationCount,
681 _position = queue._head;
682
683 E get current => _current;
684
685 bool moveNext() {
686 _queue._checkModification(_modificationCount);
687 if (_position == _end) {
688 _current = null;
689 return false;
690 }
691 _current = _queue._table[_position];
692 _position = (_position + 1) & (_queue._table.length - 1);
693 return true;
694 }
695 }
OLDNEW
« no previous file with comments | « no previous file | tests/corelib/queue_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698