// 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 specialized double-linked list of elements that extends [LinkedListEntry]. /// /// This is not a generic data structure. It only accepts elements that *extend* /// the [LinkedListEntry] class. See the [Queue] implementations for generic /// collections that allow constant time adding and removing at the ends. /// /// This is not a [List] implementation. Despite its name, this class does not /// implement the [List] interface. It does not allow constant time lookup by /// index. /// /// Because the elements themselves contain the links of this linked list, /// each element can be in only one list at a time. To add an element to another /// list, it must first be removed from its current list (if any). /// For the same reason, the [remove] and [contains] methods /// are based on *identity*, even if the [LinkedListEntry] chooses /// to override [Object.==]. /// /// In return, each element knows its own place in the linked list, as well as /// which list it is in. This allows constant time /// [LinkedListEntry.insertAfter], [LinkedListEntry.insertBefore] and /// [LinkedListEntry.unlink] operations when all you have is the element. /// /// A `LinkedList` also allows constant time adding and removing at either end, /// and a constant time length getter. /// /// Example: /// ```dart /// final class EntryItem extends LinkedListEntry { /// final int id; /// final String text; /// EntryItem(this.id, this.text); /// /// @override /// String toString() { /// return '$id : $text'; /// } /// } /// /// void main() { /// final linkedList = LinkedList(); /// linkedList /// .addAll([EntryItem(1, 'A'), EntryItem(2, 'B'), EntryItem(3, 'C')]); /// print(linkedList.first); // 1 : A /// print(linkedList.last); // 3 : C /// /// // Add new item after first item. /// linkedList.first.insertAfter(EntryItem(15, 'E')); /// // Add new item before last item. /// linkedList.last.insertBefore(EntryItem(10, 'D')); /// // Iterate items. /// for (var entry in linkedList) { /// print(entry); /// // 1 : A /// // 15 : E /// // 2 : B /// // 10 : D /// // 3 : C /// } /// /// // Remove item using index from list. /// linkedList.elementAt(2).unlink(); /// print(linkedList); // (1 : A, 15 : E, 10 : D, 3 : C) /// // Remove first item. /// linkedList.first.unlink(); /// print(linkedList); // (15 : E, 10 : D, 3 : C) /// // Remove last item from list. /// linkedList.remove(linkedList.last); /// print(linkedList); // (15 : E, 10 : D) /// // Remove all items. /// linkedList.clear(); /// print(linkedList.length); // 0 /// print(linkedList.isEmpty); // true /// } /// ``` base class LinkedList> extends Iterable { int _modificationCount = 0; int _length = 0; E? _first; /// Constructs a new empty linked list. LinkedList(); /// Adds [entry] to the beginning of the linked list. void addFirst(E entry) { _insertBefore(_first, entry, updateFirst: true); _first = entry; } /// Adds [entry] to the end of the linked list. void add(E entry) { _insertBefore(_first, entry, updateFirst: false); } /// Adds [entries] to the end of the linked list. void addAll(Iterable entries) { entries.forEach(add); } /// Removes [entry] from the linked list. /// /// Returns false and does nothing if [entry] is not in this linked list. /// /// This is equivalent to calling `entry.unlink()` if the entry is in this /// list. bool remove(E entry) { if (entry._list != this) return false; _unlink(entry); // Unlink will decrement length. return true; } /// Whether [entry] is a [LinkedListEntry] belonging to this list. /// /// The [entry] is considered as belonging to this list if /// its [LinkedListEntry.list] is this list. bool contains(Object? entry) => entry is LinkedListEntry && identical(this, entry.list); Iterator get iterator => _LinkedListIterator(this); int get length => _length; /// Remove all elements from this linked list. void clear() { _modificationCount++; if (isEmpty) return; E next = _first!; do { E entry = next; next = entry._next!; entry._next = entry._previous = entry._list = null; } while (!identical(next, _first)); _first = null; _length = 0; } E get first { if (isEmpty) { throw StateError('No such element'); } return _first!; } E get last { if (isEmpty) { throw StateError('No such element'); } return _first!._previous!; } E get single { if (isEmpty) { throw StateError('No such element'); } if (_length > 1) { throw StateError('Too many elements'); } return _first!; } /// Call [action] with each entry in this linked list. /// /// It's an error if [action] modifies the linked list. void forEach(void action(E entry)) { int modificationCount = _modificationCount; if (isEmpty) return; E current = _first!; do { action(current); if (modificationCount != _modificationCount) { throw ConcurrentModificationError(this); } current = current._next!; } while (!identical(current, _first)); } bool get isEmpty => _length == 0; /// Inserts [newEntry] as last entry of the list. /// /// If [updateFirst] is true and [entry] is the first entry in the list, /// updates the [_first] field to point to the [newEntry] as first entry. void _insertBefore(E? entry, E newEntry, {required bool updateFirst}) { if (newEntry.list != null) { throw StateError('LinkedListEntry is already in a LinkedList'); } _modificationCount++; newEntry._list = this; if (isEmpty) { assert(entry == null); newEntry._previous = newEntry._next = newEntry; _first = newEntry; _length++; return; } E predecessor = entry!._previous!; E successor = entry; newEntry._previous = predecessor; newEntry._next = successor; predecessor._next = newEntry; successor._previous = newEntry; if (updateFirst && identical(entry, _first)) { _first = newEntry; } _length++; } void _unlink(E entry) { _modificationCount++; entry._next!._previous = entry._previous; E? next = entry._previous!._next = entry._next; _length--; entry._list = entry._next = entry._previous = null; if (isEmpty) { _first = null; } else if (identical(entry, _first)) { _first = next; } } } class _LinkedListIterator> implements Iterator { final LinkedList _list; final int _modificationCount; E? _current; E? _next; bool _visitedFirst; _LinkedListIterator(LinkedList list) : _list = list, _modificationCount = list._modificationCount, _next = list._first, _visitedFirst = false; E get current => _current as E; bool moveNext() { if (_modificationCount != _list._modificationCount) { throw ConcurrentModificationError(this); } if (_list.isEmpty || (_visitedFirst && identical(_next, _list.first))) { _current = null; return false; } _visitedFirst = true; _current = _next; _next = _next!._next; return true; } } /// An object that can be an element in a [LinkedList]. /// /// All elements of a `LinkedList` must extend this class. /// The class provides the internal links that link elements together /// in the `LinkedList`, and a reference to the linked list itself /// that an element is currently part of. /// /// An entry can be in at most one linked list at a time. /// While an entry is in a linked list, the [list] property points to that /// linked list, and otherwise the `list` property is `null`. /// /// When created, an entry is not in any linked list. abstract base mixin class LinkedListEntry> { LinkedList? _list; E? _next; E? _previous; /// The linked list containing this element. /// /// The value is `null` if this entry is not currently in any list. LinkedList? get list => _list; /// Unlink the element from its linked list. /// /// The entry must currently be in a linked list when this method is called. void unlink() { _list!._unlink(this as E); } /// The successor of this element in its linked list. /// /// The value is `null` if there is no successor in the linked list, /// or if this entry is not currently in any list. E? get next { if (_list == null || identical(_list!.first, _next)) return null; return _next; } /// The predecessor of this element in its linked list. /// /// The value is `null` if there is no predecessor in the linked list, /// or if this entry is not currently in any list. E? get previous { if (_list == null || identical(this, _list!.first)) return null; return _previous; } /// Insert an element after this element in this element's linked list. /// /// This entry must be in a linked list when this method is called. /// The [entry] must not be in a linked list. void insertAfter(E entry) { _list!._insertBefore(_next, entry, updateFirst: false); } /// Insert an element before this element in this element's linked list. /// /// This entry must be in a linked list when this method is called. /// The [entry] must not be in a linked list. void insertBefore(E entry) { _list!._insertBefore(this as E, entry, updateFirst: true); } }