indexed_map 1.1.0
indexed_map: ^1.1.0 copied to clipboard
A high-performance hybrid data structure providing O(1) lookup by ID and ordered/indexed access, with comprehensive duplicate handling policies.
1.1.0 #
Performance #
- O(1)
indexOfId: Added a reverse index map (Map<I, int>) that keeps ID-to-position mappings synchronized on every mutation. This makesindexOfId()truly O(1) instead of the previous O(n) linear scan viaList.indexOf. All internal operations (add,insertAt,removeById,moveIdTo,upsertKeepingPosition,sortBy) now benefit from this. - O(1)
containsoverride: Uses the item'sindexIdfor instant membership checks instead of the inherited O(n) iteration fromIterableMixin. Note: this checks by ID, not by value equality. Useany((e) => e == element)for equality-based checks. - O(1)
first/lastoverrides: Direct list access instead of going through the lazyvaluesiterable. - Dedicated
Iterator: Custom_IndexedMapIteratoravoids creating intermediate lazy iterables on everyfor-inloop, with fail-fast concurrent-modification detection.
Bug Fixes #
ItemWrapperchanged fromfinaltobaseclass: Users can now extendItemWrapperto add custom metadata fields (e.g.,createdAt,isPinned). Previously, thefinalmodifier prevented this outside the library.operator []=with ID collision: Fixed a bug where settingmap[index] = newItemwith anewItem.indexIdthat already exists at a different index could corrupt internal state. The colliding entry is now properly removed first, and the insertion position is correctly adjusted when the colliding entry is before the target index.upsertKeepingPositionwith ID collision: Fixed a bug where changing an item's ID to one that already exists elsewhere in the map would leave orphaned entries. Both the old entry and the colliding entry are now properly cleaned up, with correct position adjustment when the collision is before the target.moveIdTobounds validation: Now returnsfalsefor out-of-boundstoIndex(negative or >= length) instead of throwing aRangeError. This is consistent with the method'sboolreturn type contract.insertAtwithreplaceMoveToEnd: Fixed index adjustment when the existing item is before the insertion point, preventing off-by-one positioning.removeWheresingle-pass evaluation: The predicate is now evaluated exactly once per item in a single pass, preventing state desync with stateful or non-deterministic predicates. Re-entrant mutations inside the predicate throwConcurrentModificationError.- Fail-fast iteration: Structural mutations (additions, removals, reordering) during iteration now throw
ConcurrentModificationError, matching standard Dart collection semantics. In-place value replacements at a fixed position (same-id swaps) do not invalidate iterators. - Constructor
map/listparams deprecated: These parameters are now@Deprecatedand ignored (previously they could corrupt internal state via shared mutable references). They will be removed in 2.0.0.
New API #
addAll(Iterable<T> items): Bulk-add items, returning the count of items actually inserted/replaced (respects duplicate policy).removeWhere(bool Function(T) test): Remove all items matching a predicate, returning the count removed.Iterable<I> get keys: Ordered iterable of all IDs.Map<I, T> toMap(): Returns a simpleMap<I, T>from ID to item (not wrappers).toString(): Meaningful debug output showing all entries (e.g.,IndexedMap(u1: User(u1, Alice), u2: User(u2, Bob))).
Documentation #
- Fixed performance table: corrected
indexOfIdcomplexity to O(1) (now accurate with reverse index). - Fixed
add(item)complexity note to clarify O(1) for new items. - Added documentation for all new API methods.
- Removed incorrect
ItemWrapperextension example from Advanced Features (replaced with accurate guidance). - Fixed
Messageconstructor in code examples to match actual API.
Infrastructure #
- Fixed
dependabot.yml: setpackage-ecosystemto"pub"(was empty string). - Removed unused
collectiondev dependency.
1.0.0 #
Initial Release #
Core Features:
- IndexedMap<T, I> - Hybrid data structure combining Map and List benefits
- O(1) ID-based lookups via
getById(id) - O(1) indexed access via
operator[](index) - Ordered iteration with full Iterable mixin support
- MapIndexable interface for type-safe ID extraction
Duplicate Handling Policies:
DuplicatePolicy.ignore- Keep existing, ignore duplicatesDuplicatePolicy.replaceKeepPosition- Replace in-place (default)DuplicatePolicy.replaceMoveToEnd- Replace and move to end
Core Operations:
add(item)- Append to end with duplicate policy handlinginsertAt(index, item)- Insert at specific positionremoveById(id)/removeAt(index)- Remove by ID or positionmoveIdTo(id, toIndex)- Reorder items by IDsortBy(comparator)- In-place sorting maintaining ID associationsupsertKeepingPosition()- Update with potential ID changes
Query Operations:
containsId(id)- Check ID existenceindexOfId(id)- Find position by IDgetWrapperById(id)- Access to internal wrapper metadata
Views and Access:
values- Read-only iterable of itemstoList()- Defensive copy as Listwrappers- Unmodifiable view of internal wrappersasMapView- Unmodifiable view of internal map
Construction Methods:
IndexedMap()- Empty constructor with optional policyIndexedMap.fromIterable()- Bulk construction from iterable