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

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: 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 ListQueue.from(Iterable<E> source) : _head = 0, _tail = 0 {
360 int length = source.length;
floitsch 2013/02/07 16:44:31 Since we still do a List check below, might as wel
Lasse Reichstein Nielsen 2013/02/11 15:02:32 Please elaborate. I need the length to find the ne
361 int capacity = _nextPowerOf2(length);
362 if (capacity < _INITIAL_CAPACITY) capacity = _INITIAL_CAPACITY;
363 _table = new List<E>.fixedLength(capacity);
364 if (source is List) {
365 List sourceList = source;
366 _table.setRange(0, length, sourceList, 0);
367 _tail = length;
368 } else {
369 addAll(source);
370 }
371 }
372
373 // Iterable interface.
374
375 Iterator<E> get iterator => new _ListQueueIterator(this);
376
377 void forEach(void action (E element)) {
378 int modificationCount = _modificationCount;
379 for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
380 action(_table[i]);
381 _checkModification(modificationCount);
382 }
383 }
384
385 bool get isEmpty => _head == _tail;
386
387 int get length => (_tail - _head) & (_table.length - 1);
388
389 E get first {
390 if (_head == _tail) throw new StateError("No elements");
391 return _table[_head];
392 }
393
394 E get last {
395 if (_head == _tail) throw new StateError("No elements");
396 return _table[(_tail - 1) & (_table.length - 1)];
397 }
398
399 E get single {
400 if (_head == _tail) throw new StateError("No elements");
401 if (length > 1) throw new StateError("Too many elements");
402 return _table[_head];
403 }
404
405 E elementAt(int index) {
406 if (index < 0 || index > length) throw new RangeError.range(index, 0, length );
floitsch 2013/02/07 16:44:31 80 chars.
407 return _table[(_head + index) & (_table.length - 1)];
408 }
409
410 List<E> toList() {
411 if (_head <= _tail) {
412 int length = _tail - head;
413 List list = new List<E>(length);
414 list.setRange(0, length, _table, _head);
415 return list;
416 } else {
417 int firstPartSize = _table.length - _start;
418 int length = firstPartSize + _tail;
419 List list = new List<E>(length);
420 list.setRange(0, firstPartSize, _table, _head);
421 list.setRange(firstPartSize, _tail, _table, 0);
422 return list;
423 }
424 }
425
426 // Collection interface.
427
428 void add(E element) {
429 _table[_tail] = element;
430 _tail = (_tail + 1) & (_table.length - 1);
431 if (_head == _tail) _grow();
432 _modificationCount++;
433 }
434
435 void addAll(Iterable<E> elements) {
436 if (elements is List) {
437 List list = elements;
438 int addCount = list.length;
439 int length = this.length;
440 if (length + addCount >= _table.length) {
441 _preGrow(length + _addCount);
442 _table.setRange(length, addCount, list, 0);
floitsch 2013/02/07 16:44:31 Add comment: After [_preGrow] all elements have be
Lasse Reichstein Nielsen 2013/02/11 15:02:32 Done.
443 _tail += addCount;
444 } else {
445 // Adding addCount elements won't reach _head.
446 int endSpace = _table.length - _tail;
447 if (addCount < endSpace) {
448 _table.setRange(_tail, addCount, list, 0);
449 _tail += addCount;
450 } else {
451 int preSpace = addCount - endSpace;
452 _table.setRange(_tail, endSpace, list, 0);
453 _table.setRange(0, preSpace, list, endSpace);
454 _tail = preSpace;
455 }
456 }
457 _modificationCount++;
458 } else {
459 for (E element in elements) add(element);
460 }
461 }
462
463 void remove(Object object) {
464 for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
465 E element = _table[i];
466 if (element == object) {
467 _remove(i);
468 return;
469 }
470 }
471 _modificationCount++;
472 }
473
474 void removeAll(Iterable objectsToRemove) {
475 IterableMixinWorkaround.removeAllList(this, objectsToRemove);
476 }
477
478 void retainAll(Iterable objectsToRetain) {
479 IterableMixinWorkaround.retainAll(this, objectsToRetain);
480 }
481
482 /**
483 * Remove all elements matched by [test].
484 *
485 * This method is inefficient since it works by repeatedly removing single
486 * elements, each of which can take linear time.
487 */
488 void removeMatching(bool test(E element)) {
floitsch 2013/02/07 16:44:31 Share the code for remove and retainMatching.
Lasse Reichstein Nielsen 2013/02/11 15:02:32 Done.
489 int index = _head;
490 int modificationCount = _modificationCount;
491 int i = _head;
492 while (i != _tail) {
493 E element = _table[i];
494 bool match = test(element);
495 _checkModification(modificationCount);
496 if (match) {
497 i = _remove(i);
498 modificationCount = ++_modificationCount;
499 } else {
500 i = (i + 1) & (_table.length - 1);
501 }
502 }
503 }
504
505 /**
506 * Remove all elements not matched by [test].
507 *
508 * This method is inefficient since it works by repeatedly removing single
509 * elements, each of which can take linear time.
510 */
511 void retainMatching(bool test(E element)) {
512 int index = _head;
513 int modificationCount = _modificationCount;
514 int i = _head;
515 while (i != _tail) {
516 E element = _table[i];
517 bool match = test(element);
518 _checkModification(modificationCount);
519 if (!match) {
520 i = _remove(i);
521 modificationCount = ++_modificationCount;
522 } else {
523 i = (i + 1) & (_table.length - 1);
524 }
525 }
526 }
527
528 void clear() {
529 if (_head != _tail) {
530 for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
531 _table[i] = null;
532 }
533 _head = _tail = 0;
534 _modificationCount++;
535 }
536 }
537
538 String toString() {
539 return Collections.collectionToString(this);
540 }
541
542 // Queue interface.
543
544 void addLast(E element) { add(element); }
545
546 void addFirst(E element) {
547 _head = (_head - 1) & (_table.length - 1);
548 _table[_head] = element;
549 if (_head == _tail) _grow();
550 _modificationCount++;
551 }
552
553 E removeFirst() {
554 if (_head == _tail) throw new StateError("No elements");
555 _modificationCount++;
556 E result = _table[_head];
557 _head = (_head + 1) & (_table.length - 1);
558 return result;
559 }
560
561 E removeLast() {
562 if (_head == _tail) throw new StateError("No elements");
563 _modificationCount++;
564 _tail = (_tail - 1) & (_table.length - 1);
565 return _table[_tail];
566 }
567
568 // Internal helper functions.
569
570 /**
571 * Whether [number] is a power of two.
572 *
573 * Only works for positive numbers.
574 */
575 static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
576
577 /**
578 * Rounds [number] up to the nearest power of 2.
579 *
580 * If [number] is a power of 2 already, it is returned.
581 *
582 * Only works for positive numbers.
583 */
584 static int _nextPowerOf2(int number) {
floitsch 2013/02/07 16:44:31 not doing the right thing. the result is never big
Lasse Reichstein Nielsen 2013/02/11 15:02:32 Done.
585 for(;;) {
586 int nextNumber = number & (number - 1);
587 if (nextNumber == 0) return number;
588 number = nextNumber;
589 }
590 }
591
592 /** Check if the queue has been modified during iteration. */
593 void _checkModification(int expectedModificationCount) {
594 if (expectedModificationCount != _modificationCount) {
595 throw new ConcurrentModificationError(this);
596 }
597 }
598
599 /**
600 * Removes the element at [offset] into [_table].
601 *
602 * Removal is performed by linerarly moving elements either before or after
603 * [offset] by one position.
604 *
605 * Returns the new offset of the following element. This may be the same
606 * offset or the following offset depending on how elements are moved
607 * to fill the hole.
608 */
609 int _remove(int offset) {
610 int mask = _table.length - 1;
611 int startDistance = (offset - _head) & mask;
612 int endDistance = (_tail - offset) & mask;
613 if (startDistance < endDistance) {
614 // Closest to start.
615 int i = offset;
616 while (i != _head) {
floitsch 2013/02/07 16:44:31 setRange doesn't work on the same list?
Lasse Reichstein Nielsen 2013/02/11 15:02:32 I assumed it doesn't. It actually seems that it
617 int prevOffset = (_offset - 1) & mask;
618 _table[i] = _table[prevOffset];
619 i = _prevOffset;
620 }
621 _table[_head] = null;
622 _head++;
floitsch 2013/02/07 16:44:31 _head = _head + 1 & mask ?
623 return offset + 1;
floitsch 2013/02/07 16:44:31 (offset + 1) & mask
Lasse Reichstein Nielsen 2013/02/11 15:02:32 Done.
624 } else {
625 _tail--;
626 int i = offset;
627 while (i != _tail) {
628 int nextOffset = (i + 1) & mask;
629 _table[i] = _table[nextOffset];
630 i = nextOffset;
631 }
632 _table[_tail] = null;
633 return offset;
634 }
635 }
636
637 /**
638 * Grow the table when full.
639 */
640 void _grow() {
641 List<E> newTable = new List<E>.fixedLength(_table.length * 2);
642 int split = _table.length - _head;
643 newTable.setRange(0, split, _table, _head);
644 newTable.setRange(split, _head, _table, 0);
645 _head = 0;
646 _tail = _table.length;
647 _table = newTable;
648 }
649
650 /** Grows the table even if it is not full. */
651 void _preGrow(int newElementCount) {
652 assert(newElementCount >= length);
653 int newCapacity = _nextPowerOf2(newElementCount);
654 List<E> newTable = new List<E>.fixedLength(newCapacity);
655 if (_head <= _tail) {
656 int length = _tail - head;
657 newTable.setRange(0, length, _table, _head);
658 _table = newTable;
659 _head = 0;
660 _tail = length;
661 } else {
662 int firstPartSize = _table.length - _start;
663 newTable.setRange(0, firstPartSize, _table, _head);
664 newTable.setRange(firstPartSize, _tail, _table, 0);
665 _table = _newTable;
666 _head = 0;
667 _tail += firstPartSize;
668 }
669 }
670 }
671
672 /**
673 * Iterator for a [ListQueue].
674 *
675 * Considers any add, remove operation a concurrent modification
floitsch 2013/02/07 16:44:31 as
Lasse Reichstein Nielsen 2013/02/11 15:02:32 No "as" intended.
676 */
677 class _ListQueueIterator<E> implements Iterator<E> {
678 final ListQueue _queue;
679 final int _end;
680 final int _modificationCount;
681 int _position;
682 E _current;
683
684 _ListQueueIterator(ListQueue queue)
685 : _queue = queue,
686 _end = queue._tail,
687 _modificationCount = queue._modificationCount,
688 _position = queue._head;
689
690 E get current => _current;
691
692 bool moveNext() {
693 _queue._checkModification(_modificationCount);
694 if (_position == _end) {
695 _current = null;
696 return false;
697 }
698 _current = _queue._table[_position];
699 _position = (_position + 1) & (_queue._table.length - 1);
700 return true;
701 }
702 }
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