Dart 16 🎯 Core Collections: Set for Uniqueness and Set Operations
A Set is an unordered collection of unique elements. Where a List preserves insertion order and allows duplicates, a Set guarantees that each value appears at most once and provides fast membership tests. The two collections answer different questions: a List is for sequences where order and repetition matter, and a Set is for membership and uniqueness. Dart provides Set as a first-class collection with literal syntax, the standard set algebra operations — union, intersection, difference — and the performance characteristics that make it the right tool for deduplication, tagging, and membership checks.
The default Set implementation is LinkedHashSet, which preserves insertion order while guaranteeing uniqueness. This is a deliberate design choice: most uses of a set in application code want the uniqueness guarantee, and preserving insertion order makes the iteration predictable. A HashSet trades ordering for a small performance gain, and a SplayTreeSet sorts the elements by their natural order or by a comparator. The three implementations share the same interface and differ in their ordering and performance characteristics.
This chapter covers set creation, the uniqueness guarantee, the three implementations, the set algebra operations, the patterns for deduplication, membership, and tags, and the differences between sets and lists.
Key point: A Set is an unordered collection of unique elements. Dart’s default Set is a LinkedHashSet, which preserves insertion order. HashSet is unordered and faster for membership; SplayTreeSet sorts its elements. Use set algebra — union, intersection, difference — to combine sets, and Set.from(list) to deduplicate a list.
Why Set exists alongside List
The uniqueness problem. A list can contain duplicates. When the requirement is that each element appears once — a set of tags, a set of permissions, a set of visited URLs — the list must be checked before every insertion, which is expensive. The Set enforces uniqueness at insertion time and provides fast membership.
The membership problem. Checking whether a list contains a value requires a linear scan. Checking whether a set contains a value is a hash lookup, which is constant time on average. For collections that are checked frequently, the set is the correct choice.
The algebra problem. Combining two collections of unique elements — the union, the intersection, the difference — is awkward with lists and natural with sets. The set algebra operations are the standard tools for permissions, tags, and filters.
The ordering problem. Some uses of a set need predictable iteration order, and some do not. The LinkedHashSet preserves insertion order, the HashSet does not, and the SplayTreeSet sorts. The three implementations let the developer choose the trade-off.
The equality problem. Two sets are equal if they contain the same elements, regardless of order. A set comparison is a membership comparison, not an order comparison. This is the correct semantics for collections of unique elements.
a. Creating sets
A set literal uses curly braces. The elements inside are unique.
var numbers = {1, 2, 3, 4, 5};
var names = {'Alice', 'Bob', 'Carol'};
var empty = <int>{};
The type is inferred from the elements. An explicit type can be given with the angle bracket syntax, which is required for an empty set to avoid ambiguity with a map literal.
var numbers = <int>{1, 2, 3};
var empty = <String>{};
A set can be created from a list, which removes the duplicates:
var list = [1, 2, 2, 3, 3, 3];
var set = Set<int>.from(list); // {1, 2, 3}
var alsoSet = {...list}; // {1, 2, 3}
var another = list.toSet(); // {1, 2, 3}
The spread operator in a set literal adds the elements of another iterable, and the duplicates are removed by the set itself.
var a = {1, 2, 3};
var b = {3, 4, 5};
var combined = {...a, ...b}; // {1, 2, 3, 4, 5}
A set can also be created from a range:
var range = Set<int>.from([for (var i = 1; i <= 5; i++) i]);
b. The uniqueness guarantee
A set contains each element at most once. Adding an element that already exists has no effect.
var tags = {'dart', 'flutter'};
tags.add('dart'); // no effect
print(tags); // {dart, flutter}
The add method returns true if the element was added and false if it was already present. The addAll method adds multiple elements and ignores the ones that are already present.
var added = tags.add('dart'); // false
var added2 = tags.add('web'); // true
The uniqueness is determined by the element’s == and hashCode. For the built-in types, this is value equality. For custom types, the class must override == and hashCode for the set to detect duplicates correctly.
class Point {
final int x;
final int y;
Point(this.x, this.y);
@override
bool operator ==(Object other) =>
other is Point && other.x == x && other.y == y;
@override
int get hashCode => Object.hash(x, y);
}
var points = {Point(1, 2), Point(1, 2)}; // {Point(1, 2)}
Without the overrides, the two Point instances are different objects and both are added.
c. The three implementations
Dart provides three Set implementations, each with different ordering and performance characteristics.
| Implementation | Ordering | Performance | Use case |
|---|---|---|---|
LinkedHashSet | Insertion order | Good | Default |
HashSet | Unordered | Fastest | Membership-heavy |
SplayTreeSet | Sorted | Slower | Sorted iteration |
LinkedHashSet is the default. It preserves the order in which elements were inserted, which makes iteration predictable. The cost is slightly more memory for the linked list that maintains the order.
var set = <String>{};
set.add('b');
set.add('a');
set.add('c');
print(set); // {b, a, c} — insertion order
HashSet does not guarantee any order. It is faster for insertion and membership because it does not maintain the linked list. Use it when the order does not matter and performance is the priority.
import 'dart:collection';
var set = HashSet<String>();
set.add('b');
set.add('a');
print(set); // order not guaranteed
SplayTreeSet keeps its elements sorted. It requires the elements to be comparable, either by their natural order (Comparable) or by a comparator passed to the constructor. The performance is slower than the hash-based sets, but the iteration is in sorted order.
import 'dart:collection';
var set = SplayTreeSet<int>((a, b) => b.compareTo(a)); // descending
set.addAll([3, 1, 4, 1, 5]);
print(set); // {5, 4, 3, 1}
The SplayTreeSet is the right choice when the set must be iterated in a specific order, such as a leaderboard or a sorted list of tags.
d. Set algebra operations
The set operations combine two sets and produce a new set.
| Operation | Method | Result |
|---|---|---|
| Union | a.union(b) | All elements in either set |
| Intersection | a.intersection(b) | Elements in both sets |
| Difference | a.difference(b) | Elements in a but not b |
var a = {1, 2, 3, 4};
var b = {3, 4, 5, 6};
print(a.union(b)); // {1, 2, 3, 4, 5, 6}
print(a.intersection(b)); // {3, 4}
print(a.difference(b)); // {1, 2}
print(b.difference(a)); // {5, 6}
The union is the combination. The intersection is the overlap. The difference is what remains when the other set is removed.
These operations are not symmetric. a.difference(b) and b.difference(a) produce different results unless the sets are equal. The union and intersection are symmetric, but the difference is directional.
The operations can be chained:
var result = a.union(b).difference({2, 5});
// {1, 3, 4, 6}
The chain computes the union of a and b, then removes the elements 2 and 5.
The subset and superset relations are tested with methods:
var small = {1, 2};
var large = {1, 2, 3, 4};
print(small.isSubsetOf(large)); // true
print(large.isSupersetOf(small)); // true
A set is a subset of another if every element of the first is in the second. It is a superset if every element of the second is in the first.
e. Deduplication and membership
The most common uses of a set are deduplication and membership checks.
Deduplication. Converting a list to a set removes the duplicates, and converting the set back to a list gives a list with unique elements.
var list = [1, 2, 2, 3, 3, 3];
var unique = list.toSet().toList(); // [1, 2, 3]
The order is preserved if the set is a LinkedHashSet, which is the default.
Membership. The contains method checks whether an element is in the set in constant time on average.
var tags = {'dart', 'flutter', 'web'};
print(tags.contains('dart')); // true
print(tags.contains('python')); // false
For a collection that is checked frequently, the set is the right choice. The list’s contains is a linear scan; the set’s is a hash lookup.
Counting unique elements. The length of a set is the number of unique elements.
var words = ['apple', 'banana', 'apple', 'cherry'];
var uniqueCount = words.toSet().length; // 3
Finding common and unique elements. The algebra operations find the common elements (intersection) and the elements that appear in one set but not the other (difference).
var userPermissions = {'read', 'write'};
var requiredPermissions = {'read', 'write', 'delete'};
var missing = requiredPermissions.difference(userPermissions);
print(missing); // {delete}
f. Sets and lists compared
The two collections differ in ordering, uniqueness, and performance.
| Aspect | List | Set |
|---|---|---|
| Ordering | Preserved | Preserved (LinkedHashSet) |
| Duplicates | Allowed | Not allowed |
| Membership | Linear | Constant (average) |
| Index access | Yes | No |
| Iteration | By index or element | By element only |
| Algebra | No | Union, intersection, difference |
| Literal | [1, 2, 3] | {1, 2, 3} |
The list is the right choice when order and index access matter. The set is the right choice when uniqueness and membership matter. The two are convertible: list.toSet() deduplicates, and set.toList() produces a list.
var list = [3, 1, 2, 1, 3];
var set = list.toSet(); // {3, 1, 2}
var back = set.toList(); // [3, 1, 2]
The conversion preserves the insertion order when the set is a LinkedHashSet, which is the default.
Complete Example Session
// ============================================
// PART 1: CREATE A SET
// ============================================
void main() {
var numbers = {1, 2, 3, 4, 5};
var names = <String>{'Alice', 'Bob'};
var empty = <int>{};
print(numbers); // {1, 2, 3, 4, 5}
print(names); // {Alice, Bob}
print(empty); // {}
}
// ============================================
// PART 2: UNIQUENESS
// ============================================
void main() {
var tags = {'dart', 'flutter'};
tags.add('dart'); // no effect
print(tags); // {dart, flutter}
var added = tags.add('web');
print(added); // true
}
// ============================================
// PART 3: DEDUPLICATE A LIST
// ============================================
void main() {
var list = [1, 2, 2, 3, 3, 3];
var unique = list.toSet().toList();
print(unique); // [1, 2, 3]
}
// ============================================
// PART 4: INSERTION ORDER
// ============================================
void main() {
var set = <String>{};
set.add('b');
set.add('a');
set.add('c');
print(set); // {b, a, c}
}
// ============================================
// PART 5: HASHSET (UNORDERED)
// ============================================
import 'dart:collection';
void main() {
var set = HashSet<String>();
set.addAll(['b', 'a', 'c']);
print(set); // order not guaranteed
}
// ============================================
// PART 6: SPLAYTREESET (SORTED)
// ============================================
import 'dart:collection';
void main() {
var set = SplayTreeSet<int>();
set.addAll([3, 1, 4, 1, 5]);
print(set); // {1, 3, 4, 5}
}
// ============================================
// PART 7: UNION, INTERSECTION, DIFFERENCE
// ============================================
void main() {
var a = {1, 2, 3, 4};
var b = {3, 4, 5, 6};
print(a.union(b)); // {1, 2, 3, 4, 5, 6}
print(a.intersection(b)); // {3, 4}
print(a.difference(b)); // {1, 2}
}
// ============================================
// PART 8: SUBSET AND SUPERSET
// ============================================
void main() {
var small = {1, 2};
var large = {1, 2, 3, 4};
print(small.isSubsetOf(large)); // true
print(large.isSupersetOf(small)); // true
}
// ============================================
// PART 9: MEMBERSHIP AND PERMISSIONS
// ============================================
void main() {
var userPermissions = {'read', 'write'};
var requiredPermissions = {'read', 'write', 'delete'};
var missing = requiredPermissions.difference(userPermissions);
print(missing); // {delete}
}
// ============================================
// PART 10: CUSTOM EQUALITY
// ============================================
class Point {
final int x;
final int y;
Point(this.x, this.y);
@override
bool operator ==(Object other) =>
other is Point && other.x == x && other.y == y;
@override
int get hashCode => Object.hash(x, y);
}
void main() {
var points = {Point(1, 2), Point(1, 2), Point(3, 4)};
print(points.length); // 2
}
These ten parts cover creating a set, the uniqueness guarantee, deduplication, insertion order, HashSet, SplayTreeSet, the algebra operations, subset and superset, membership and permissions, and custom equality.
Quick Reference
Set Creation
| Form | Example |
|---|---|
| Literal | {1, 2, 3} |
| Typed empty | <int>{} |
| From list | Set<int>.from(list) |
| Spread | {...a, ...b} |
| From list method | list.toSet() |
The Three Implementations
| Implementation | Ordering | Import |
|---|---|---|
LinkedHashSet | Insertion | Default |
HashSet | Unordered | dart:collection |
SplayTreeSet | Sorted | dart:collection |
Set Operations
| Operation | Method |
|---|---|
| Union | a.union(b) |
| Intersection | a.intersection(b) |
| Difference | a.difference(b) |
| Subset | a.isSubsetOf(b) |
| Superset | a.isSupersetOf(b) |
Common Methods
| Method | Purpose |
|---|---|
add(element) | Add one, returns bool |
addAll(iterable) | Add many |
remove(element) | Remove one |
contains(element) | Membership check |
containsAll(iterable) | Check all |
clear() | Remove all |
toList() | Convert to list |
Set vs List
| Aspect | List | Set |
|---|---|---|
| Duplicates | Allowed | Not allowed |
| Membership | Linear | Constant |
| Index access | Yes | No |
| Algebra | No | Yes |
| Literal | [...] | {...} |
Conversion
| From | To | Method |
|---|---|---|
| List → Set | Deduplicate | list.toSet() |
| Set → List | Ordered list | set.toList() |
| Iterable → Set | Unique | Set.from(iterable) |
Best Practices
✅ Do This:
// Use a set for unique elements
var tags = {'dart', 'flutter'};
// Deduplicate with toSet()
var unique = list.toSet().toList();
// Use set algebra
var missing = required.difference(actual);
// Use SplayTreeSet for sorted iteration
var sorted = SplayTreeSet<int>()..addAll([3, 1, 2]);
// Override == and hashCode for custom types
class Point {
@override
bool operator ==(Object other) => ...;
@override
int get hashCode => ...;
}
❌ Don’t Do This:
// Use a list for membership checks
if (list.contains(x)) { } // ❌ use a set
// Rely on HashSet ordering
var set = HashSet<int>()..addAll([1, 2, 3]); // ❌ order not guaranteed
// Forget to override == and hashCode
var points = {Point(1, 2), Point(1, 2)}; // ❌ both added
// Use a set when index access is needed
var set = {1, 2, 3};
set[0]; // ❌ sets are not indexable
// Convert back and forth unnecessarily
list.toSet().toList().toSet(); // ❌ redundant
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Duplicates not removed | == and hashCode not overridden | Override both |
| Order unexpected | Used HashSet | Use the default LinkedHashSet |
| Index access fails | Sets are not indexable | Convert to a list |
| Membership slow | Used a list | Use a set |
| Sorted iteration not working | SplayTreeSet not used | Import and use it |
Empty {} is a map | Curly braces are map literals by default | Use <int>{} for an empty set |
Real-World Examples
1. Unique Tags
var tags = {'dart', 'flutter', 'web'};
2. Deduplicate a List
var unique = [1, 2, 2, 3].toSet().toList();
3. Membership Check
if (permissions.contains('admin')) { }
4. Union of Two Sets
var all = setA.union(setB);
5. Intersection
var common = setA.intersection(setB);
6. Difference
var missing = required.difference(actual);
7. Subset Check
if (small.isSubsetOf(large)) { }
8. Sorted Set
var scores = SplayTreeSet<int>()..addAll([85, 92, 78]);
9. Unique Words
var uniqueWords = text.split(' ').toSet();
10. Custom Equality
var points = {Point(1, 2), Point(1, 2)}; // one element
Visual
Set Uniqueness
┌──────────────────────────────────────────────────────────────┐
│ List: [1, 2, 2, 3, 3, 3] │
│ │ │
│ ▼ toSet() │
│ Set: {1, 2, 3} │
│ │ │
│ ▼ toList() │
│ List: [1, 2, 3] │
└──────────────────────────────────────────────────────────────┘
Set Algebra
┌──────────────────────────────────────────────────────────────┐
│ a = {1, 2, 3, 4} │
│ b = {3, 4, 5, 6} │
│ │
│ UNION: {1, 2, 3, 4, 5, 6} │
│ INTERSECTION: {3, 4} │
│ DIFFERENCE: a.difference(b) = {1, 2} │
│ b.difference(a) = {5, 6} │
└──────────────────────────────────────────────────────────────┘
The Three Implementations
┌──────────────────────────────────────────────────────────────┐
│ LinkedHashSet (default): │
│ └── Insertion order preserved │
│ {b, a, c} stays {b, a, c} │
│ │
│ HashSet: │
│ └── No guaranteed order │
│ Fastest for membership │
│ │
│ SplayTreeSet: │
│ └── Sorted by natural order or comparator │
│ {3, 1, 4, 1, 5} becomes {1, 3, 4, 5} │
└──────────────────────────────────────────────────────────────┘
Custom Equality
┌──────────────────────────────────────────────────────────────┐
│ WITHOUT == and hashCode: │
│ var points = {Point(1, 2), Point(1, 2)}; │
│ └── Two different objects → {Point(1, 2), Point(1, 2)} │
│ │
│ WITH == and hashCode: │
│ var points = {Point(1, 2), Point(1, 2)}; │
│ └── Equal by value → {Point(1, 2)} │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Set | Unordered collection of unique elements |
| Default implementation | LinkedHashSet |
| Ordering | Insertion order (default) |
| Uniqueness | Enforced by == and hashCode |
| Membership | Constant time on average |
| Algebra | union, intersection, difference |
| Subset test | isSubsetOf, isSupersetOf |
| Deduplication | list.toSet() |
| Sorted set | SplayTreeSet |
| Unordered set | HashSet |
| Empty set literal | <int>{} |
Key takeaways:
- A set is an unordered collection of unique elements. The uniqueness is enforced at insertion time, so adding a duplicate has no effect. The
addmethod returns a boolean indicating whether the element was added. - The default
Setis aLinkedHashSet. It preserves insertion order, which makes iteration predictable. TheHashSetis unordered and faster for membership; theSplayTreeSetkeeps its elements sorted. - Uniqueness is determined by
==andhashCode. For the built-in types, this is value equality. For custom types, the class must override both for the set to detect duplicates. - The set algebra operations combine two sets.
unionis the combination,intersectionis the overlap, anddifferenceis what remains when the other set is removed.differenceis directional; the others are symmetric. - Deduplication is
list.toSet(). The result is a set with the unique elements, andtoList()converts it back to a list with the insertion order preserved. - Membership is fast. The
containsmethod uses a hash lookup, which is constant time on average, versus the linear scan of a list. - The empty set literal requires a type.
{}is an empty map, not an empty set. Use<int>{}or<String>{}to create an empty set.
Remember: The Set is the collection for uniqueness and membership. It guarantees that each element appears once, provides constant-time membership checks, and supports the set algebra operations that combine collections of unique elements. The default LinkedHashSet preserves insertion order; the HashSet trades ordering for speed; the SplayTreeSet sorts. The choice among them is a trade-off between ordering and performance, and the default is right for most application code. Use a set when uniqueness matters, a list when order and index access matter, and convert between them when the requirements change.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!