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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/corelib/queue_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/collection/queue.dart
diff --git a/sdk/lib/collection/queue.dart b/sdk/lib/collection/queue.dart
index 87c6fb7896fb7934b40cd198d25b234d13dc71dc..9a9efbd1f57d40ed97644daf92b22096d531a674 100644
--- a/sdk/lib/collection/queue.dart
+++ b/sdk/lib/collection/queue.dart
@@ -9,18 +9,18 @@ part of dart.collection;
* can iterate over the elements of a queue through [forEach] or with
* an [Iterator].
*/
-abstract class Queue<E> extends Collection<E> {
+abstract class Queue<E> implements Collection<E> {
/**
* Creates a queue.
*/
- factory Queue() => new DoubleLinkedQueue<E>();
+ factory Queue() => new ListQueue<E>();
/**
* Creates a queue with the elements of [other]. The order in
* the queue will be the order provided by the iterator of [other].
*/
- factory Queue.from(Iterable<E> other) => new DoubleLinkedQueue<E>.from(other);
+ factory Queue.from(Iterable<E> other) => new ListQueue<E>.from(other);
/**
* Removes and returns the first element of this queue. Throws an
@@ -154,11 +154,11 @@ class _DoubleLinkedQueueEntrySentinel<E> extends DoubleLinkedQueueEntry<E> {
}
/**
- * Implementation of a double linked list that box list elements into
- * DoubleLinkedQueueEntry objects.
+ * A [Queue] implementation based on a double-linked list.
*
- * WARNING: This class is temporary located in dart:core. It'll be removed
- * at some point in the near future.
+ * Allows constant time add, remove-at-ends and peek operations.
+ *
+ * Can do [removeAll] and [retainAll] in linear time.
*/
class DoubleLinkedQueue<E> extends Collection<E> implements Queue<E> {
_DoubleLinkedQueueEntrySentinel<E> _sentinel;
@@ -317,3 +317,386 @@ class _DoubleLinkedQueueIterator<E> implements Iterator<E> {
E get current => _current;
}
+
+/**
+ * List based [Queue].
+ *
+ * Keeps a cyclic buffer of elements, and grows to a larger buffer when
+ * it fills up. This guarantees constant time peek and remove operations, and
+ * amortized constant time add operations.
+ *
+ * The structure is efficient for any queue or stack usage.
+ *
+ * Collection operations like [removeAll] and [removeMatching] are very
+ * inefficient. If those are needed, use a [DoubleLinkedQueue] instead.
+ */
+class ListQueue<E> extends Collection<E> implements Queue<E>{
+ static const int _INITIAL_CAPACITY = 8;
+ List<E> _table;
+ int _head;
+ int _tail;
+ int _modificationCount = 0;
+
+ /**
+ * Create an empty queue.
+ *
+ * If [initialCapacity] is given, prepare the queue for at least that many
+ * elements.
+ */
+ ListQueue([int initialCapacity]) : _head = 0, _tail = 0 {
+ if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {
+ initialCapacity = _INITIAL_CAPACITY;
+ } else if (!_isPowerOf2(initialCapacity)) {
+ initialCapacity = _nextPowerOf2(initialCapacity);
+ }
+ assert(_isPowerOf2(initialCapacity));
+ _table = new List<E>.fixedLength(initialCapacity);
+ }
+
+ /**
+ * Create a queue initially containing the elements of [source].
+ */
+ ListQueue.from(Iterable<E> source) : _head = 0, _tail = 0 {
+ 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
+ int capacity = _nextPowerOf2(length);
+ if (capacity < _INITIAL_CAPACITY) capacity = _INITIAL_CAPACITY;
+ _table = new List<E>.fixedLength(capacity);
+ if (source is List) {
+ List sourceList = source;
+ _table.setRange(0, length, sourceList, 0);
+ _tail = length;
+ } else {
+ addAll(source);
+ }
+ }
+
+ // Iterable interface.
+
+ Iterator<E> get iterator => new _ListQueueIterator(this);
+
+ void forEach(void action (E element)) {
+ int modificationCount = _modificationCount;
+ for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
+ action(_table[i]);
+ _checkModification(modificationCount);
+ }
+ }
+
+ bool get isEmpty => _head == _tail;
+
+ int get length => (_tail - _head) & (_table.length - 1);
+
+ E get first {
+ if (_head == _tail) throw new StateError("No elements");
+ return _table[_head];
+ }
+
+ E get last {
+ if (_head == _tail) throw new StateError("No elements");
+ return _table[(_tail - 1) & (_table.length - 1)];
+ }
+
+ E get single {
+ if (_head == _tail) throw new StateError("No elements");
+ if (length > 1) throw new StateError("Too many elements");
+ return _table[_head];
+ }
+
+ E elementAt(int index) {
+ if (index < 0 || index > length) throw new RangeError.range(index, 0, length);
floitsch 2013/02/07 16:44:31 80 chars.
+ return _table[(_head + index) & (_table.length - 1)];
+ }
+
+ List<E> toList() {
+ if (_head <= _tail) {
+ int length = _tail - head;
+ List list = new List<E>(length);
+ list.setRange(0, length, _table, _head);
+ return list;
+ } else {
+ int firstPartSize = _table.length - _start;
+ int length = firstPartSize + _tail;
+ List list = new List<E>(length);
+ list.setRange(0, firstPartSize, _table, _head);
+ list.setRange(firstPartSize, _tail, _table, 0);
+ return list;
+ }
+ }
+
+ // Collection interface.
+
+ void add(E element) {
+ _table[_tail] = element;
+ _tail = (_tail + 1) & (_table.length - 1);
+ if (_head == _tail) _grow();
+ _modificationCount++;
+ }
+
+ void addAll(Iterable<E> elements) {
+ if (elements is List) {
+ List list = elements;
+ int addCount = list.length;
+ int length = this.length;
+ if (length + addCount >= _table.length) {
+ _preGrow(length + _addCount);
+ _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.
+ _tail += addCount;
+ } else {
+ // Adding addCount elements won't reach _head.
+ int endSpace = _table.length - _tail;
+ if (addCount < endSpace) {
+ _table.setRange(_tail, addCount, list, 0);
+ _tail += addCount;
+ } else {
+ int preSpace = addCount - endSpace;
+ _table.setRange(_tail, endSpace, list, 0);
+ _table.setRange(0, preSpace, list, endSpace);
+ _tail = preSpace;
+ }
+ }
+ _modificationCount++;
+ } else {
+ for (E element in elements) add(element);
+ }
+ }
+
+ void remove(Object object) {
+ for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
+ E element = _table[i];
+ if (element == object) {
+ _remove(i);
+ return;
+ }
+ }
+ _modificationCount++;
+ }
+
+ void removeAll(Iterable objectsToRemove) {
+ IterableMixinWorkaround.removeAllList(this, objectsToRemove);
+ }
+
+ void retainAll(Iterable objectsToRetain) {
+ IterableMixinWorkaround.retainAll(this, objectsToRetain);
+ }
+
+ /**
+ * Remove all elements matched by [test].
+ *
+ * This method is inefficient since it works by repeatedly removing single
+ * elements, each of which can take linear time.
+ */
+ 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.
+ int index = _head;
+ int modificationCount = _modificationCount;
+ int i = _head;
+ while (i != _tail) {
+ E element = _table[i];
+ bool match = test(element);
+ _checkModification(modificationCount);
+ if (match) {
+ i = _remove(i);
+ modificationCount = ++_modificationCount;
+ } else {
+ i = (i + 1) & (_table.length - 1);
+ }
+ }
+ }
+
+ /**
+ * Remove all elements not matched by [test].
+ *
+ * This method is inefficient since it works by repeatedly removing single
+ * elements, each of which can take linear time.
+ */
+ void retainMatching(bool test(E element)) {
+ int index = _head;
+ int modificationCount = _modificationCount;
+ int i = _head;
+ while (i != _tail) {
+ E element = _table[i];
+ bool match = test(element);
+ _checkModification(modificationCount);
+ if (!match) {
+ i = _remove(i);
+ modificationCount = ++_modificationCount;
+ } else {
+ i = (i + 1) & (_table.length - 1);
+ }
+ }
+ }
+
+ void clear() {
+ if (_head != _tail) {
+ for (int i = _head; i != _tail; i = (i + 1) & (_table.length - 1)) {
+ _table[i] = null;
+ }
+ _head = _tail = 0;
+ _modificationCount++;
+ }
+ }
+
+ String toString() {
+ return Collections.collectionToString(this);
+ }
+
+ // Queue interface.
+
+ void addLast(E element) { add(element); }
+
+ void addFirst(E element) {
+ _head = (_head - 1) & (_table.length - 1);
+ _table[_head] = element;
+ if (_head == _tail) _grow();
+ _modificationCount++;
+ }
+
+ E removeFirst() {
+ if (_head == _tail) throw new StateError("No elements");
+ _modificationCount++;
+ E result = _table[_head];
+ _head = (_head + 1) & (_table.length - 1);
+ return result;
+ }
+
+ E removeLast() {
+ if (_head == _tail) throw new StateError("No elements");
+ _modificationCount++;
+ _tail = (_tail - 1) & (_table.length - 1);
+ return _table[_tail];
+ }
+
+ // Internal helper functions.
+
+ /**
+ * Whether [number] is a power of two.
+ *
+ * Only works for positive numbers.
+ */
+ static bool _isPowerOf2(int number) => (number & (number - 1)) == 0;
+
+ /**
+ * Rounds [number] up to the nearest power of 2.
+ *
+ * If [number] is a power of 2 already, it is returned.
+ *
+ * Only works for positive numbers.
+ */
+ 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.
+ for(;;) {
+ int nextNumber = number & (number - 1);
+ if (nextNumber == 0) return number;
+ number = nextNumber;
+ }
+ }
+
+ /** Check if the queue has been modified during iteration. */
+ void _checkModification(int expectedModificationCount) {
+ if (expectedModificationCount != _modificationCount) {
+ throw new ConcurrentModificationError(this);
+ }
+ }
+
+ /**
+ * Removes the element at [offset] into [_table].
+ *
+ * Removal is performed by linerarly moving elements either before or after
+ * [offset] by one position.
+ *
+ * Returns the new offset of the following element. This may be the same
+ * offset or the following offset depending on how elements are moved
+ * to fill the hole.
+ */
+ int _remove(int offset) {
+ int mask = _table.length - 1;
+ int startDistance = (offset - _head) & mask;
+ int endDistance = (_tail - offset) & mask;
+ if (startDistance < endDistance) {
+ // Closest to start.
+ int i = offset;
+ 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
+ int prevOffset = (_offset - 1) & mask;
+ _table[i] = _table[prevOffset];
+ i = _prevOffset;
+ }
+ _table[_head] = null;
+ _head++;
floitsch 2013/02/07 16:44:31 _head = _head + 1 & mask ?
+ return offset + 1;
floitsch 2013/02/07 16:44:31 (offset + 1) & mask
Lasse Reichstein Nielsen 2013/02/11 15:02:32 Done.
+ } else {
+ _tail--;
+ int i = offset;
+ while (i != _tail) {
+ int nextOffset = (i + 1) & mask;
+ _table[i] = _table[nextOffset];
+ i = nextOffset;
+ }
+ _table[_tail] = null;
+ return offset;
+ }
+ }
+
+ /**
+ * Grow the table when full.
+ */
+ void _grow() {
+ List<E> newTable = new List<E>.fixedLength(_table.length * 2);
+ int split = _table.length - _head;
+ newTable.setRange(0, split, _table, _head);
+ newTable.setRange(split, _head, _table, 0);
+ _head = 0;
+ _tail = _table.length;
+ _table = newTable;
+ }
+
+ /** Grows the table even if it is not full. */
+ void _preGrow(int newElementCount) {
+ assert(newElementCount >= length);
+ int newCapacity = _nextPowerOf2(newElementCount);
+ List<E> newTable = new List<E>.fixedLength(newCapacity);
+ if (_head <= _tail) {
+ int length = _tail - head;
+ newTable.setRange(0, length, _table, _head);
+ _table = newTable;
+ _head = 0;
+ _tail = length;
+ } else {
+ int firstPartSize = _table.length - _start;
+ newTable.setRange(0, firstPartSize, _table, _head);
+ newTable.setRange(firstPartSize, _tail, _table, 0);
+ _table = _newTable;
+ _head = 0;
+ _tail += firstPartSize;
+ }
+ }
+}
+
+/**
+ * Iterator for a [ListQueue].
+ *
+ * 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.
+ */
+class _ListQueueIterator<E> implements Iterator<E> {
+ final ListQueue _queue;
+ final int _end;
+ final int _modificationCount;
+ int _position;
+ E _current;
+
+ _ListQueueIterator(ListQueue queue)
+ : _queue = queue,
+ _end = queue._tail,
+ _modificationCount = queue._modificationCount,
+ _position = queue._head;
+
+ E get current => _current;
+
+ bool moveNext() {
+ _queue._checkModification(_modificationCount);
+ if (_position == _end) {
+ _current = null;
+ return false;
+ }
+ _current = _queue._table[_position];
+ _position = (_position + 1) & (_queue._table.length - 1);
+ return true;
+ }
+}
« 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