Dart SetMixin
last modified April 4, 2025
In Dart, SetMixin is an abstract class that provides base implementation for Set collections. It helps create custom Set implementations with unique elements.
SetMixin implements core Set operations while allowing customization. It ensures all elements are unique based on their equality implementation.
Basic SetMixin Implementation
Here's how to create a basic Set implementation using SetMixin.
import 'dart:collection'; class MySet<E> extends SetBase<E> with SetMixin<E> { final _set = <E>{}; @override Iterator<E> get iterator => _set.iterator; @override int get length => _set.length; @override bool contains(Object? element) => _set.contains(element); @override E? lookup(Object? element) => _set.lookup(element); @override bool add(E element) => _set.add(element); @override bool remove(Object? element) => _set.remove(element); @override void clear() => _set.clear(); } void main() { var mySet = MySet<int>(); mySet.addAll([1, 2, 3, 3, 4]); print(mySet); // {1, 2, 3, 4} }
We create MySet by extending SetBase and mixing in SetMixin. The implementation delegates to an internal Set. Duplicates are automatically handled.
$ dart main.dart {1, 2, 3, 4}
Custom Equality Set
We can create a Set with custom equality by overriding contains and lookup.
import 'dart:collection'; class CaseInsensitiveSet extends SetBase<String> with SetMixin<String> { final _set = <String>{}; @override Iterator<String> get iterator => _set.iterator; @override int get length => _set.length; @override bool contains(Object? element) { if (element is! String) return false; return _set.any((e) => e.toLowerCase() == element.toLowerCase()); } @override String? lookup(Object? element) { if (element is! String) return null; return _set.firstWhere( (e) => e.toLowerCase() == element.toLowerCase(), orElse: () => null, ); } @override bool add(String element) { if (contains(element)) return false; return _set.add(element); } @override bool remove(Object? element) { if (element is! String) return false; final existing = lookup(element); return existing != null ? _set.remove(existing) : false; } @override void clear() => _set.clear(); } void main() { var ciSet = CaseInsensitiveSet(); ciSet.add('Apple'); ciSet.add('apple'); // Won't add print(ciSet); // {Apple} }
This Set treats strings case-insensitively. The contains and lookup methods are overridden to implement custom equality logic.
$ dart main.dart {Apple}
Set Operations with SetMixin
SetMixin provides implementations for common set operations like union and intersection.
import 'dart:collection'; class MySet<E> extends SetBase<E> with SetMixin<E> { final _set = <E>{}; @override Iterator<E> get iterator => _set.iterator; @override int get length => _set.length; @override bool contains(Object? element) => _set.contains(element); @override E? lookup(Object? element) => _set.lookup(element); @override bool add(E element) => _set.add(element); @override bool remove(Object? element) => _set.remove(element); @override void clear() => _set.clear(); } void main() { var setA = MySet<int>()..addAll([1, 2, 3]); var setB = MySet<int>()..addAll([3, 4, 5]); print('Union: ${setA.union(setB)}'); print('Intersection: ${setA.intersection(setB)}'); print('Difference: ${setA.difference(setB)}'); }
We demonstrate union, intersection, and difference operations. These methods are provided by SetMixin and work with any Set implementation.
$ dart main.dart Union: {1, 2, 3, 4, 5} Intersection: {3} Difference: {1, 2}
Filtering Elements
SetMixin provides where method to filter elements based on a condition.
import 'dart:collection'; class MySet<E> extends SetBase<E> with SetMixin<E> { final _set = <E>{}; @override Iterator<E> get iterator => _set.iterator; @override int get length => _set.length; @override bool contains(Object? element) => _set.contains(element); @override E? lookup(Object? element) => _set.lookup(element); @override bool add(E element) => _set.add(element); @override bool remove(Object? element) => _set.remove(element); @override void clear() => _set.clear(); } void main() { var numbers = MySet<int>()..addAll([1, 2, 3, 4, 5, 6]); var evens = numbers.where((n) => n % 2 == 0).toSet(); print('Original: $numbers'); print('Evens: $evens'); }
The where method returns an iterable of elements that satisfy the predicate. We convert it back to a Set using toSet().
$ dart main.dart Original: {1, 2, 3, 4, 5, 6} Evens: {2, 4, 6}
SetMixin with Custom Objects
When storing custom objects, proper hashCode and == implementations are crucial.
import 'dart:collection'; class Person { final String name; final int age; Person(this.name, this.age); @override bool operator ==(Object other) => identical(this, other) || other is Person && name == other.name && age == other.age; @override int get hashCode => name.hashCode ^ age.hashCode; @override String toString() => 'Person($name, $age)'; } class PersonSet extends SetBase<Person> with SetMixin<Person> { final _set = <Person>{}; @override Iterator<Person> get iterator => _set.iterator; @override int get length => _set.length; @override bool contains(Object? element) => _set.contains(element); @override Person? lookup(Object? element) => _set.lookup(element); @override bool add(Person element) => _set.add(element); @override bool remove(Object? element) => _set.remove(element); @override void clear() => _set.clear(); } void main() { var people = PersonSet(); people.add(Person('Alice', 30)); people.add(Person('Bob', 25)); people.add(Person('Alice', 30)); // Duplicate print(people); // {Person(Alice, 30), Person(Bob, 25)} }
The Person class implements proper equality checks. The Set correctly identifies duplicates based on the custom == implementation.
$ dart main.dart {Person(Alice, 30), Person(Bob, 25)}
Best Practices
- Element Immutability: Use immutable elements for Set stability.
- Proper Hashing: Always override hashCode when overriding ==.
- Performance: Consider element types for optimal performance.
- Null Safety: Use non-nullable types when appropriate.
Source
This tutorial covered Dart's SetMixin with practical examples demonstrating its key features and usage patterns for creating custom Set implementations.
Author
List all Dart tutorials.