PersistentList<E> class
class PersistentList<E> extends ListBase<E>{ bool isEmbeddedObject = false; BasePersistentObject _parent; String _pathToMe; Type elementType; List _list; // set internalList(List value) => _list = value; List get internalList => _list; _ValueConverter valueConverter; PersistentList._internal(this._parent, this.elementType, this._pathToMe) { List lst = _parent.map[_pathToMe]; if (lst == null) { lst = []; } _list = objectory.dataListDecorator(lst); _parent.map[_pathToMe] = _list; if (objectory.newInstance(elementType) is EmbeddedPersistentObject) { isEmbeddedObject = true; } valueConverter = new _ValueConverter(this); } factory PersistentList(BasePersistentObject parent, Type elementType, String pathToMe) { PersistentList result = parent._compoundProperties[pathToMe]; if (result == null) { result = new PersistentList._internal(parent,elementType,pathToMe); parent._compoundProperties[pathToMe] = result; } return result; } toString() => "PersistentList($_list)"; void setDirty(String propertyName) { _parent.setDirty(_pathToMe); } internValue(E value) { var el = value; if (el is EmbeddedPersistentObject) { el._parent = _parent; el._pathToMe = _pathToMe; return el.map; } if (el is PersistentObject) { return el.dbRef; } return value; } int get length => _list.length; void set length(int newLength) { _list.length = newLength; } void operator[]=(int index, E value){ _list[index] = internValue(value); setDirty(null); } E operator[](int index) => valueConverter.convertValue(_list[index]); void clear() { setDirty(null); _list.clear(); } }
Extends
ListBase<E> > PersistentList<E>
Constructors
factory PersistentList(BasePersistentObject parent, Type elementType, String pathToMe) #
Creates a list of the given length.
The created list is fixed-length if length is provided.
List fixedLengthList = new List(3);
fixedLengthList.length; // 3
fixedLengthList.length = 1; // Error
The list has length 0 and is growable if length is omitted.
List growableList = new List();
growableList.length; // 0;
growableList.length = 3;
The length must not be negative or null, if it is provided.
factory PersistentList(BasePersistentObject parent, Type elementType, String pathToMe) { PersistentList result = parent._compoundProperties[pathToMe]; if (result == null) { result = new PersistentList._internal(parent,elementType,pathToMe); parent._compoundProperties[pathToMe] = result; } return result; }
Properties
final E first #
Returns the first element.
If this
is empty throws a StateError. Otherwise this method is
equivalent to this.elementAt(0)
E get first { if (length == 0) throw new StateError("No elements"); return this[0]; }
final E last #
E get last { if (length == 0) throw new StateError("No elements"); return this[length - 1]; }
void set length(int newLength) #
Changes the length of this list.
If
newLength is greater than
the current length, entries are initialized to null
.
Throws an UnsupportedError if the list is fixed-length.
void set length(int newLength) { _list.length = newLength; }
final E single #
Returns the single element in this
.
If this
is empty or has more than one element throws a StateError.
E get single { if (length == 0) throw new StateError("No elements"); if (length > 1) throw new StateError("Too many elements"); return this[0]; }
_ValueConverter valueConverter #
_ValueConverter valueConverter
Operators
E operator [](int index) #
Returns the object at the given index in the list or throws a RangeError if index is out of bounds.
E operator[](int index) => valueConverter.convertValue(_list[index]);
Methods
void add(E element) #
Adds value
to the end of this list,
extending the length by one.
Throws an UnsupportedError if the list is fixed-length.
void add(E element) { this[this.length++] = element; }
void addAll(Iterable<E> iterable) #
Appends all objects of iterable to the end of this list.
Extends the length of the list by the number of objects in iterable. Throws an UnsupportedError if this list is fixed-length.
void addAll(Iterable<E> iterable) { for (E element in iterable) { this[this.length++] = element; } }
bool any(bool test(E element)) #
Returns true if one element of this collection satisfies the predicate test. Returns false otherwise.
bool any(bool test(E element)) { int length = this.length; for (int i = 0; i < length; i++) { if (test(this[i])) return true; if (length != this.length) { throw new ConcurrentModificationError(this); } } return false; }
Map<int, E> asMap() #
Returns an unmodifiable Map view of this
.
The map uses the indices of this list as keys and the corresponding objects
as values. The Map.keys
Iterable iterates the indices of this list
in numerical order.
List<String> words = ['fee', 'fi', 'fo', 'fum'];
Map<int, String> map = words.asMap();
map[0] + map[1]; // 'feefi';
map.keys.toList(); // [0, 1, 2, 3]
Map<int, E> asMap() { return new ListMapView(this); }
void clear() #
Removes all objects from this list; the length of the list becomes zero.
Throws an UnsupportedError, and retains all objects, if this is a fixed-length list.
void clear() { setDirty(null); _list.clear(); }
bool contains(Object element) #
Returns true if the collection contains an element equal to element.
bool contains(Object element) { int length = this.length; for (int i = 0; i < length; i++) { if (this[i] == element) return true; if (length != this.length) { throw new ConcurrentModificationError(this); } } return false; }
E elementAt(int index) #
Returns the indexth element.
If this
has fewer than
index elements throws a RangeError.
Note: if this
does not have a deterministic iteration order then the
function may simply return any element without any iteration if there are
at least
index elements in this
.
E elementAt(int index) => this[index];
bool every(bool test(E element)) #
Returns true if every elements of this collection satisify the
predicate
test. Returns false
otherwise.
bool every(bool test(E element)) { int length = this.length; for (int i = 0; i < length; i++) { if (!test(this[i])) return false; if (length != this.length) { throw new ConcurrentModificationError(this); } } return true; }
Iterable expand(Iterable f(E element)) #
Expands each element of this Iterable into zero or more elements.
The resulting Iterable runs through the elements returned by f for each element of this, in order.
The returned Iterable is lazy, and calls f for each element of this every time it's iterated.
Iterable expand(Iterable f(E element)) => new ExpandIterable<E, dynamic>(this, f);
void fillRange(int start, int end, [E fill]) #
Sets the objects in the range
start inclusive to
end exclusive
to the given fillValue
.
An error occurs if
start..
end is not a valid range for this
.
void fillRange(int start, int end, [E fill]) { _rangeCheck(start, end); for (int i = start; i < end; i++) { this[i] = fill; } }
dynamic firstWhere(bool test(E element), {Object orElse()}) #
Returns the first element that satisfies the given predicate test.
If none matches, the result of invoking the
orElse function is
returned. By default, when
orElse is null
, a StateError is
thrown.
dynamic firstWhere(bool test(E element), { Object orElse() }) { int length = this.length; for (int i = 0; i < length; i++) { E element = this[i]; if (test(element)) return element; if (length != this.length) { throw new ConcurrentModificationError(this); } } if (orElse != null) return orElse(); throw new StateError("No matching element"); }
dynamic fold(initialValue, combine(previousValue, E element)) #
Reduces a collection to a single value by iteratively combining each element of the collection with an existing value using the provided function.
Use initialValue as the initial value, and the function combine to create a new value from the previous one and an element.
Example of calculating the sum of an iterable:
iterable.fold(0, (prev, element) => prev + element);
fold(var initialValue, combine(var previousValue, E element)) { var value = initialValue; int length = this.length; for (int i = 0; i < length; i++) { value = combine(value, this[i]); if (length != this.length) { throw new ConcurrentModificationError(this); } } return value; }
Iterable<E> getRange(int start, int end) #
Returns an Iterable that iterates over the objects in the range start inclusive to end exclusive.
An error occurs if end is before start.
An error occurs if the
start and
end are not valid ranges at the time
of the call to this method. The returned Iterable behaves like
skip(start).take(end - start)
. That is, it does not throw exceptions
if this
changes size.
List<String> colors = ['red', 'green', 'blue', 'orange', 'pink'];
Iterable<String> range = colors.getRange(1, 4);
range.join(', '); // 'green, blue, orange'
colors.length = 3;
range.join(', '); // 'green, blue'
Iterable<E> getRange(int start, int end) { _rangeCheck(start, end); return new SubListIterable(this, start, end); }
int indexOf(Object element, [int startIndex = 0]) #
Returns the first index of element in this list.
Searches the list from index start
to the end of the list.
The first time an object o
is encountered so that o == element
,
the index of o
is returned.
List<String> notes = ['do', 're', 'mi', 're'];
notes.indexOf('re'); // 1
notes.indexOf('re', 2); // 3
Returns -1 if element is not found.
notes.indexOf('fa'); // -1
int indexOf(Object element, [int startIndex = 0]) { if (startIndex >= this.length) { return -1; } if (startIndex < 0) { startIndex = 0; } for (int i = startIndex; i < this.length; i++) { if (this[i] == element) { return i; } } return -1; }
void insert(int index, E element) #
Inserts the object at position index in this list.
This increases the length of the list by one and shifts all objects at or after the index towards the end of the list.
An error occurs if the index is less than 0 or greater than length. An UnsupportedError occurs if the list is fixed-length.
void insert(int index, E element) { if (index < 0 || index > length) { throw new RangeError.range(index, 0, length); } if (index == this.length) { add(element); return; } // We are modifying the length just below the is-check. Without the check // Array.copy could throw an exception, leaving the list in a bad state // (with a length that has been increased, but without a new element). if (index is! int) throw new ArgumentError(index); this.length++; setRange(index + 1, this.length, this, index); this[index] = element; }
void insertAll(int index, Iterable<E> iterable) #
Inserts all objects of iterable at position index in this list.
This increases the length of the list by the length of iterable and shifts all later objects towards the end of the list.
An error occurs if the index is less than 0 or greater than length. An UnsupportedError occurs if the list is fixed-length.
void insertAll(int index, Iterable<E> iterable) { if (index < 0 || index > length) { throw new RangeError.range(index, 0, length); } if (iterable is EfficientLength) { iterable = iterable.toList(); } int insertionLength = iterable.length; // There might be errors after the length change, in which case the list // will end up being modified but the operation not complete. Unless we // always go through a "toList" we can't really avoid that. this.length += insertionLength; setRange(index + insertionLength, this.length, this, index); setAll(index, iterable); }
dynamic internValue(E value) #
internValue(E value) { var el = value; if (el is EmbeddedPersistentObject) { el._parent = _parent; el._pathToMe = _pathToMe; return el.map; } if (el is PersistentObject) { return el.dbRef; } return value; }
String join([String separator = ""]) #
Converts each element to a String and concatenates the strings.
Converts each element to a String by calling Object.toString on it. Then concatenates the strings, optionally separated by the separator string.
String join([String separator = ""]) { int length = this.length; if (!separator.isEmpty) { if (length == 0) return ""; String first = "${this[0]}"; if (length != this.length) { throw new ConcurrentModificationError(this); } StringBuffer buffer = new StringBuffer(first); for (int i = 1; i < length; i++) { buffer.write(separator); buffer.write(this[i]); if (length != this.length) { throw new ConcurrentModificationError(this); } } return buffer.toString(); } else { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < length; i++) { buffer.write(this[i]); if (length != this.length) { throw new ConcurrentModificationError(this); } } return buffer.toString(); } }
int lastIndexOf(Object element, [int startIndex]) #
Returns the last index in the list a
of the given
element, starting
the search at index
startIndex to 0.
Returns -1 if
element is not found.
int lastIndexOf(Object element, [int startIndex]) { if (startIndex == null) { startIndex = this.length - 1; } else { if (startIndex < 0) { return -1; } if (startIndex >= this.length) { startIndex = this.length - 1; } } for (int i = startIndex; i >= 0; i--) { if (this[i] == element) { return i; } } return -1; }
dynamic lastWhere(bool test(E element), {Object orElse()}) #
Returns the last element that satisfies the given predicate test.
If none matches, the result of invoking the
orElse function is
returned. By default, when
orElse is null
, a StateError is
thrown.
dynamic lastWhere(bool test(E element), { Object orElse() }) { int length = this.length; for (int i = length - 1; i >= 0; i--) { E element = this[i]; if (test(element)) return element; if (length != this.length) { throw new ConcurrentModificationError(this); } } if (orElse != null) return orElse(); throw new StateError("No matching element"); }
Iterable map(f(E element)) #
Returns a lazy Iterable where each element e
of this
is replaced
by the result of f(e)
.
This method returns a view of the mapped elements. As long as the returned Iterable is not iterated over, the supplied function f will not be invoked. The transformed elements will not be cached. Iterating multiple times over the the returned Iterable will invoke the supplied function f multiple times on the same element.
Iterable map(f(E element)) => new MappedListIterable(this, f);
E reduce(E combine(E previousValue, E element)) #
Reduces a collection to a single value by iteratively combining elements of the collection using the provided function.
Example of calculating the sum of an iterable:
iterable.reduce((value, element) => value + element);
E reduce(E combine(E previousValue, E element)) { if (length == 0) throw new StateError("No elements"); E value = this[0]; for (int i = 1; i < length; i++) { value = combine(value, this[i]); } return value; }
bool remove(Object element) #
Removes the first occurence of value
from this list.
Returns true if value
was in the list, false otherwise.
List<String> parts = ['head', 'shoulders', 'knees', 'toes'];
parts.remove('head'); // true
parts.join(', '); // 'shoulders, knees, toes'
The method has no effect if value
was not in the list.
// Note: 'head' has already been removed.
parts.remove('head'); // false
parts.join(', '); // 'shoulders, knees, toes'
An UnsupportedError occurs if the list is fixed-length.
bool remove(Object element) { for (int i = 0; i < this.length; i++) { if (this[i] == element) { this.setRange(i, this.length - 1, this, i + 1); this.length -= 1; return true; } } return false; }
E removeAt(int index) #
Removes the object at position index from this list.
This method reduces the length of this
by one and moves all later objects
down by one position.
Returns the removed object.
The
index must be in the range 0 ≤ index < length
.
Throws an UnsupportedError if this is a fixed-length list. In that case the list is not modified.
E removeAt(int index) { E result = this[index]; setRange(index, this.length - 1, this, index + 1); length--; return result; }
E removeLast() #
Pops and returns the last object in this list.
Throws an UnsupportedError if this is a fixed-length list.
E removeLast() { if (length == 0) { throw new StateError("No elements"); } E result = this[length - 1]; length--; return result; }
void removeRange(int start, int end) #
Removes the objects in the range start inclusive to end exclusive.
The
start and
end indices must be in the range
0 ≤ index ≤ length
, and start ≤ end
.
Throws an UnsupportedError if this is a fixed-length list. In that case the list is not modified.
void removeRange(int start, int end) { _rangeCheck(start, end); int length = end - start; setRange(start, this.length - length, this, end); this.length -= length; }
void removeWhere(bool test(E element)) #
Removes all objects from this list that satisfy test.
An object o
satisfies
test if test(o)
is true.
List<String> numbers = ['one', 'two', 'three', 'four'];
numbers.removeWhere((item) => item.length == 3);
numbers.join(', '); // 'three, four'
Throws an UnsupportedError if this is a fixed-length list.
void removeWhere(bool test(E element)) { _filter(this, test, false); }
void replaceRange(int start, int end, Iterable<E> newContents) #
Removes the objects in the range
start inclusive to
end exclusive
and inserts the contents of replacement
in its place.
List<int> list = [1, 2, 3, 4, 5];
list.replaceRange(1, 4, [6, 7]);
list.join(', '); // '1, 6, 7, 5'
An error occurs if
start..
end is not a valid range for this
.
void replaceRange(int start, int end, Iterable<E> newContents) { _rangeCheck(start, end); if (newContents is! EfficientLength) { newContents = newContents.toList(); } int removeLength = end - start; int insertLength = newContents.length; if (removeLength >= insertLength) { int delta = removeLength - insertLength; int insertEnd = start + insertLength; int newLength = this.length - delta; this.setRange(start, insertEnd, newContents); if (delta != 0) { this.setRange(insertEnd, newLength, this, end); this.length = newLength; } } else { int delta = insertLength - removeLength; int newLength = this.length + delta; int insertEnd = start + insertLength; // aka. end + delta. this.length = newLength; this.setRange(insertEnd, newLength, this, end); this.setRange(start, insertEnd, newContents); } }
void retainWhere(bool test(E element)) #
Removes all objects from this list that fail to satisfy test.
An object o
satisfies
test if test(o)
is true.
List<String> numbers = ['one', 'two', 'three', 'four'];
numbers.retainWhere((item) => item.length == 3);
numbers.join(', '); // 'one, two'
Throws an UnsupportedError if this is a fixed-length list.
void retainWhere(bool test(E element)) { _filter(this, test, true); }
void setAll(int index, Iterable<E> iterable) #
Overwrites objects of this
with the objects of
iterable, starting
at position
index in this list.
List<String> list = ['a', 'b', 'c'];
list.setAll(1, ['bee', 'sea']);
list.join(', '); // 'a, bee, sea'
This operation does not increase the length of this
.
The index must be non-negative and no greater than length.
The iterable must not have more elements than what can fit from index to length.
If iterable
is based on this list, its values may change /during/ the
setAll
operation.
void setAll(int index, Iterable<E> iterable) { if (iterable is List) { setRange(index, index + iterable.length, iterable); } else { for (E element in iterable) { this[index++] = element; } } }
void setDirty(String propertyName) #
void setDirty(String propertyName) { _parent.setDirty(_pathToMe); }
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) #
Copies the objects of iterable, skipping skipCount objects first, into the range start, inclusive, to end, exclusive, of the list.
List<int> list1 = [1, 2, 3, 4];
List<int> list2 = [5, 6, 7, 8, 9];
// Copies the 4th and 5th items in list2 as the 2nd and 3rd items
// of list1.
list1.setRange(1, 3, list2, 3);
list1.join(', '); // '1, 8, 9, 4'
The
start and
end indices must satisfy 0 ≤ start ≤ end ≤ length
.
If
start equals
end, this method has no effect.
The
iterable must have enough objects to fill the range from start
to end
after skipping
skipCount objects.
If iterable
is this list, the operation will copy the elements originally
in the range from skipCount
to skipCount + (end - start)
to the
range start
to end
, even if the two ranges overlap.
If iterable
depends on this list in some other way, no guarantees are
made.
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) { _rangeCheck(start, end); int length = end - start; if (length == 0) return; if (skipCount < 0) throw new ArgumentError(skipCount); List otherList; int otherStart; // TODO(floitsch): Make this accept more. if (iterable is List) { otherList = iterable; otherStart = skipCount; } else { otherList = iterable.skip(skipCount).toList(growable: false); otherStart = 0; } if (otherStart + length > otherList.length) { throw new StateError("Not enough elements"); } if (otherStart < start) { // Copy backwards to ensure correct copy if [from] is this. for (int i = length - 1; i >= 0; i--) { this[start + i] = otherList[otherStart + i]; } } else { for (int i = 0; i < length; i++) { this[start + i] = otherList[otherStart + i]; } } }
void shuffle([Random random]) #
void shuffle([Random random]) { if (random == null) random = new Random(); int length = this.length; while (length > 1) { int pos = random.nextInt(length); length -= 1; var tmp = this[length]; this[length] = this[pos]; this[pos] = tmp; } }
E singleWhere(bool test(E element)) #
Returns the single element that satisfies test. If no or more than one element match then a StateError is thrown.
E singleWhere(bool test(E element)) { int length = this.length; E match = null; bool matchFound = false; for (int i = 0; i < length; i++) { E element = this[i]; if (test(element)) { if (matchFound) { throw new StateError("More than one matching element"); } matchFound = true; match = element; } if (length != this.length) { throw new ConcurrentModificationError(this); } } if (matchFound) return match; throw new StateError("No matching element"); }
Iterable<E> skipWhile(bool test(E element)) #
Returns an Iterable that skips elements while test is satisfied.
The filtering happens lazily. Every new Iterator of the returned
Iterable iterates over all elements of this
.
As long as the iterator's elements satisfy
test they are
discarded. Once an element does not satisfy the
test the iterator stops
testing and uses every later element unconditionally. That is, the elements
of the returned Iterable are the elements of this
starting from the
first element that does not satisfy
test.
Iterable<E> skipWhile(bool test(E element)) { return new SkipWhileIterable<E>(this, test); }
void sort([int compare(E a, E b)]) #
Sorts this list according to the order specified by the compare function.
The compare function must act as a Comparator.
List<String> numbers = ['one', 'two', 'three', 'four'];
// Sort from shortest to longest.
numbers.sort((x, y) => x.length.compareTo(y.length));
numbers.join(', '); // 'one, two, four, three'
The default List implementations use Comparable.compare if compare is omitted.
List<int> nums = [13, 2, -11];
nums.sort();
nums.join(', '); // '-11, 2, 13'
void sort([int compare(E a, E b)]) { if (compare == null) { var defaultCompare = Comparable.compare; compare = defaultCompare; } Sort.sort(this, compare); }
List<E> sublist(int start, [int end]) #
Returns a new list containing the objects from start inclusive to end exclusive.
List<String> colors = ['red', 'green', 'blue', 'orange', 'pink'];
colors.sublist(1, 3); // ['green', 'blue']
If
end is omitted, the length of this
is used.
colors.sublist(1); // ['green', 'blue', 'orange', 'pink']
An error occurs if
start is outside the range 0
.. length
or if
end is outside the range start
.. length
.
List<E> sublist(int start, [int end]) { if (end == null) end = this.length; _rangeCheck(start, end); int length = end - start; List<E> result = new List<E>()..length = length; for (int i = 0; i < length; i++) { result[i] = this[start + i]; } return result; }
Iterable<E> takeWhile(bool test(E element)) #
Returns an Iterable that stops once test is not satisfied anymore.
The filtering happens lazily. Every new Iterator of the returned
Iterable starts iterating over the elements of this
.
When the iterator encounters an element e
that does not satisfy
test,
it discards e
and moves into the finished state. That is, it does not
get or provide any more elements.
Iterable<E> takeWhile(bool test(E element)) { return new TakeWhileIterable<E>(this, test); }
List<E> toList({bool growable: true}) #
Creates a List containing the elements of this Iterable.
The elements are in iteration order. The list is fixed-length if growable is false.
List<E> toList({ bool growable: true }) { List<E> result; if (growable) { result = new List<E>()..length = length; } else { result = new List<E>(length); } for (int i = 0; i < length; i++) { result[i] = this[i]; } return result; }
dynamic toString() #
Returns a string representation of this object.
toString() => "PersistentList($_list)";
Iterable<E> where(bool test(E element)) #
Returns a lazy Iterable with all elements that satisfy the predicate test.
This method returns a view of the mapped elements. As long as the returned Iterable is not iterated over, the supplied function test will not be invoked. Iterating will not cache results, and thus iterating multiple times over the returned Iterable will invoke the supplied function test multiple times on the same element.
Iterable<E> where(bool test(E element)) => new WhereIterable<E>(this, test);