Skip to main content

Collection Equality

In Dart, collections compare by identity (reference equality) by default, not by content (structural equality). This guide explains why [1, 2] != [1, 2] and how to perform deep structural comparisons accurately.


1. Default Equality Behavior (Reference Equality)

By default, Dart collections use the identical() check when evaluated with ==:

final listA = [1, 2, 3];
final listB = [1, 2, 3];

print(listA == listB); // FALSE! Different references in memory.

final listC = listA;
print(listA == listC); // TRUE! Same reference.

The same applies to Set and Map:

print({'a': 1} == {'a': 1}); // FALSE
print({1, 2, 3} == {1, 2, 3}); // FALSE

:::info Exception: const Collections Compile-time constant (const) collections are canonicalized. Identical const collection literals evaluate to the exact same instance:

print(const [1, 2] == const [1, 2]); // TRUE! (Same constant instance)

:::


2. Structural Equality with package:collection

To compare collections by their contents, use the package:collection package.

# pubspec.yaml
dependencies:
collection: ^1.18.0

Shallow Equality Helpers

import 'package:collection/collection.dart';

// List equality
final listEq = ListEquality();
print(listEq.equals([1, 2, 3], [1, 2, 3])); // TRUE

// Set equality (ignores element order)
final setEq = SetEquality();
print(setEq.equals({3, 2, 1}, {1, 2, 3})); // TRUE

// Map equality
final mapEq = MapEquality();
print(mapEq.equals({'a': 1}, {'a': 1})); // TRUE

3. Deep Structural Equality (DeepCollectionEquality)

When collections contain nested lists, maps, or sets, shallow equality is insufficient. Use DeepCollectionEquality:

import 'package:collection/collection.dart';

final deepEq = DeepCollectionEquality();

final nestedA = {
'user': 'Alice',
'tags': ['flutter', 'dart'],
'settings': {'dark': true}
};

final nestedB = {
'user': 'Alice',
'tags': ['flutter', 'dart'],
'settings': {'dark': true}
};

print(deepEq.equals(nestedA, nestedB)); // TRUE!

4. Custom Class Integration (Overriding == & hashCode)

When using custom classes inside collections or using collections inside data objects, override == and hashCode using ListEquality or DeepCollectionEquality:

import 'package:collection/collection.dart';

class UserGroup {
final String id;
final List<String> memberIds;

UserGroup(this.id, this.memberIds);

static const _listEquality = ListEquality<String>();


bool operator ==(Object other) =>
identical(this, other) ||
other is UserGroup &&
runtimeType == other.runtimeType &&
id == other.id &&
_listEquality.equals(memberIds, other.memberIds);


int get hashCode => id.hashCode ^ _listEquality.hash(memberIds);
}

void main() {
final group1 = UserGroup('admin', ['u1', 'u2']);
final group2 = UserGroup('admin', ['u1', 'u2']);

print(group1 == group2); // TRUE
}

Summary of Equality Types

Comparison MethodDescriptionExample Use Case
== (Default)Reference equality (identical)Checking if two references point to the same list
const checkCanonicalized instance matchComparing constant configurations
ListEquality()Shallow equality for listsComparing simple List<int>
SetEquality()Unordered element equalityComparing Set<String>
MapEquality()Key-value pair equalityComparing simple JSON maps
DeepCollectionEquality()Recursive deep equalityComparing complex nested API responses