Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 39 additions & 39 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,80 +13,80 @@ Benchmarks used to measure the performance of equality comparisons using `packag

```
EmptyEquatable
total runs: 2 064 037
total runs: 2 042 513
total time: 2.0000 s
average run: 0 μs
runs/second: Infinity
units: 100
units: 100
units/second: Infinity
time per unit: 0.0000 μs

PrimitiveEquatable
total runs: 729 555
total runs: 686 863
total time: 2.0000 s
average run: 2 μs
runs/second: 500 000
units: 100
units/second: 50 000 000
runs/second: 500 000
units: 100
units/second: 50 000 000
time per unit: 0.0200 μs

CollectionEquatable (static, small)
total runs: 51 944
total runs: 142 582
total time: 2.0000 s
average run: 38 μs
runs/second: 26 316
units: 100
units/second: 2 631 579
time per unit: 0.3800 μs
average run: 14 μs
runs/second: 71 429
units: 100
units/second: 7 142 857
time per unit: 0.1400 μs

CollectionEquatable (static, medium)
total runs: 44 572
total runs: 111 556
total time: 2.0000 s
average run: 44 μs
runs/second: 22 727
units: 100
units/second: 2 272 727
time per unit: 0.4400 μs
average run: 17 μs
runs/second: 58 824
units: 100
units/second: 5 882 353
time per unit: 0.1700 μs

CollectionEquatable (static, large)
total runs: 21 027
total time: 2.0001 s
average run: 95 μs
runs/second: 10 526
units: 100
units/second: 1 052 632
time per unit: 0.9500 μs
total runs: 35 227
total time: 2.0000 s
average run: 56 μs
runs/second: 17 857
units: 100
units/second: 1 785 714
time per unit: 0.5600 μs

CollectionEquatable (dynamic, small)
total runs: 400 934
total runs: 400 138
total time: 2.0000 s
average run: 4 μs
runs/second: 250 000
units: 100
units/second: 25 000 000
runs/second: 250 000
units: 100
units/second: 25 000 000
time per unit: 0.0400 μs

CollectionEquatable (dynamic, medium)
total runs: 400 408
total runs: 402 805
total time: 2.0000 s
average run: 4 μs
runs/second: 250 000
units: 100
units/second: 25 000 000
runs/second: 250 000
units: 100
units/second: 25 000 000
time per unit: 0.0400 μs

CollectionEquatable (dynamic, large)
total runs: 400 966
total runs: 407 055
total time: 2.0000 s
average run: 4 μs
runs/second: 250 000
units: 100
units/second: 25 000 000
runs/second: 250 000
units: 100
units/second: 25 000 000
time per unit: 0.0400 μs
```

_Last Updated: June 3, 2024 using `725b76c9ef072695f3ae4f036c4fa5e015528f13`_
_Last Updated: October 11, 2024 using `8d283b02f073d3a18e4f63aa528f7e0209826ac7`_

_MacBook Pro (M1 Pro, 16GB RAM)_

Dart SDK version: 3.5.0-218.0.dev (dev) (Mon Jun 3 13:02:57 2024 -0700) on "macos_arm64"
Dart SDK version: Dart SDK version: 3.5.3 (stable) (Wed Sep 11 16:22:47 2024 +0000) on "macos_arm64"
2 changes: 1 addition & 1 deletion benchmarks/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: equatable_benchmarks
publish_to: none

environment:
sdk: ">=2.12.0 <3.0.0"
sdk: ">=3.5.0 <4.0.0"

dependencies:
equatable: ^2.0.0
Expand Down
2 changes: 1 addition & 1 deletion lib/src/equatable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ abstract class Equatable {
return identical(this, other) ||
other is Equatable &&
runtimeType == other.runtimeType &&
equals(props, other.props);
iterableEquals(props, other.props);
}

@override
Expand Down
2 changes: 1 addition & 1 deletion lib/src/equatable_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ mixin EquatableMixin {
return identical(this, other) ||
other is EquatableMixin &&
runtimeType == other.runtimeType &&
equals(props, other.props);
iterableEquals(props, other.props);
}

@override
Expand Down
66 changes: 47 additions & 19 deletions lib/src/equatable_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,56 @@ int mapPropsToHashCode(Iterable<Object?>? props) {
return _finish(props == null ? 0 : props.fold(0, _combine));
}

const DeepCollectionEquality _equality = DeepCollectionEquality();
/// Determines whether two iterables are equal.
@pragma('vm:prefer-inline')
bool iterableEquals(Iterable<Object?> a, Iterable<Object?> b) {
assert(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on calling setEquals instead of asserting here?

if (a is Set && b is Set) return setEquals(a, b);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't expect anyone to use this function. I'm not sure it's even exported. So I'd prefer to leave it simple, without extra ifs

a is! Set && b is! Set,
"iterableEquals doesn't support Sets. Use setEquals instead.",
);
if (identical(a, b)) return true;
if (a.length != b.length) return false;
for (var i = 0; i < a.length; i++) {
if (!objectsEquals(a.elementAt(i), b.elementAt(i))) return false;
}
return true;
}

/// Determines whether [list1] and [list2] are equal.
bool equals(List<Object?>? list1, List<Object?>? list2) {
if (identical(list1, list2)) return true;
if (list1 == null || list2 == null) return false;
final length = list1.length;
if (length != list2.length) return false;
/// Determines whether two sets are equal.
bool setEquals(Set<Object?> a, Set<Object?> b) {
if (identical(a, b)) return true;
if (a.length != b.length) return false;
for (final element in a) {
if (!b.any((e) => objectsEquals(element, e))) return false;
}
return true;
}

for (var i = 0; i < length; i++) {
final unit1 = list1[i];
final unit2 = list2[i];
/// Determines whether two maps are equal.
bool mapEquals(Map<Object?, Object?> a, Map<Object?, Object?> b) {
if (identical(a, b)) return true;
if (a.length != b.length) return false;
for (final key in a.keys) {
if (!objectsEquals(a[key], b[key])) return false;
}
return true;
}

if (_isEquatable(unit1) && _isEquatable(unit2)) {
if (unit1 != unit2) return false;
} else if (unit1 is Iterable || unit1 is Map) {
if (!_equality.equals(unit1, unit2)) return false;
} else if (unit1?.runtimeType != unit2?.runtimeType) {
return false;
} else if (unit1 != unit2) {
return false;
}
/// Determines whether two objects are equal.
bool objectsEquals(Object? a, Object? b) {
if (identical(a, b)) return true;
if (_isEquatable(a) && _isEquatable(b)) {
return a == b;
} else if (a is Set && b is Set) {
return setEquals(a, b);
} else if (a is Iterable && b is Iterable) {
return iterableEquals(a, b);
} else if (a is Map && b is Map) {
return mapEquals(a, b);
} else if (a?.runtimeType != b?.runtimeType) {
return false;
} else if (a != b) {
return false;
}
return true;
}
Expand Down
Loading