diff --git a/lib/src/iterable/built_iterable.dart b/lib/src/iterable/built_iterable.dart index 7011f7d..db132f5 100644 --- a/lib/src/iterable/built_iterable.dart +++ b/lib/src/iterable/built_iterable.dart @@ -11,4 +11,6 @@ abstract class BuiltIterable implements Iterable { /// Converts to a [BuiltSet]. BuiltSet toBuiltSet(); + + const BuiltIterable(); } diff --git a/lib/src/list/built_list.dart b/lib/src/list/built_list.dart index 089cfbd..88abc7e 100644 --- a/lib/src/list/built_list.dart +++ b/lib/src/list/built_list.dart @@ -15,23 +15,27 @@ part of '../list.dart'; /// for the general properties of Built Collections. abstract class BuiltList implements Iterable, BuiltIterable { final List _list; - int? _hashCode; /// Instantiates with elements from an [Iterable]. factory BuiltList([Iterable iterable = const []]) { - if (iterable is _BuiltList && iterable.hasExactElementType(E)) { + if ((iterable is _BuiltList && iterable.hasExactElementType(E)) || + (iterable is _ConstBuiltList && iterable.hasExactElementType(E))) { return iterable as BuiltList; } else { return _BuiltList.from(iterable); } } + const factory BuiltList.fromList([List list]) = + _ConstBuiltList.withSafeList; + /// Instantiates with elements from an [Iterable]. /// /// `E` must not be `dynamic`. factory BuiltList.of(Iterable iterable) { - if (iterable is _BuiltList && iterable.hasExactElementType(E)) { - return iterable; + if ((iterable is _BuiltList && iterable.hasExactElementType(E)) || + iterable is _ConstBuiltList && iterable.hasExactElementType(E)) { + return iterable as BuiltList; } else { return _BuiltList.of(iterable); } @@ -61,10 +65,7 @@ abstract class BuiltList implements Iterable, BuiltIterable { /// A `BuiltList` is only equal to another `BuiltList` with equal elements in /// the same order. Then, the `hashCode` is guaranteed to be the same. @override - int get hashCode { - _hashCode ??= hashObjects(_list); - return _hashCode!; - } + int get hashCode => hashObjects(_list); /// Deep equality. /// @@ -234,11 +235,19 @@ abstract class BuiltList implements Iterable, BuiltIterable { // Internal. - BuiltList._(this._list); + const BuiltList._(this._list); } /// Default implementation of the public [BuiltList] interface. class _BuiltList extends BuiltList { + int? _hashCode; + + @override + int get hashCode { + _hashCode ??= super.hashCode; + return _hashCode!; + } + _BuiltList.withSafeList(List list) : super._(list); _BuiltList.from([Iterable iterable = const []]) @@ -265,6 +274,12 @@ class _BuiltList extends BuiltList { bool hasExactElementType(Type type) => E == type; } +/// An alternative implementation of BuiltList that supports a const constructor +class _ConstBuiltList extends BuiltList { + const _ConstBuiltList.withSafeList([List list = const []]) : super._(list); + bool hasExactElementType(Type type) => E == type; +} + /// Extensions for [BuiltList] on [List]. extension BuiltListExtension on List { /// Converts to a [BuiltList]. diff --git a/lib/src/list/list_builder.dart b/lib/src/list/list_builder.dart index a9a1413..4bc895f 100644 --- a/lib/src/list/list_builder.dart +++ b/lib/src/list/list_builder.dart @@ -13,7 +13,7 @@ part of '../list.dart'; /// for the general properties of Built Collections. class ListBuilder { late List _list; - _BuiltList? _listOwner; + BuiltList? _listOwner; /// Instantiates with elements from an [Iterable]. factory ListBuilder([Iterable iterable = const []]) { @@ -38,7 +38,7 @@ class ListBuilder { /// Replaces all elements with elements from an [Iterable]. void replace(Iterable iterable) { - if (iterable is _BuiltList) { + if (iterable is BuiltList) { _setOwner(iterable); } else { _setSafeList(List.from(iterable)); @@ -258,7 +258,7 @@ class ListBuilder { ListBuilder._uninitialized(); - void _setOwner(_BuiltList listOwner) { + void _setOwner(BuiltList listOwner) { _list = listOwner._list; _listOwner = listOwner; } diff --git a/lib/src/list_multimap/built_list_multimap.dart b/lib/src/list_multimap/built_list_multimap.dart index 3092ed5..8d95d0b 100644 --- a/lib/src/list_multimap/built_list_multimap.dart +++ b/lib/src/list_multimap/built_list_multimap.dart @@ -17,19 +17,16 @@ part of '../list_multimap.dart'; abstract class BuiltListMultimap { final Map> _map; - // Precomputed. - final BuiltList _emptyList = BuiltList(); - - // Cached. - int? _hashCode; - Iterable? _keys; - Iterable? _values; + const factory BuiltListMultimap.fromMap([Map> map]) = + _ConstBuiltListMultimap.withSafeMap; /// Instantiates with elements from a [Map], [ListMultimap] or /// [BuiltListMultimap]. factory BuiltListMultimap([multimap = const {}]) { if (multimap is _BuiltListMultimap && - multimap.hasExactKeyAndValueTypes(K, V)) { + multimap.hasExactKeyAndValueTypes(K, V) || + multimap is _ConstBuiltListMultimap && + multimap.hasExactKeyAndValueTypes(K, V)) { return multimap as BuiltListMultimap; } else if (multimap is Map) { return _BuiltListMultimap.copy(multimap.keys, (k) => multimap[k]); @@ -72,11 +69,12 @@ abstract class BuiltListMultimap { /// to be the same. @override int get hashCode { - _hashCode ??= hashObjects(_map.keys - .map((key) => hash2(key.hashCode, _map[key].hashCode)) - .toList(growable: false) - ..sort()); - return _hashCode!; + return hashObjects( + _map.keys + .map((key) => hash2(key.hashCode, _map[key].hashCode)) + .toList(growable: false) + ..sort(), + ); } /// Deep equality. @@ -109,7 +107,7 @@ abstract class BuiltListMultimap { /// As [ListMultimap], but results are [BuiltList]s and not mutable. BuiltList operator [](Object? key) { var result = _map[key]; - return result ?? _emptyList; + return result ?? BuiltList(); } /// As [ListMultimap.containsKey]. @@ -142,24 +140,18 @@ abstract class BuiltListMultimap { /// As [ListMultimap.keys], but result is stable; it always returns the same /// instance. - Iterable get keys { - _keys ??= _map.keys; - return _keys!; - } + Iterable get keys => _map.keys; /// As [ListMultimap.length]. int get length => _map.length; /// As [ListMultimap.values], but result is stable; it always returns the /// same instance. - Iterable get values { - _values ??= _map.values.expand((x) => x); - return _values!; - } + Iterable get values => _map.values.expand((x) => x); // Internal. - BuiltListMultimap._(this._map); + const BuiltListMultimap._(this._map); } /// Default implementation of the public [BuiltListMultimap] interface. @@ -177,5 +169,46 @@ class _BuiltListMultimap extends BuiltListMultimap { } } + // Precomputed. + final BuiltList _emptyList = BuiltList(); + + /// As [ListMultimap], but results are [BuiltList]s and not mutable. + @override + BuiltList operator [](Object? key) { + var result = _map[key]; + // Precomputed. + return result ?? _emptyList; + } + + // Cached. + int? _hashCode; + Iterable? _keys; + Iterable? _values; + + @override + Iterable get values { + _values ??= super.values; + return _values!; + } + + @override + Iterable get keys { + _keys ??= super.keys; + return _keys!; + } + + @override + int get hashCode { + _hashCode ??= super.hashCode; + return _hashCode!; + } + + bool hasExactKeyAndValueTypes(Type key, Type value) => K == key && V == value; +} + +class _ConstBuiltListMultimap extends BuiltListMultimap { + const _ConstBuiltListMultimap.withSafeMap( + [Map> src = const {}]) + : super._(src); bool hasExactKeyAndValueTypes(Type key, Type value) => K == key && V == value; } diff --git a/lib/src/list_multimap/list_multimap_builder.dart b/lib/src/list_multimap/list_multimap_builder.dart index 2c4b23b..d7bbc13 100644 --- a/lib/src/list_multimap/list_multimap_builder.dart +++ b/lib/src/list_multimap/list_multimap_builder.dart @@ -17,7 +17,7 @@ class ListMultimapBuilder { late Map> _builtMap; // Instance that _builtMap belongs to. If present, _builtMap must not be // mutated. - _BuiltListMultimap? _builtMapOwner; + BuiltListMultimap? _builtMapOwner; // ListBuilders for keys that are being changed. late Map> _builderMap; @@ -57,7 +57,7 @@ class ListMultimapBuilder { /// /// Any [ListBuilder]s associated with this collection are disconnected. void replace(dynamic multimap) { - if (multimap is _BuiltListMultimap) { + if (multimap is BuiltListMultimap) { _setOwner(multimap); } else if (multimap is Map) { _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]); @@ -178,7 +178,7 @@ class ListMultimapBuilder { ListMultimapBuilder._uninitialized(); - void _setOwner(_BuiltListMultimap builtListMultimap) { + void _setOwner(BuiltListMultimap builtListMultimap) { _builtMapOwner = builtListMultimap; _builtMap = builtListMultimap._map; _builderMap = >{}; diff --git a/lib/src/map/built_map.dart b/lib/src/map/built_map.dart index 99a3ec6..f7b7acd 100644 --- a/lib/src/map/built_map.dart +++ b/lib/src/map/built_map.dart @@ -19,11 +19,6 @@ abstract class BuiltMap { final _MapFactory? _mapFactory; final Map _map; - // Cached. - int? _hashCode; - Iterable? _keys; - Iterable? _values; - /// Instantiates with elements from a [Map] or [BuiltMap]. factory BuiltMap([map = const {}]) { if (map is _BuiltMap && map.hasExactKeyAndValueTypes(K, V)) { @@ -35,6 +30,9 @@ abstract class BuiltMap { } } + const factory BuiltMap.fromMap([Map map]) = + _ConstBuiltMap.withSafeMap; + /// Instantiates with elements from a [Map]. factory BuiltMap.from(Map map) { return _BuiltMap.copyAndCheckTypes(map.keys, (k) => map[k]); @@ -54,8 +52,7 @@ abstract class BuiltMap { /// Converts to a [MapBuilder] for modification. /// /// The `BuiltMap` remains immutable and can continue to be used. - MapBuilder toBuilder() => - MapBuilder._fromBuiltMap(this as _BuiltMap); + MapBuilder toBuilder() => MapBuilder._fromBuiltMap(this); /// Converts to a [MapBuilder], applies updates to it, and builds. BuiltMap rebuild(Function(MapBuilder) updates) => @@ -82,13 +79,10 @@ abstract class BuiltMap { /// A `BuiltMap` is only equal to another `BuiltMap` with equal key/value /// pairs in any order. Then, the `hashCode` is guaranteed to be the same. @override - int get hashCode { - _hashCode ??= hashObjects(_map.keys - .map((key) => hash2(key.hashCode, _map[key].hashCode)) - .toList(growable: false) - ..sort()); - return _hashCode!; - } + int get hashCode => hashObjects(_map.keys + .map((key) => hash2(key.hashCode, _map[key].hashCode)) + .toList(growable: false) + ..sort()); /// Deep equality. /// @@ -132,20 +126,14 @@ abstract class BuiltMap { bool get isNotEmpty => _map.isNotEmpty; /// As [Map.keys], but result is stable; it always returns the same instance. - Iterable get keys { - _keys ??= _map.keys; - return _keys!; - } + Iterable get keys => _map.keys; /// As [Map.length]. int get length => _map.length; /// As [Map.values], but result is stable; it always returns the same /// instance. - Iterable get values { - _values ??= _map.values; - return _values!; - } + Iterable get values => _map.values; /// As [Map.entries]. Iterable> get entries => _map.entries; @@ -156,11 +144,16 @@ abstract class BuiltMap { // Internal. - BuiltMap._(this._mapFactory, this._map); + const BuiltMap._(this._mapFactory, this._map); } /// Default implementation of the public [BuiltMap] interface. class _BuiltMap extends BuiltMap { + // Cached. + int? _hashCode; + Iterable? _keys; + Iterable? _values; + _BuiltMap.withSafeMap(_MapFactory? mapFactory, Map map) : super._(mapFactory, map); @@ -196,9 +189,32 @@ class _BuiltMap extends BuiltMap { } } + @override + Iterable get values { + _values ??= super.values; + return _values!; + } + + @override + Iterable get keys { + _keys ??= super.keys; + return _keys!; + } + + @override + int get hashCode { + _hashCode ??= super.hashCode; + return _hashCode!; + } + bool hasExactKeyAndValueTypes(Type key, Type value) => K == key && V == value; } +class _ConstBuiltMap extends BuiltMap { + const _ConstBuiltMap.withSafeMap([Map map = const {}]) + : super._(null, map); +} + /// Extensions for [BuiltMap] on [Map]. extension BuiltMapExtension on Map { /// Converts to a [BuiltMap]. diff --git a/lib/src/map/map_builder.dart b/lib/src/map/map_builder.dart index d44948d..a858e95 100644 --- a/lib/src/map/map_builder.dart +++ b/lib/src/map/map_builder.dart @@ -15,7 +15,7 @@ class MapBuilder { /// Used by [_createMap] to instantiate [_map]. The default value is `null`. _MapFactory? _mapFactory; late Map _map; - _BuiltMap? _mapOwner; + BuiltMap? _mapOwner; /// Instantiates with elements from a [Map] or [BuiltMap]. factory MapBuilder([map = const {}]) { @@ -38,7 +38,7 @@ class MapBuilder { /// Replaces all elements with elements from a [Map] or [BuiltMap]. void replace(Object map) { - if (map is _BuiltMap && map._mapFactory == _mapFactory) { + if (map is BuiltMap && map._mapFactory == _mapFactory) { _setOwner(map); } else if (map is BuiltMap) { var replacement = _createMap(); @@ -167,12 +167,12 @@ class MapBuilder { MapBuilder._uninitialized(); - MapBuilder._fromBuiltMap(_BuiltMap map) + MapBuilder._fromBuiltMap(BuiltMap map) : _mapFactory = map._mapFactory, _map = map._map, _mapOwner = map; - void _setOwner(_BuiltMap mapOwner) { + void _setOwner(BuiltMap mapOwner) { assert(mapOwner._mapFactory == _mapFactory, "Can't reuse a built map that uses a different base"); _mapOwner = mapOwner; diff --git a/lib/src/set/built_set.dart b/lib/src/set/built_set.dart index 7038902..82e44b4 100644 --- a/lib/src/set/built_set.dart +++ b/lib/src/set/built_set.dart @@ -18,7 +18,6 @@ typedef _SetFactory = Set Function(); abstract class BuiltSet implements Iterable, BuiltIterable { final _SetFactory? _setFactory; final Set _set; - int? _hashCode; /// Instantiates with elements from an [Iterable]. factory BuiltSet([Iterable iterable = const []]) => BuiltSet.from(iterable); @@ -41,6 +40,8 @@ abstract class BuiltSet implements Iterable, BuiltIterable { } } + const factory BuiltSet.fromSet([Set set]) = _ConstBuiltSet.withSafeSet; + /// Creates a [SetBuilder], applies updates to it, and builds. factory BuiltSet.build(Function(SetBuilder) updates) => (SetBuilder()..update(updates)).build(); @@ -48,8 +49,7 @@ abstract class BuiltSet implements Iterable, BuiltIterable { /// Converts to a [SetBuilder] for modification. /// /// The `BuiltSet` remains immutable and can continue to be used. - SetBuilder toBuilder() => - SetBuilder._fromBuiltSet(this as _BuiltSet); + SetBuilder toBuilder() => SetBuilder._fromBuiltSet(this); /// Converts to a [SetBuilder], applies updates to it, and builds. BuiltSet rebuild(Function(SetBuilder) updates) => @@ -67,9 +67,9 @@ abstract class BuiltSet implements Iterable, BuiltIterable { /// any order. Then, the `hashCode` is guaranteed to be the same. @override int get hashCode { - _hashCode ??= hashObjects( - _set.map((e) => e.hashCode).toList(growable: false)..sort()); - return _hashCode!; + return hashObjects( + _set.map((e) => e.hashCode).toList(growable: false)..sort(), + ); } /// Deep equality. @@ -223,11 +223,19 @@ abstract class BuiltSet implements Iterable, BuiltIterable { // Internal. - BuiltSet._(this._setFactory, this._set); + const BuiltSet._(this._setFactory, this._set); } /// Default implementation of the public [BuiltSet] interface. class _BuiltSet extends BuiltSet { + int? _hashCode; + + @override + int get hashCode { + _hashCode ??= super.hashCode; + return _hashCode!; + } + _BuiltSet.withSafeSet(_SetFactory? setFactory, Set set) : super._(setFactory, set); @@ -253,6 +261,10 @@ class _BuiltSet extends BuiltSet { bool hasExactElementType(Type type) => E == type; } +class _ConstBuiltSet extends BuiltSet { + const _ConstBuiltSet.withSafeSet([Set s = const {}]) : super._(null, s); +} + /// Extensions for [BuiltSet] on [Set]. extension BuiltSetExtension on Set { /// Converts to a [BuiltSet]. diff --git a/lib/src/set/set_builder.dart b/lib/src/set/set_builder.dart index 3d128aa..6dda7a2 100644 --- a/lib/src/set/set_builder.dart +++ b/lib/src/set/set_builder.dart @@ -15,7 +15,7 @@ class SetBuilder { /// Used by [_createSet] to instantiate [_set]. The default value is `null`. _SetFactory? _setFactory; late Set _set; - _BuiltSet? _setOwner; + BuiltSet? _setOwner; /// Instantiates with elements from an [Iterable]. factory SetBuilder([Iterable iterable = const []]) { @@ -38,7 +38,7 @@ class SetBuilder { /// Replaces all elements with elements from an [Iterable]. void replace(Iterable iterable) { - if (iterable is _BuiltSet && iterable._setFactory == _setFactory) { + if (iterable is BuiltSet && iterable._setFactory == _setFactory) { _withOwner(iterable); } else { // Can't use addAll because it requires an Iterable. @@ -184,12 +184,12 @@ class SetBuilder { SetBuilder._uninitialized(); - SetBuilder._fromBuiltSet(_BuiltSet set) + SetBuilder._fromBuiltSet(BuiltSet set) : _setFactory = set._setFactory, _set = set._set, _setOwner = set; - void _withOwner(_BuiltSet setOwner) { + void _withOwner(BuiltSet setOwner) { assert(setOwner._setFactory == _setFactory, "Can't reuse a built set that uses a different base"); _set = setOwner._set; diff --git a/lib/src/set_multimap/built_set_multimap.dart b/lib/src/set_multimap/built_set_multimap.dart index 469f599..de5fb52 100644 --- a/lib/src/set_multimap/built_set_multimap.dart +++ b/lib/src/set_multimap/built_set_multimap.dart @@ -16,19 +16,13 @@ part of '../set_multimap.dart'; abstract class BuiltSetMultimap { final Map> _map; - // Precomputed. - final BuiltSet _emptySet = BuiltSet(); - - // Cached. - int? _hashCode; - Iterable? _keys; - Iterable? _values; - /// Instantiates with elements from a [Map], [SetMultimap] or /// [BuiltSetMultimap]. factory BuiltSetMultimap([multimap = const {}]) { - if (multimap is _BuiltSetMultimap && - multimap.hasExactKeyAndValueTypes(K, V)) { + if ((multimap is _BuiltSetMultimap && + multimap.hasExactKeyAndValueTypes(K, V)) || + (multimap is _ConstBuiltSetMultimap && + multimap.hasExactKeyAndValueTypes(K, V))) { return multimap as BuiltSetMultimap; } else if (multimap is Map) { return _BuiltSetMultimap.copyAndCheck( @@ -42,6 +36,9 @@ abstract class BuiltSetMultimap { } } + const factory BuiltSetMultimap.fromMap([Map> map]) = + _ConstBuiltSetMultimap.withSafeSet; + /// Creates a [SetMultimapBuilder], applies updates to it, and builds. factory BuiltSetMultimap.build(Function(SetMultimapBuilder) updates) => (SetMultimapBuilder()..update(updates)).build(); @@ -72,11 +69,10 @@ abstract class BuiltSetMultimap { /// to be the same. @override int get hashCode { - _hashCode ??= hashObjects(_map.keys + return hashObjects(_map.keys .map((key) => hash2(key.hashCode, _map[key].hashCode)) .toList(growable: false) ..sort()); - return _hashCode!; } /// Deep equality. @@ -109,7 +105,7 @@ abstract class BuiltSetMultimap { /// As [SetMultimap], but results are [BuiltSet]s and not mutable. BuiltSet? operator [](Object? key) { var result = _map[key]; - return identical(result, null) ? _emptySet : result; + return identical(result, null) ? BuiltSet() : result; } /// As [SetMultimap.containsKey]. @@ -142,28 +138,55 @@ abstract class BuiltSetMultimap { /// As [SetMultimap.keys], but result is stable; it always returns the same /// instance. - Iterable get keys { - _keys ??= _map.keys; - return _keys!; - } + Iterable get keys => _map.keys; /// As [SetMultimap.length]. int get length => _map.length; /// As [SetMultimap.values], but result is stable; it always returns the /// same instance. - Iterable get values { - _values ??= _map.values.expand((x) => x); - return _values!; - } + Iterable get values => _map.values.expand((x) => x); // Internal. - BuiltSetMultimap._(this._map); + const BuiltSetMultimap._(this._map); } /// Default implementation of the public [BuiltSetMultimap] interface. class _BuiltSetMultimap extends BuiltSetMultimap { + // Precomputed. + final BuiltSet _emptySet = BuiltSet(); + + // Cached. + int? _hashCode; + + @override + int get hashCode { + _hashCode ??= super.hashCode; + return _hashCode!; + } + + Iterable? _keys; + + @override + Iterable get keys { + _keys ??= super.keys; + return _keys!; + } + + Iterable? _values; + @override + Iterable get values { + _values ??= super.values; + return _values!; + } + + @override + BuiltSet? operator [](Object? key) { + var result = _map[key]; + return identical(result, null) ? _emptySet : result; + } + _BuiltSetMultimap.withSafeMap(Map> map) : super._(map); _BuiltSetMultimap.copyAndCheck(Iterable keys, Function lookup) @@ -179,3 +202,10 @@ class _BuiltSetMultimap extends BuiltSetMultimap { bool hasExactKeyAndValueTypes(Type key, Type value) => K == key && V == value; } + +class _ConstBuiltSetMultimap extends BuiltSetMultimap { + const _ConstBuiltSetMultimap.withSafeSet([Map> src = const {}]) + : super._(src); + + bool hasExactKeyAndValueTypes(Type key, Type value) => K == key && V == value; +} diff --git a/lib/src/set_multimap/set_multimap_builder.dart b/lib/src/set_multimap/set_multimap_builder.dart index 2d7bf3e..4e42133 100644 --- a/lib/src/set_multimap/set_multimap_builder.dart +++ b/lib/src/set_multimap/set_multimap_builder.dart @@ -17,7 +17,7 @@ class SetMultimapBuilder { late Map> _builtMap; // Instance that _builtMap belongs to. If present, _builtMap must not be // mutated. - _BuiltSetMultimap? _builtMapOwner; + BuiltSetMultimap? _builtMapOwner; // SetBuilders for keys that are being changed. late Map> _builderMap; @@ -52,7 +52,7 @@ class SetMultimapBuilder { /// Replaces all elements with elements from a [Map], [SetMultimap] or /// [BuiltSetMultimap]. void replace(dynamic multimap) { - if (multimap is _BuiltSetMultimap) { + if (multimap is BuiltSetMultimap) { _setOwner(multimap); } else if (multimap is Map) { _setWithCopyAndCheck(multimap.keys, (k) => multimap[k]); @@ -162,7 +162,7 @@ class SetMultimapBuilder { SetMultimapBuilder._uninitialized(); - void _setOwner(_BuiltSetMultimap builtSetMultimap) { + void _setOwner(BuiltSetMultimap builtSetMultimap) { _builtMapOwner = builtSetMultimap; _builtMap = builtSetMultimap._map; _builderMap = >{};