blob: a9c898969429e27cbe854b7cf577c6acfb06c7bc [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of dart.collection;
/**
* A linked list implementation, providing O(1) removal(unlink) of elements and
* manual traversal through [next] and [previous].
*
* The list elements must extend [LinkedListEntry].
*/
class LinkedList<E extends LinkedListEntry<E>>
extends IterableBase<E>
implements _LinkedListLink {
int _modificationCount = 0;
int _length = 0;
_LinkedListLink _next;
_LinkedListLink _previous;
/**
* Construct a new empty linked list.
*/
LinkedList() {
_next = _previous = this;
}
/**
* Add [entry] to the beginning of the list.
*/
void addFirst(E entry) {
_insertAfter(this, entry);
}
/**
* Add [entry] to the end of the list.
*/
void add(E entry) {
_insertAfter(_previous, entry);
}
/**
* Add [entries] to the end of the list.
*/
void addAll(Iterable<E> entries) {
entries.forEach((entry) => _insertAfter(_previous, entry));
}
/**
* Remove [entry] from the list. This is the same as calling `entry.unlink()`.
*
* If [entry] is not in the list, `false` is returned.
*/
bool remove(E entry) {
if (entry._list != this) return false;
_unlink(entry); // Unlink will decrement length.
return true;
}
Iterator<E> get iterator => new _LinkedListIterator<E>(this);
String toString() => ToString.iterableToString(this);
int get length => _length;
void clear() {
_modificationCount++;
_LinkedListLink next = _next;
while (!identical(next, this)) {
E entry = next;
next = entry._next;
entry._next = entry._previous = entry._list = null;
}
_next = _previous = this;
_length = 0;
}
E get first {
if (identical(_next, this)) {
throw new StateError('No such element');
}
return _next;
}
E get last {
if (identical(_previous, this)) {
throw new StateError('No such element');
}
return _previous;
}
E get single {
if (identical(_previous, this)) {
throw new StateError('No such element');
}
if (!identical(_previous, _next)) {
throw new StateError('Too many elements');
}
return _next;
}
/**
* Call [action] with each entry in the list.
*
* It's an error if [action] modify the list.
*/
void forEach(void action(E entry)) {
int modificationCount = _modificationCount;
_LinkedListLink current = _next;
while (!identical(current, this)) {
action(current);
if (modificationCount != _modificationCount) {
throw new ConcurrentModificationError(this);
}
current = current._next;
}
}
bool get isEmpty => _length == 0;
void _insertAfter(_LinkedListLink entry, E newEntry) {
if (newEntry.list != null) {
throw new StateError(
'LinkedListEntry is already in a LinkedList');
}
_modificationCount++;
newEntry._list = this;
var predecessor = entry;
var successor = entry._next;
successor._previous = newEntry;
newEntry._previous = predecessor;
newEntry._next = successor;
predecessor._next = newEntry;
_length++;
}
void _unlink(LinkedListEntry<E> entry) {
_modificationCount++;
entry._next._previous = entry._previous;
entry._previous._next = entry._next;
_length--;
entry._list = entry._next = entry._previous = null;
}
}
class _LinkedListIterator<E extends LinkedListEntry>
implements Iterator<E> {
final LinkedList<E> _list;
final int _modificationCount;
E _current;
_LinkedListLink _next;
_LinkedListIterator(LinkedList<E> list)
: _list = list,
_modificationCount = list._modificationCount,
_next = list._next;
E get current => _current;
bool moveNext() {
if (identical(_next, _list)) {
_current = null;
return false;
}
if (_modificationCount != _list._modificationCount) {
throw new ConcurrentModificationError(this);
}
_current = _next;
_next = _next._next;
return true;
}
}
class _LinkedListLink {
_LinkedListLink _next;
_LinkedListLink _previous;
}
/**
* Entry element for a [LinkedList]. Any entry must extend this class.
*/
abstract class LinkedListEntry<E> implements _LinkedListLink {
LinkedList<E> _list;
_LinkedListLink _next;
_LinkedListLink _previous;
/**
* Get the list containing this element.
*/
LinkedList<E> get list => _list;
/**
* Unlink the element from the list.
*/
void unlink() {
_list._unlink(this);
}
/**
* Return the succeeding element in the list.
*/
E get next {
if (identical(_next, _list)) return null;
return _next as E;
}
/**
* Return the preceeding element in the list.
*/
E get previous {
if (identical(_previous, _list)) return null;
return _previous as E;
}
/**
* insert an element after this.
*/
void insertAfter(E entry) {
_list._insertAfter(this, entry);
}
/**
* Insert an element before this.
*/
void insertBefore(E entry) {
_list._insertAfter(_previous, entry);
}
}