fast_immutable_collections 12.0.0
fast_immutable_collections: ^12.0.0 copied to clipboard
Immutable lists, sets, maps, and multimaps, which are as fast as their native mutable counterparts. Extension methods and comparators for native Dart collections.
Sponsored by MyText.ai
12.0.0 #
-
Breaking change:
IList,ISetandIMapthat compare by identity (isDeepEquals: falsein the constructor) now compare the collection objects themselves. In other words,collection1 == collection2is now the same asidentical(collection1, collection2)when deep equals is turned off. Previously, they compared their internal structure, so two different collections that shared the same internal structure were considered equal. However, this internal structure changes when a collection is flushed (which may also happen automatically, just by reading it), so their==andhashCodecould change, and they could be lost in aSetor asMapkeys. ThehashCodeof constant ones also changed each time it was read. Note: You can still usesame()to check if two collections share the same internal structure. Collections that compare by deep equals (isDeepEquals: truein the constructor) are the default, and are not affected. -
Fixed
IMap.fromJsonandIMap.toJsonfor non-String keys when used withjson_serializable. IMap keys now work the same as regularMapkeys, including enums (https://github.com/marcglasberg/fast_immutable_collections/issues/39),DateTime,BigIntandUri(https://github.com/marcglasberg/fast_immutable_collections/issues/58), and custom key types with aJsonConverteror their ownfromJson/toJson(https://github.com/marcglasberg/fast_immutable_collections/issues/82). For example:@JsonSerializable() class MyClass { final IMap<MyEnum, bool> byEnum; final IMap<DateTime, String> byDate; }The
fromJsonKfunction now receives the JSON key string, except when the key type isbool,int,doubleornum, in which case it receives the parsed value (asjson_serializableexpects). If the key is not a string, number, bool,DateTime,BigInt,Urior enum,IMap.toJsonnow calls the key's owntoJsonmethod.Note: For
IMap<Object, V>andIMap<dynamic, V>,fromJsonKnow receives the JSON key string. Previously, the key was wrongly converted into abool. -
Much faster iteration (like
for (final item in ilist)) ofIList,ISetandIMapthat were not yet flushed. Previously, each item went through the iterators of all internal nodes created byaddandaddAll. Now each item is read only once, using very little extra memory, and without creating any lists. For example, iterating anIListof 10,000 items after 500addcalls is now about 480 times faster, and anISetorIMapafter 50addcalls, about 9 to 45 times faster. The same is true forIMap.entries,IMap.keysandIMap.values, including maps updated withaddorupdateof existing keys, and for methods likejoin,elementAtand[]. For example, after 200addcalls,IList.joinis about 48 times faster, andIList[]about 90 times faster. -
Much faster
flush(andunlock) ofIList,ISet,IMapandIMapOfSetscreated by manyadd,addAllandupdatecalls. These calls create a chain of internal nodes, and flushing used to iterate this chain, which was slow because each item went through the iterators of all the nodes above it. Now flushing walks down the chain only once, without iterating it. Since flushing happens automatically after a number of operations, this also makes many consecutive additions much faster. For example, adding 1000 items to anIListof 10,000 items is now about 800 times faster, and to anISetorIMapof 10,000 items, about 12 to 20 times faster. -
Faster
where,map,any,every,forEach,fold,toListandtoSetofIListandISet(and of thekeysandvaluesofIMap) that were not yet flushed, since they no longer go through the iterators of all internal nodes. For example,whereandanyare now 16 to 50 times faster after 20addcalls,IList.toListis 3 to 12 times faster, andtoList(growable: false)is 1.5 to 2.9 times faster. -
ISet.toListis about 5 times faster for sets that are already flushed. -
Fixed
ISet.anyItemthrowingStateErrorfor a non-empty set created by adding items to an empty set. For example:ISet<int>([]).addAll([1]).anyItem. -
Fixed
IList.singleandISet.singlereturning an item, instead of throwingStateError, after adding items to a list or set with a single item. For example:IList([1]).addAll([2]).singlereturned1. -
ISet.differenceandISet.intersectionare about 1.4 times faster for sets that were not yet flushed, and their results now keep the iteration order of the set. -
Prevented the possibility of stack overflows in many methods (like
length,contains,[],first,entriesand iteration) ofIList,ISetandIMapwith a large number of unflushed operations. Note that this was only possible whenImmutableCollection.autoFlushwasfalse. -
Fixed
ImmutableCollection.resetAllConfigurations()not resettingIList.defaultConfig,ISet.defaultConfigandIMap.defaultConfig. It now also resetsIMapOfSets.defaultConfig. -
Fixed
equalItemsofIListandISet, andequalItemsToIMapofIMap, returningfalsefor collections with equal items but different configurations, after theirhashCodewas calculated. Also fixedequalItemsAndConfigreturningfalsefor collections that compare by identity and have equal items and configurations, after theirhashCodewas calculated. -
Fixed
ISet.withConfig(iset, config)not sorting the set whenconfig.sortistrueandisetis anISet. For example,ISet.withConfig({3, 1, 2}.lock, ConfigSet(sort: true))returned[3, 1, 2]. -
Fixed
IMap.cast()throwing aTypeErrorfor maps that were not yet flushed (for example, afteradd). Now, as documented, if the map is already anIMap<RK, RV>, it's returned unchanged. -
Fixed
IMap.toValueSet(compare: ...)failing an assertion. Now it sorts the values with the givencomparefunction. -
Fixed
IMap.unlockSortednot sorting the map when the map'sConfigMap.sortisfalse. -
Fixed
IMap.entryOrNullthrowing for a key that exists with anullvalue. -
Fixed
IMapOfSets.withConfig(null, config)ignoring the givenconfig, andIMapOfSets.removeValuesandIMapOfSets.removeValuesWherelosing the map configuration (likesortKeys). -
Fixed
remove,removeAllandremoveManyof constant and empty lists,removeof constant and empty sets, andremoveandremoveWhereof constant and empty maps, returning a new collection instead of the same instance when nothing is removed. -
Fixed
IList<Never>().addAll(...)throwing whenImmutableCollection.disallowUnsafeConstructorsistrue. -
Fixed
sumBythrowingUnsupportedErrorfor empty iterables when the result type isnum. For example,<num>[].sumBy((e) => e)now returns0. -
Fixed
compareObjectignoringnullsBefore: truewhen comparing the keys and values ofMapEntrys. -
Fixed
ListMap.mapnot keeping the order of theListMap. -
Fixed
lookupofModifiableSetFromISetandUnmodifiableSetFromISet(returned byISet.unlockLazyandISet.unlockView) returning the given element, instead of the element that is in the set. -
Fixed the docs of
isFirst,isNotFirst,isLastandisNotLast, which said they returnnullfor empty iterables.
11.2.1 #
- Fixed the outdated benchmarks in
example/benchmark(https://github.com/marcglasberg/fast_immutable_collections/issues/85). The package code itself is unchanged.
11.2.0 #
-
Added
cachedmethod andCacheKeyclass for caching derived computations onIList,ISet, andIMap.Since immutable collections never change, any value derived from their contents is stable and can be safely cached. The new
cachedmethod lets you lazily compute and cache a derived value (like an index map) inside the collection instance itself, so subsequent calls return the cached result in O (1).Define a
CacheKey<C, R>that pairs a cache identity with a typed computation function. Usestatic finalor top-level variables for keys so the same object reference is reused across calls:class PairState { final IList<Pair> pairs; static final _byId = CacheKey<IList<Pair>, Map<Id, Pair>>( (list) => {for (var p in list) p.id: p}, ); Pair? findById(Id id) => pairs.cached(_byId)[id]; }The first call to
cachedbuilds the map in O (n) and caches it. Every subsequent call is O (1). When the collection is replaced with a new instance (e.g., an item is added), the old cache is garbage-collected with the old instance, and the new one builds its own cache on first access.Multiple cache keys can be used on the same collection, each caching independently:
static final _byId = CacheKey<IList<User>, Map<String, User>>( (list) => {for (var u in list) u.id: u}, ); static final _byEmail = CacheKey<IList<User>, Map<String, User>>( (list) => {for (var u in list) u.email: u}, );Notes:
- The cache adds zero overhead to collections that don't use it (a single null pointer).
- The cache survives
flush()since the collection identity is preserved. - Constant collections (
const IList.empty(),const IListConst(...), etc.) supportcachedbut compute the value each time without caching, since they cannot hold mutable state. CacheKeycan beconstwhen using a static or top-level function reference.
11.1.0 #
- Added helper extension method
IList<IList<T>>.putXY()for setting values in
2D lists, using x,y coordinates.
11.0.4 #
- Doc improvements.
10.2.4 #
- Optimized
IMap.update().
10.2.3 #
- Improved
IList.zip()generic typing.
10.2.2 #
-
You can now declare empty lists, sets and maps like this (https://github.com/marcglasberg/fast_immutable_collections/pull/74):
const IList<String>.empty(); const ISet<String>.empty(); const IMap<String, int>.empty(); -
Better inference for sumBy returning zero (https://github.com/marcglasberg/fast_immutable_collections/pull/71).
10.0.0 #
- Removed tuples in favor of records.
9.2.1 #
- @useResult annotation to signal that a method should return a copy of the collection, instead of mutating it.
9.1.6 #
- Small docs improvement.
9.1.5 #
- Fixed type erasure in IMap.toJson and build issue for benchmark app.
9.1.4 #
- Removed unnecessary map creation when deserializing IMap from Json.
- Bumped environment to '>=2.14.0 <3.0.0'
9.1.1 #
- Function
compareObjectnow also compares enums by their name.
9.0.0 #
- Version bump of dependencies: collection: ^1.17.0, meta: ^1.8.0
8.2.0 #
IList.replaceBymethod lets you define a function to transform an item at a specific index location.
8.1.1 #
IList.indexOfextension fix (doesn't break anymore when list is empty and start is zero).
8.1.0 #
Iterable.intersectsWithextension.
8.0.0 #
- Breaking change:
IList.replaceFirstWheresignature is nowIList<T> replaceFirstWhere(bool Function(T item) test, T Function(T? item) replacement, {bool addIfNotFound = false})instead ofIList<T> replaceFirstWhere(bool Function(T item) test, T to, {bool addIfNotFound = false})In case this change breaks your code, the fix is simple. Instead of something likeilist.replaceFirstWhere((String item) => item=="1", "2")do this:ilist.replaceFirstWhere((String item) => item=="1", (_) => "2")
1.0.0 #
- Initial version: 2021/01/12
