From c770ed387ac5d949fddfd4b2d665ed48ed11146f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 18 Dec 2024 19:29:26 +0100 Subject: [PATCH 1/6] Adds `[Heap]PriorityQueue.of` constructor. Introduces efficient "heapify" algorithm for converting an unsorted list to a heap-sorted list, using it for the `of` constructor, and after a large `addAll` operation, when it's presumed faster than just bubbling down all the new elements. Also rewrites `HeapPriorityQueue` to use a growable list as backing array, instead of implementing the same thing using the double-when-full algorithm, and still having to deal with nullable cells. The platform growable list implementation is assumed to efficiently avoid some of those `null` checks. --- pkgs/collection/CHANGELOG.md | 3 + pkgs/collection/lib/src/priority_queue.dart | 179 +++++++++--------- pkgs/collection/test/priority_queue_test.dart | 29 +++ 3 files changed, 125 insertions(+), 86 deletions(-) diff --git a/pkgs/collection/CHANGELOG.md b/pkgs/collection/CHANGELOG.md index 0123f87fc..0e80eda21 100644 --- a/pkgs/collection/CHANGELOG.md +++ b/pkgs/collection/CHANGELOG.md @@ -5,6 +5,9 @@ `Map.entries`. - Explicitly mark `BoolList` as `abstract interface` - Address diagnostics from `strict_top_level_inference`. +- Optimize equality and hash code for maps by using `update` and a `values` + iterator to avoid extra lookups. +- Add `PriorityQueue.of` constructor and optimize adding many elements. ## 1.19.1 diff --git a/pkgs/collection/lib/src/priority_queue.dart b/pkgs/collection/lib/src/priority_queue.dart index 11b0348a2..2055cdfdb 100644 --- a/pkgs/collection/lib/src/priority_queue.dart +++ b/pkgs/collection/lib/src/priority_queue.dart @@ -36,6 +36,19 @@ abstract class PriorityQueue { factory PriorityQueue([int Function(E, E)? comparison]) = HeapPriorityQueue; + /// Create a new priority queue. + /// + /// The [comparison] is a [Comparator] used to compare the priority of + /// elements. An element that compares as less than another element has + /// a higher priority. + /// + /// If [comparison] is omitted, it defaults to [Comparable.compare]. If this + /// is the case, `E` must implement [Comparable], and this is checked at + /// runtime for every comparison. + factory PriorityQueue.of( + Iterable elements, int Function(E, E) comparison) = + HeapPriorityQueue.of; + /// Number of elements in the queue. int get length; @@ -169,27 +182,16 @@ abstract class PriorityQueue { /// * The [toSet] operation effectively adds each element to the new set, taking /// an expected O(n*log(n)) time. class HeapPriorityQueue implements PriorityQueue { - /// Initial capacity of a queue when created, or when added to after a - /// [clear]. - /// - /// Number can be any positive value. Picking a size that gives a whole - /// number of "tree levels" in the heap is only done for aesthetic reasons. - static const int _initialCapacity = 7; - /// The comparison being used to compare the priority of elements. final Comparator comparison; /// List implementation of a heap. - List _queue = List.filled(_initialCapacity, null); - - /// Number of elements in queue. - /// - /// The heap is implemented in the first [_length] entries of [_queue]. - int _length = 0; + List _queue; /// Modification count. /// /// Used to detect concurrent modifications during iteration. + /// Incremented whenever an element is added or removed. int _modificationCount = 0; /// Create a new priority queue. @@ -202,31 +204,72 @@ class HeapPriorityQueue implements PriorityQueue { /// is the case, `E` must implement [Comparable], and this is checked at /// runtime for every comparison. HeapPriorityQueue([int Function(E, E)? comparison]) - : comparison = comparison ?? defaultCompare; + : comparison = comparison ?? defaultCompare, + _queue = []; + + /// Creates a new priority queue containing [elements]. + /// + /// The [comparison] is a [Comparator] used to compare the priority of + /// elements. An element that compares as less than another element has + /// a higher priority. + HeapPriorityQueue.of(Iterable elements, int Function(E, E) this.comparison) + : _queue = elements.toList() { + _heapify(); + } - E _elementAt(int index) => _queue[index] ?? (null as E); + /// Converts an unordered list of elements to a heap-ordered list of elements. + /// + /// Does so by ordering sub-trees iteratively, then bubbling their parent + /// down into the two ordered subtrees. + /// Trivially ignores the last half of elements, which have no children. + /// Does a number of bubble-down steps that is bounded by the number + /// of elements. Each bubble-down step does two comparisons. + void _heapify() { + // Last non-leaf node's index, negative for empty or one-element queue. + var cursor = _queue.length ~/ 2 - 1; + while (cursor >= 0) { + _bubbleDown(_queue[cursor], cursor); + cursor -= 1; + } + } @override void add(E element) { _modificationCount++; - _add(element); + _queue.add(element); + _bubbleUp(element, _queue.length - 1); } @override void addAll(Iterable elements) { - var modified = 0; - for (var element in elements) { - modified = 1; - _add(element); + var endIndex = _queue.length; + _queue.addAll(elements); + var newLength = _queue.length; + var addedCount = newLength - endIndex; + if (addedCount == 0) return; + _modificationCount++; + // Approximation for when the time to bubble up all added elements, + // taking approx. addedCount * (log2(newLength)-1) comparisons worst-case, + // (bubble-up does one comparison per element per level), + // is slower than just heapifying the entire heap, which does + // newLength * 2 comparisons worst-case. + // Uses `endIndex.bitLength` instead of `newLength.bitLength` because + // if `addedCount` is greater than `newLength`, the bitLength won't matter + // for any non-trivial heap, and if not, every added element is a leaf + // element, so it only has to look at log2(endIndex) parents. + if (addedCount * endIndex.bitLength >= newLength * 2) { + _heapify(); + return; + } + for (var i = endIndex; i < newLength; i++) { + _bubbleUp(_queue[i], i); } - _modificationCount += modified; } @override void clear() { _modificationCount++; - _queue = const []; - _length = 0; + _queue.clear(); } @override @@ -243,27 +286,24 @@ class HeapPriorityQueue implements PriorityQueue { Iterable get unorderedElements => _UnorderedElementsIterable(this); @override - E get first { - if (_length == 0) throw StateError('No element'); - return _elementAt(0); - } + E get first => _queue.first; @override - bool get isEmpty => _length == 0; + bool get isEmpty => _queue.isEmpty; @override - bool get isNotEmpty => _length != 0; + bool get isNotEmpty => _queue.isNotEmpty; @override - int get length => _length; + int get length => _queue.length; @override bool remove(E element) { var index = _locate(element); if (index < 0) return false; _modificationCount++; - var last = _removeLast(); - if (index < _length) { + var last = _queue.removeLast(); + if (index < _queue.length) { var comp = comparison(last, element); if (comp <= 0) { _bubbleUp(last, index); @@ -284,19 +324,17 @@ class HeapPriorityQueue implements PriorityQueue { Iterable removeAll() { _modificationCount++; var result = _queue; - var length = _length; - _queue = const []; - _length = 0; - return result.take(length).cast(); + _queue = []; + return result.skip(0); // Hide list nature. } @override E removeFirst() { - if (_length == 0) throw StateError('No element'); + if (_queue.isEmpty) throw StateError('No element'); _modificationCount++; - var result = _elementAt(0); - var last = _removeLast(); - if (_length > 0) { + var result = _queue.first; + var last = _queue.removeLast(); + if (_queue.length > 0) { _bubbleDown(last, 0); } return result; @@ -306,34 +344,19 @@ class HeapPriorityQueue implements PriorityQueue { List toList() => _toUnorderedList()..sort(comparison); @override - Set toSet() { - var set = SplayTreeSet(comparison); - for (var i = 0; i < _length; i++) { - set.add(_elementAt(i)); - } - return set; - } + Set toSet() => SplayTreeSet(comparison)..addAll(_queue); @override List toUnorderedList() => _toUnorderedList(); - List _toUnorderedList() => - [for (var i = 0; i < _length; i++) _elementAt(i)]; + List _toUnorderedList() => _queue.toList(); /// Returns some representation of the queue. /// /// The format isn't significant, and may change in the future. @override String toString() { - return _queue.take(_length).toString(); - } - - /// Add element to the queue. - /// - /// Grows the capacity if the backing list is full. - void _add(E element) { - if (_length == _queue.length) _grow(); - _bubbleUp(element, _length++); + return _queue.skip(0).toString(); } /// Find the index of an object in the heap. @@ -343,7 +366,7 @@ class HeapPriorityQueue implements PriorityQueue { /// A matching object, `o`, must satisfy that /// `comparison(o, object) == 0 && o == object`. int _locate(E object) { - if (_length == 0) return -1; + if (_queue.isEmpty) return -1; // Count positions from one instead of zero. This gives the numbers // some nice properties. For example, all right children are odd, // their left sibling is even, and the parent is found by shifting @@ -355,14 +378,14 @@ class HeapPriorityQueue implements PriorityQueue { // in the heap will also have lower priority. do { var index = position - 1; - var element = _elementAt(index); + var element = _queue[index]; var comp = comparison(element, object); if (comp <= 0) { if (comp == 0 && element == object) return index; // Element may be in subtree. // Continue with the left child, if it is there. var leftChildPosition = position * 2; - if (leftChildPosition <= _length) { + if (leftChildPosition <= _queue.length) { position = leftChildPosition; continue; } @@ -375,18 +398,13 @@ class HeapPriorityQueue implements PriorityQueue { } // Then go to the right sibling of the left-child. position += 1; - } while (position > _length); // Happens if last element is a left child. + } while ( + position > _queue.length); // Happens if last element is a left child. } while (position != 1); // At root again. Happens for right-most element. return -1; } - E _removeLast() { - var newLength = _length - 1; - var last = _elementAt(newLength); - _queue[newLength] = null; - _length = newLength; - return last; - } + E _removeLast() => _queue.removeLast(); /// Place [element] in heap at [index] or above. /// @@ -396,7 +414,7 @@ class HeapPriorityQueue implements PriorityQueue { void _bubbleUp(E element, int index) { while (index > 0) { var parentIndex = (index - 1) ~/ 2; - var parent = _elementAt(parentIndex); + var parent = _queue[parentIndex]; if (comparison(element, parent) > 0) break; _queue[index] = parent; index = parentIndex; @@ -411,10 +429,10 @@ class HeapPriorityQueue implements PriorityQueue { /// swap it with the highest priority child. void _bubbleDown(E element, int index) { var rightChildIndex = index * 2 + 2; - while (rightChildIndex < _length) { + while (rightChildIndex < _queue.length) { var leftChildIndex = rightChildIndex - 1; - var leftChild = _elementAt(leftChildIndex); - var rightChild = _elementAt(rightChildIndex); + var leftChild = _queue[leftChildIndex]; + var rightChild = _queue[rightChildIndex]; var comp = comparison(leftChild, rightChild); int minChildIndex; E minChild; @@ -435,8 +453,8 @@ class HeapPriorityQueue implements PriorityQueue { rightChildIndex = index * 2 + 2; } var leftChildIndex = rightChildIndex - 1; - if (leftChildIndex < _length) { - var child = _elementAt(leftChildIndex); + if (leftChildIndex < _queue.length) { + var child = _queue[leftChildIndex]; var comp = comparison(element, child); if (comp > 0) { _queue[index] = child; @@ -445,17 +463,6 @@ class HeapPriorityQueue implements PriorityQueue { } _queue[index] = element; } - - /// Grows the capacity of the list holding the heap. - /// - /// Called when the list is full. - void _grow() { - var newCapacity = _queue.length * 2 + 1; - if (newCapacity < _initialCapacity) newCapacity = _initialCapacity; - var newQueue = List.filled(newCapacity, null); - newQueue.setRange(0, _length, _queue); - _queue = newQueue; - } } /// Implementation of [HeapPriorityQueue.unorderedElements]. diff --git a/pkgs/collection/test/priority_queue_test.dart b/pkgs/collection/test/priority_queue_test.dart index f07a1a39e..abf7b6926 100644 --- a/pkgs/collection/test/priority_queue_test.dart +++ b/pkgs/collection/test/priority_queue_test.dart @@ -23,6 +23,35 @@ void testDefault() { }); testInt(PriorityQueue.new); testCustom(PriorityQueue.new); + + group('(Heap)PriorityQueue.of returns functional priority queue', () { + List extract(PriorityQueue queue) { + var result = []; + while (queue.isNotEmpty) { + result.add(queue.removeFirst()); + } + return result; + } + + for (var i = 0; i < 1024; i = i * 2 + 1) { + test('size $i', () { + var input = List.generate(i, (x) => x); + for (var j = 0; j < 5; j++) { + var copy = (input.toList()..shuffle()).where((_) => true); + { + var queue = HeapPriorityQueue.of(copy, (a, b) => a - b); + var elements = extract(queue); + expect(elements, input); + } + { + var queue = HeapPriorityQueue.of(copy, (a, b) => a - b); + var elements = extract(queue); + expect(elements, input); + } + } + }); + } + }); } void testInt(PriorityQueue Function() create) { From 396140fb7049eb59b222f770d2e3d8316584d827 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 18 Dec 2024 19:36:02 +0100 Subject: [PATCH 2/6] Fix analyzer warnings. --- pkgs/collection/lib/src/priority_queue.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkgs/collection/lib/src/priority_queue.dart b/pkgs/collection/lib/src/priority_queue.dart index 2055cdfdb..e27128afe 100644 --- a/pkgs/collection/lib/src/priority_queue.dart +++ b/pkgs/collection/lib/src/priority_queue.dart @@ -183,7 +183,7 @@ abstract class PriorityQueue { /// an expected O(n*log(n)) time. class HeapPriorityQueue implements PriorityQueue { /// The comparison being used to compare the priority of elements. - final Comparator comparison; + final int Function(E, E) comparison; /// List implementation of a heap. List _queue; @@ -212,7 +212,7 @@ class HeapPriorityQueue implements PriorityQueue { /// The [comparison] is a [Comparator] used to compare the priority of /// elements. An element that compares as less than another element has /// a higher priority. - HeapPriorityQueue.of(Iterable elements, int Function(E, E) this.comparison) + HeapPriorityQueue.of(Iterable elements, this.comparison) : _queue = elements.toList() { _heapify(); } @@ -334,7 +334,7 @@ class HeapPriorityQueue implements PriorityQueue { _modificationCount++; var result = _queue.first; var last = _queue.removeLast(); - if (_queue.length > 0) { + if (_queue.isNotEmpty) { _bubbleDown(last, 0); } return result; @@ -404,8 +404,6 @@ class HeapPriorityQueue implements PriorityQueue { return -1; } - E _removeLast() => _queue.removeLast(); - /// Place [element] in heap at [index] or above. /// /// Put element into the empty cell at `index`. From 8a4489184202d068ded6fcfdd44c45ed468821e3 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 3 Feb 2025 11:23:41 +0100 Subject: [PATCH 3/6] Update priority_queue.dart Update docs. Let `removeAll` return a list. It's wasteful not to. As an implementation class, it's OK to depend on implementation. --- pkgs/collection/lib/src/priority_queue.dart | 50 ++++++++++++--------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/pkgs/collection/lib/src/priority_queue.dart b/pkgs/collection/lib/src/priority_queue.dart index e27128afe..4e6c1caee 100644 --- a/pkgs/collection/lib/src/priority_queue.dart +++ b/pkgs/collection/lib/src/priority_queue.dart @@ -22,9 +22,9 @@ import 'utils.dart'; /// always give equal objects the same priority, /// otherwise [contains] or [remove] might not work correctly. abstract class PriorityQueue { - /// Creates an empty [PriorityQueue]. + /// Creates an empty priority queue. /// - /// The created [PriorityQueue] is a plain [HeapPriorityQueue]. + /// The created `PriorityQueue` is a plain [HeapPriorityQueue]. /// /// The [comparison] is a [Comparator] used to compare the priority of /// elements. An element that compares as less than another element has @@ -36,15 +36,16 @@ abstract class PriorityQueue { factory PriorityQueue([int Function(E, E)? comparison]) = HeapPriorityQueue; - /// Create a new priority queue. + /// Creates a new [HeapPriorityQueue] containing [elements]. /// /// The [comparison] is a [Comparator] used to compare the priority of /// elements. An element that compares as less than another element has /// a higher priority. /// - /// If [comparison] is omitted, it defaults to [Comparable.compare]. If this - /// is the case, `E` must implement [Comparable], and this is checked at - /// runtime for every comparison. + /// Unlike [PriorityQueue.new], the [comparison] cannot be omitted. + /// If the elements are comparable to each other, use [Comparable.compare] + /// as the comparison function, or use a more specialized function + /// if one is available. factory PriorityQueue.of( Iterable elements, int Function(E, E) comparison) = HeapPriorityQueue.of; @@ -164,23 +165,30 @@ abstract class PriorityQueue { /// /// The elements are kept in a heap structure, /// where the element with the highest priority is immediately accessible, -/// and modifying a single element takes -/// logarithmic time in the number of elements on average. +/// and modifying a single element takes, on average, +/// logarithmic time in the number of elements. /// /// * The [add] and [removeFirst] operations take amortized logarithmic time, -/// O(log(n)), but may occasionally take linear time when growing the capacity -/// of the heap. -/// * The [addAll] operation works as doing repeated [add] operations. +/// O(log(*N*)) where *N* is the number of elements, but may occasionally +/// take linear time when growing the capacity of the heap. +/// * The [addAll] operation works by doing repeated [add] operations. +/// May be more efficient in some cases. /// * The [first] getter takes constant time, O(1). /// * The [clear] and [removeAll] methods also take constant time, O(1). /// * The [contains] and [remove] operations may need to search the entire -/// queue for the elements, taking O(n) time. -/// * The [toList] operation effectively sorts the elements, taking O(n*log(n)) +/// queue for the elements, taking O(*N*) time. +/// * The [toList] operation effectively sorts the elements, taking O(n * log(*N*)) /// time. /// * The [toUnorderedList] operation copies, but does not sort, the elements, /// and is linear, O(n). -/// * The [toSet] operation effectively adds each element to the new set, taking -/// an expected O(n*log(n)) time. +/// * The [toSet] operation effectively adds each element to the new +/// [SplayTreeSet], taking an expected O(n * log(*N*)) time. +/// +/// The [comparison] function is used to order elements, with earlier elements +/// having higher priority. That is, elements are extracted from the queue +/// in ascending [comparison] order. +/// If two elements have the same priority, their ordering is unspecified +/// and may be arbitary. class HeapPriorityQueue implements PriorityQueue { /// The comparison being used to compare the priority of elements. final int Function(E, E) comparison; @@ -316,16 +324,14 @@ class HeapPriorityQueue implements PriorityQueue { /// Removes all the elements from this queue and returns them. /// - /// The returned iterable has no specified order. - /// The operation does not copy the elements, - /// but instead keeps them in the existing heap structure, - /// and iterates over that directly. + /// The [HeapPriorityQueue] returns a [List] of its elements, + /// with no guaranteed order. @override - Iterable removeAll() { + List removeAll() { _modificationCount++; var result = _queue; _queue = []; - return result.skip(0); // Hide list nature. + return result; } @override @@ -401,7 +407,7 @@ class HeapPriorityQueue implements PriorityQueue { } while ( position > _queue.length); // Happens if last element is a left child. } while (position != 1); // At root again. Happens for right-most element. - return -1; + return -1; } /// Place [element] in heap at [index] or above. From 44bb5c61a7cccc17342d42acea8c1cbe46e34060 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 3 Apr 2025 17:24:21 -0500 Subject: [PATCH 4/6] [collection] explicitly make BoolList abstract interface (#875) It couldn't be extended in practice since it doesn't have a public constructor Also made the only implementations final --- pkgs/collection/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/collection/CHANGELOG.md b/pkgs/collection/CHANGELOG.md index 0e80eda21..5164fca3a 100644 --- a/pkgs/collection/CHANGELOG.md +++ b/pkgs/collection/CHANGELOG.md @@ -8,6 +8,7 @@ - Optimize equality and hash code for maps by using `update` and a `values` iterator to avoid extra lookups. - Add `PriorityQueue.of` constructor and optimize adding many elements. +- Address diagnostics from `strict_top_level_inference`. ## 1.19.1 From 52ab8c9ebce2f9ea0bfaba9ac538d7c8f779fe9d Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 18 Dec 2024 19:29:26 +0100 Subject: [PATCH 5/6] Adds `[Heap]PriorityQueue.of` constructor. Introduces efficient "heapify" algorithm for converting an unsorted list to a heap-sorted list, using it for the `of` constructor, and after a large `addAll` operation, when it's presumed faster than just bubbling down all the new elements. Also rewrites `HeapPriorityQueue` to use a growable list as backing array, instead of implementing the same thing using the double-when-full algorithm, and still having to deal with nullable cells. The platform growable list implementation is assumed to efficiently avoid some of those `null` checks. --- pkgs/collection/lib/src/priority_queue.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/collection/lib/src/priority_queue.dart b/pkgs/collection/lib/src/priority_queue.dart index 4e6c1caee..1887ece07 100644 --- a/pkgs/collection/lib/src/priority_queue.dart +++ b/pkgs/collection/lib/src/priority_queue.dart @@ -181,7 +181,7 @@ abstract class PriorityQueue { /// time. /// * The [toUnorderedList] operation copies, but does not sort, the elements, /// and is linear, O(n). -/// * The [toSet] operation effectively adds each element to the new +/// * The [toSet] operation effectively adds each element to the new /// [SplayTreeSet], taking an expected O(n * log(*N*)) time. /// /// The [comparison] function is used to order elements, with earlier elements @@ -326,6 +326,8 @@ class HeapPriorityQueue implements PriorityQueue { /// /// The [HeapPriorityQueue] returns a [List] of its elements, /// with no guaranteed order. + /// + /// If the elements are not needed, use [clear] instead. @override List removeAll() { _modificationCount++; @@ -407,7 +409,7 @@ class HeapPriorityQueue implements PriorityQueue { } while ( position > _queue.length); // Happens if last element is a left child. } while (position != 1); // At root again. Happens for right-most element. - return -1; + return -1; } /// Place [element] in heap at [index] or above. From fdcfb5ef79507ed15babe1883452ee66669c8858 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 4 Apr 2025 17:59:10 +0200 Subject: [PATCH 6/6] No long lines or typos. --- pkgs/collection/lib/src/priority_queue.dart | 6 +++--- pkgs/collection/test/priority_queue_test.dart | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkgs/collection/lib/src/priority_queue.dart b/pkgs/collection/lib/src/priority_queue.dart index 1887ece07..df5b43b4d 100644 --- a/pkgs/collection/lib/src/priority_queue.dart +++ b/pkgs/collection/lib/src/priority_queue.dart @@ -177,8 +177,8 @@ abstract class PriorityQueue { /// * The [clear] and [removeAll] methods also take constant time, O(1). /// * The [contains] and [remove] operations may need to search the entire /// queue for the elements, taking O(*N*) time. -/// * The [toList] operation effectively sorts the elements, taking O(n * log(*N*)) -/// time. +/// * The [toList] operation effectively sorts the elements, +/// taking O(n * log(*N*)) time. /// * The [toUnorderedList] operation copies, but does not sort, the elements, /// and is linear, O(n). /// * The [toSet] operation effectively adds each element to the new @@ -188,7 +188,7 @@ abstract class PriorityQueue { /// having higher priority. That is, elements are extracted from the queue /// in ascending [comparison] order. /// If two elements have the same priority, their ordering is unspecified -/// and may be arbitary. +/// and may be arbitrary. class HeapPriorityQueue implements PriorityQueue { /// The comparison being used to compare the priority of elements. final int Function(E, E) comparison; diff --git a/pkgs/collection/test/priority_queue_test.dart b/pkgs/collection/test/priority_queue_test.dart index abf7b6926..a7aafc4f6 100644 --- a/pkgs/collection/test/priority_queue_test.dart +++ b/pkgs/collection/test/priority_queue_test.dart @@ -307,9 +307,9 @@ void testConcurrentModification() { var q = HeapPriorityQueue((a, b) => a - b) ..addAll([6, 4, 2, 3, 5, 8]); var e = q.unorderedElements; - q.add(12); // Modifiation before creating iterator is not a problem. + q.add(12); // Modification before creating iterator is not a problem. var it = e.iterator; - q.add(7); // Modification after creatig iterator is a problem. + q.add(7); // Modification after creating iterator is a problem. expect(it.moveNext, throwsConcurrentModificationError); it = e.iterator; // New iterator is not affected. @@ -323,9 +323,9 @@ void testConcurrentModification() { var q = HeapPriorityQueue((a, b) => a - b) ..addAll([6, 4, 2, 3, 5, 8]); var e = q.unorderedElements; - q.addAll([12]); // Modifiation before creating iterator is not a problem. + q.addAll([12]); // Modification before creating iterator is not a problem. var it = e.iterator; - q.addAll([7]); // Modification after creatig iterator is a problem. + q.addAll([7]); // Modification after creating iterator is a problem. expect(it.moveNext, throwsConcurrentModificationError); it = e.iterator; // New iterator is not affected. expect(it.moveNext(), true); @@ -340,10 +340,10 @@ void testConcurrentModification() { ..addAll([6, 4, 2, 3, 5, 8]); var e = q.unorderedElements; expect(q.removeFirst(), - 2); // Modifiation before creating iterator is not a problem. + 2); // Modification before creating iterator is not a problem. var it = e.iterator; expect(q.removeFirst(), - 3); // Modification after creatig iterator is a problem. + 3); // Modification after creating iterator is a problem. expect(it.moveNext, throwsConcurrentModificationError); it = e.iterator; // New iterator is not affected.