Set — Unique Values & Operations
Set — Unique Values & Operations
A Set in Dart is a collection of unique values — no duplicates allowed. Unlike a List, a Set does not preserve insertion order (by default) and has no concept of index-based access. These properties make Sets extremely useful in specific scenarios.
1. What is a Set?
Think of a Set like a bag of unique items:
- You can add items to the bag
- The bag automatically ignores items you already have
- You cannot ask for the "3rd item" — there is no index
- Checking if an item exists is very fast (O(1) — constant time)
dartvoid main() { Set<int> numbers = {1, 2, 3, 4, 5}; print(numbers); // {1, 2, 3, 4, 5} // Duplicates are automatically removed Set<String> fruits = {'apple', 'mango', 'apple', 'banana', 'mango'}; print(fruits); // {apple, mango, banana} print(fruits.length); // 3 — only unique values }
2. Creating Sets
Literal {} Syntax
dartvoid main() { // Typed Set Set<int> primes = {2, 3, 5, 7, 11}; Set<String> colors = {'red', 'green', 'blue'}; // Type inferred var days = {'Mon', 'Tue', 'Wed', 'Thu', 'Fri'}; // Set<String> // Empty Set — IMPORTANT: {} alone creates a Map, not a Set! Set<int> emptySet = {}; // Correct var alsoEmpty = <int>{}; // Also correct // var wrong = {}; // This creates Map<dynamic, dynamic>! print(emptySet.runtimeType); // _Set<int> }
Set() Constructor
dartvoid main() { var set1 = Set<int>(); set1.add(1); set1.add(2); print(set1); // {1, 2} }
LinkedHashSet — Ordered Set
By default, Set in Dart is a LinkedHashSet which does preserve insertion order:
dartimport 'dart:collection'; void main() { // LinkedHashSet preserves insertion order var ordered = LinkedHashSet<String>(); ordered.add('banana'); ordered.add('apple'); ordered.add('cherry'); print(ordered); // {banana, apple, cherry} // Regular Set literal also preserves order in practice (LinkedHashSet) var tags = {'flutter', 'dart', 'mobile', 'crossplatform'}; print(tags); // {flutter, dart, mobile, crossplatform} }
3. Adding Elements
dartvoid main() { Set<String> languages = {'Dart', 'Python'}; // add() — single element languages.add('JavaScript'); languages.add('Dart'); // Duplicate — silently ignored print(languages); // {Dart, Python, JavaScript} print(languages.length); // 3 // addAll() — multiple elements from an Iterable languages.addAll(['Kotlin', 'Swift', 'Python']); // Python is duplicate print(languages); // {Dart, Python, JavaScript, Kotlin, Swift} }
4. Removing Elements
dartvoid main() { Set<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // remove() — removes specific value (true if removed, false if not found) bool removed = numbers.remove(5); print(removed); // true print(numbers); // {1, 2, 3, 4, 6, 7, 8, 9, 10} // Remove non-existent element — no error bool notFound = numbers.remove(99); print(notFound); // false // removeWhere() — remove all matching a condition numbers.removeWhere((n) => n % 2 == 0); // remove all even print(numbers); // {1, 3, 7, 9} // clear() — remove all numbers.clear(); print(numbers); // {} }
5. Checking Membership — contains()
This is where Set really shines. Checking if a value exists is O(1) — instant, regardless of the Set's size.
dartvoid main() { Set<String> visitedPages = {'/home', '/about', '/products', '/contact'}; print(visitedPages.contains('/about')); // true print(visitedPages.contains('/login')); // false // contains() also works on any Iterable, but Set is fastest List<String> pageList = ['/home', '/about', '/products', '/contact']; print(pageList.contains('/about')); // true (but O(n) — slower for large lists) }
6. Set Operations — Union, Intersection, Difference
This is the unique superpower of Sets — mathematical set operations:
union() — All elements from both sets
dartvoid main() { Set<String> set1 = {'apple', 'mango', 'banana'}; Set<String> set2 = {'banana', 'grape', 'orange'}; Set<String> unionSet = set1.union(set2); print(unionSet); // {apple, mango, banana, grape, orange} // All unique elements from both sets }
intersection() — Only common elements
dartvoid main() { Set<int> evens = {2, 4, 6, 8, 10}; Set<int> multOf3 = {3, 6, 9, 12}; Set<int> common = evens.intersection(multOf3); print(common); // {6} // Only elements present in BOTH sets }
difference() — Elements in first set but not in second
dartvoid main() { Set<String> allStudents = {'Amit', 'Priya', 'Rahul', 'Zara', 'Dev'}; Set<String> presentToday = {'Priya', 'Rahul', 'Dev'}; Set<String> absentStudents = allStudents.difference(presentToday); print(absentStudents); // {Amit, Zara} // Elements in allStudents but NOT in presentToday }
7. Converting Between List and Set
dartvoid main() { // List to Set — removes duplicates List<int> listWithDuplicates = [1, 2, 2, 3, 3, 3, 4, 5, 5]; Set<int> uniqueSet = listWithDuplicates.toSet(); print(uniqueSet); // {1, 2, 3, 4, 5} // Set back to List List<int> uniqueList = uniqueSet.toList(); print(uniqueList); // [1, 2, 3, 4, 5] // One-liner deduplication trick List<String> tags = ['dart', 'flutter', 'dart', 'mobile', 'flutter']; List<String> uniqueTags = tags.toSet().toList(); print(uniqueTags); // [dart, flutter, mobile] }
8. When to Use Set vs List
| Feature | List | Set |
|---|---|---|
| Preserves order | ✅ Yes | ✅ Yes (LinkedHashSet) |
| Allows duplicates | ✅ Yes | ❌ No |
Index access [i] | ✅ Yes | ❌ No |
contains() speed | ⚠️ O(n) — linear | ✅ O(1) — instant |
| Best for | Ordered sequences, ordered data | Unique collections, fast lookups |
Use Set when:
- You need to ensure all values are unique
- You frequently check membership (
contains()) - You need set operations (union, intersection, difference)
Use List when:
- Order and position matter
- You need index-based access
- Duplicates are allowed or expected
9. Real Examples
Unique Tags System
dartvoid main() { Set<String> articleTags = {}; void addTag(String tag) { bool added = articleTags.add(tag.toLowerCase().trim()); if (added) { print('Tag "$tag" added.'); } else { print('Tag "$tag" already exists.'); } } addTag('Flutter'); // Tag "flutter" added. addTag('Dart'); // Tag "dart" added. addTag('Mobile'); // Tag "mobile" added. addTag('flutter'); // Tag "flutter" already exists. addTag('DART'); // Tag "dart" already exists. print('Tags: $articleTags'); // Tags: {flutter, dart, mobile} }
Visited Pages Tracker
dartclass PageTracker { final Set<String> _visited = {}; final List<String> _history = []; // ordered history with duplicates void visit(String page) { bool isNew = _visited.add(page); _history.add(page); if (isNew) { print('First visit to: $page'); } else { print('Revisiting: $page'); } } Set<String> get uniquePages => Set.unmodifiable(_visited); List<String> get fullHistory => List.unmodifiable(_history); int get uniquePageCount => _visited.length; int get totalVisits => _history.length; } void main() { var tracker = PageTracker(); tracker.visit('/home'); tracker.visit('/products'); tracker.visit('/about'); tracker.visit('/home'); // Revisit tracker.visit('/products'); // Revisit tracker.visit('/contact'); print('\nUnique pages visited: ${tracker.uniquePageCount}'); print('Total visits: ${tracker.totalVisits}'); print('Pages: ${tracker.uniquePages}'); }
Output:
First visit to: /home
First visit to: /products
First visit to: /about
Revisiting: /home
Revisiting: /products
First visit to: /contact
Unique pages visited: 4
Total visits: 6
Pages: {/home, /products, /about, /contact}
Summary
| Operation | Method | Notes |
|---|---|---|
| Create | {}, Set<T>(), <T>{} | Use <T>{} for empty Sets |
| Add one | add(value) | Returns bool — true if added |
| Add many | addAll(iterable) | Silently skips duplicates |
| Remove | remove(value) | Returns bool |
| Remove conditional | removeWhere(test) | Removes all matching |
| Check existence | contains(value) | O(1) — very fast |
| Set math | union(), intersection(), difference() | Returns new Set |
| Convert | toList(), toSet() | Use for deduplication |
Sets are an underused but extremely efficient data structure. Whenever you find yourself checking membership frequently or needing uniqueness guarantees — reach for a Set.