Sets in Dart
By Syncfusion®, Inc. · Last updated
How to keep unique values with sets and set operations, and how to add and remove at both ends with queues from dart:collection.
What Is a Set?
Set = a collection where every value is unique. The same value can never appear twice.
For example:
- the different subjects a student takes
- the roll numbers of students who came to the library this week
- the different words used in an essay
Adding a value that is already there does nothing.
Simple lesson: a set is like a list that refuses duplicates.
A Real-World Example
The library keeps a visitor book for the week.
- Asha visits on Monday, Wednesday and Friday.
- The librarian wants to know who visited, not how many times.
- So each name is written once, even if the student came three times.
That list of names is a set.
Every Run box on this page runs on the course's Dart runner in your browser.
Watch the curly braces { } in the output. They mean a set, not a list.
What You Will Learn
In this lesson, you will learn:
- What sets are, and how to create them.
- How to add, remove and search in a set.
- Set operations: union, intersection and difference.
- How to convert collections, and make sets that cannot change.
- How a Queue adds and removes items at both ends.
Set Fundamentals
Creating Sets with Set Literals
Set literal = values inside { }, separated by commas.
A set keeps one copy of each value
void main() {
var subjects = {'Maths', 'Science', 'English', 'Maths'};
print(subjects);
print(subjects.length);
}
'Maths' was written twice, but the set keeps it once. The length is 3.
Typed Sets and Empty Sets
- Typed set:
Set<String> houses = {'Red', 'Blue'}; - Empty set:
var visitors = <String>{};
{} on its own makes an empty Map, not a set (see Maps).
For an empty set, write the type: <String>{}, or Set<String> visitors = {};.
Unique Values
- A set checks every new value with
==. - If an equal value is already inside, the new one is not added.
- The order you added items in is kept.
Simple lesson: write a set with { }, and Dart keeps just one copy of each value.
Set Properties
| Property | Gives |
|---|---|
length | how many items |
isEmpty / isNotEmpty | whether there are no items, or some |
first / last | the first or last item added |
Set properties
void main() {
var houses = {'Red', 'Blue', 'Green'};
print(houses.length);
print(houses.isEmpty);
print(houses.isNotEmpty);
print(houses.first);
print(houses.last);
}
A set has no index. You cannot write houses[0]. Use first, last or a loop instead.
Simple lesson: a set has a length, a first and a last item, but no index.
Adding and Removing Elements
Adding Elements
add(x)= addsx, and givestrueif it was new,falseif it was already there.addAll([...])= adds many values at once.
add() gives true or false
Removing Elements
remove(x)= removesx, and givestrueif it was there.removeWhere(test)= removes every item that passes the test.clear()= removes everything.
Removing from a set
void main() {
var clubs = {'Chess', 'Music', 'Art', 'Coding', 'Dance'};
print(clubs.remove('Art'));
print(clubs.remove('Cricket'));
clubs.removeWhere((c) => c.startsWith('D'));
print(clubs);
clubs.clear();
print(clubs);
}
Simple lesson: add and remove tell you whether they changed the set, with true or false.
Searching Elements
contains(x)=trueifxis in the set.lookup(x)= gives back the item in the set that equalsx, ornull.
Is this student in the chess club?
void main() {
var chess = {'Asha', 'Ravi', 'Priya'};
print(chess.contains('Ravi'));
print(chess.contains('Kiran'));
print(chess.lookup('Priya'));
print(chess.lookup('Kiran'));
}
contains on a set is very fast, even with thousands of items. A list has to check item by item.
Simple lesson: use contains to check membership. It is the main reason to use a set.
Set Operations
Pick an operation and compare the two clubs:
union(other)= everything in either set.intersection(other)= only what is in both sets.difference(other)= what is in this set but not in the other.
Comparing two clubs
void main() {
var chess = {'Asha', 'Ravi', 'Priya', 'Kiran'};
var music = {'Priya', 'Kiran', 'Meena', 'Imran'};
print(chess.union(music));
print(chess.intersection(music));
print(chess.difference(music));
print(music.difference(chess));
}
chess.difference(music) and music.difference(chess) are different. Order matters.
These come from maths class: union (∪), intersection (∩) and difference (−).
Simple lesson: union joins, intersection keeps the shared part, and difference takes away.
Iterating Sets
Sets work with the same loops as lists (see Loops):
Three ways to visit every item
void main() {
var houses = {'Red', 'Blue', 'Green'};
for (var h in houses) {
print('$h House');
}
houses.forEach((h) => print(h.toUpperCase()));
var list = houses.toList();
for (var i = 0; i < list.length; i++) {
print('$i: ${list[i]}');
}
}
A set has no index, so a counted for loop needs toList() first.
Functional Operations
where, map, any and every work just like on lists (see Working with Iterables):
where, map, any and every on a set
void main() {
var marks = {45, 82, 91, 30, 67};
print(marks.where((m) => m >= 50).toSet());
print(marks.map((m) => m + 5).toSet());
print(marks.any((m) => m < 35));
print(marks.every((m) => m > 20));
}
Simple lesson: loop over a set with for-in, and use where and map just like with lists.
Converting Collections
Removing Duplicates
A very common trick: turn a list into a set, and back into a list.
Remove duplicate names
void main() {
var signUps = ['Asha', 'Ravi', 'Asha', 'Priya', 'Ravi', 'Asha'];
var unique = signUps.toSet();
print(unique);
var cleanList = unique.toList();
print(cleanList);
print('${signUps.length} sign-ups, ${cleanList.length} students');
}
list.toSet()= a set with the duplicates gone.set.toList()= a list, so you can use indexes and sort again.Set.from(list)= another way to make a set from a list.
Simple lesson: toSet() removes duplicates, and toList() turns a set back into a list.
Unmodifiable Sets
Unmodifiable set = a set that can never change.
const {...}= fixed while you write the code.Set.unmodifiable(x)= a locked copy, made while the program runs.
A const set cannot change
void main() {
const weekend = {'Saturday', 'Sunday'};
print(weekend.contains('Sunday'));
weekend.add('Friday');
}
Adding to it fails with an Unsupported operation error.
Easy way to remember
- Order and duplicates matter: List.
- Only "is it in there?" matters: Set.
- Need to remove duplicates:
list.toSet().toList().
Simple lesson: use const or Set.unmodifiable() when a set must never change.
What Is a Queue?
Queue = a collection where items join at one end and leave from the other.
FIFO (First In, First Out) = the first item added is the first item taken out.
For example:
- students waiting at the canteen counter
- print jobs waiting for the school printer
- messages waiting to be sent
A queue can also add and remove at both ends, when you need to.
Simple lesson: a queue is a line: join at the back, leave from the front.
A Real-World Example
At the canteen, students stand in a line.
- A new student joins at the back.
- The shopkeeper serves the student at the front.
- Nobody in the middle is served first.
That is FIFO. Try it:
A queue prints with { }, like a set, but it keeps duplicates and order.
Queue Fundamentals
Importing dart:collection
Queue is not built in like List. It lives in the dart:collection library (see Libraries).
import 'dart:collection';
Creating Queues
Your first queue
import 'dart:collection';
void main() {
var line = Queue<String>();
line.add('Asha');
line.add('Ravi');
line.add('Priya');
print(line);
var printJobs = Queue.of(['Report', 'Timetable']);
print(printJobs);
}
Queue<String>()= an empty queue of text.Queue.of([...])= a queue that starts with these items.
Queue vs List
| Question | List | Queue |
|---|---|---|
| Read any item by index? | Yes, list[3] | No, use elementAt(3) (slow) |
| Remove from the front? | Slow on big lists | Fast |
| Add to the front? | Slow on big lists | Fast |
| Best for | items you look up by position | items you process in order |
A list has to shift every item when you remove the first one. A queue does not.
Simple lesson: use a queue when you add at one end and remove from the other, again and again.
Adding Elements
| Method | What it does |
|---|---|
add(x) | joins at the back |
addLast(x) | joins at the back (same as add) |
addFirst(x) | goes straight to the front |
addAll([...]) | many items join at the back |
Joining the line
import 'dart:collection';
void main() {
var line = Queue<String>();
line.add('Asha');
line.addLast('Ravi');
line.addAll(['Priya', 'Kiran']);
line.addFirst('Teacher');
print(line);
}
addFirst lets someone skip to the front, like a teacher at the canteen.
Simple lesson: add joins at the back, and addFirst jumps to the front.
Accessing Elements
first= the item at the front, the next one to leave.last= the item at the back.elementAt(i)= the item at positioni. It walks the queue, so avoid it in big queues.
Who is at the front?
import 'dart:collection';
void main() {
var line = Queue.of(['Asha', 'Ravi', 'Priya']);
print('Next to be served: ${line.first}');
print('Last in line: ${line.last}');
print('Second in line: ${line.elementAt(1)}');
}
Reading first does not remove the item. It only looks.
Simple lesson: first peeks at the front, and last peeks at the back.
Removing Elements
| Method | What it does |
|---|---|
removeFirst() | takes the front item out, and gives it back |
removeLast() | takes the back item out, and gives it back |
remove(x) | takes out the first x found, gives true or false |
clear() | empties the queue |
Serving the canteen line
An Empty Queue
Nobody is left to serve
import 'dart:collection';
void main() {
var line = Queue<String>();
print(line.isEmpty);
line.removeFirst();
}
The error is Bad state: No element. Check isEmpty or isNotEmpty before removing.
Simple lesson: removeFirst serves the front item, so always check the queue is not empty first.
Queue Properties
length= how many items are waiting.isEmpty=truewhen nobody is waiting.isNotEmpty=truewhen at least one item is waiting.
Serve everyone, one by one
import 'dart:collection';
void main() {
var line = Queue.of(['Asha', 'Ravi', 'Priya']);
print('${line.length} waiting');
while (line.isNotEmpty) {
var student = line.removeFirst();
print('Serving $student, ${line.length} left');
}
print(line.isEmpty);
}
This while (queue.isNotEmpty) pattern is how most queues are processed.
Simple lesson: loop with while (queue.isNotEmpty) and removeFirst() to process every item in order.
Iterating Queues
You can also loop over a queue without removing anything:
Looking at the whole line
import 'dart:collection';
void main() {
var line = Queue.of(['Asha', 'Ravi', 'Priya']);
for (var s in line) {
print('Waiting: $s');
}
line.forEach((s) => print(s.toUpperCase()));
var asList = line.toList();
for (var i = 0; i < asList.length; i++) {
print('${i + 1}. ${asList[i]}');
}
}
for-inandforEach()visit items from front to back.- A queue has no index, so a counted loop needs
toList()first.
Do not add or remove items inside a for-in loop over the same queue.
Dart stops with a ConcurrentModificationError. Use while (isNotEmpty) with removeFirst() instead.
Simple lesson: for-in looks at every item, and removeFirst in a while loop processes them.
Common Use Cases
Where queues are used
Breadth-First Search
Breadth-first search = visit the start, then everything one step away, then everything two steps away, and so on.
Who gets the school notice, level by level?
import 'dart:collection';
void main() {
var tellsTo = {
'Principal': ['Teacher A', 'Teacher B'],
'Teacher A': ['Asha', 'Ravi'],
'Teacher B': ['Priya'],
};
var queue = Queue.of(['Principal']);
while (queue.isNotEmpty) {
var person = queue.removeFirst();
print(person);
queue.addAll(tellsTo[person] ?? []);
}
}
The principal is first, then both teachers, then the students. A queue keeps that order.
Easy way to remember
- Queue = canteen line: first in, first out.
add= join the back.removeFirst= serve the front.- Need an index? Use a List instead.
Simple lesson: use a queue whenever things must be handled in the order they arrived.
TL;DR
- Set = unique values:
{'Asha', 'Ravi'}. Empty:<String>{}. addreturnsfalsefor a duplicate.containsis fast.- union, intersection, difference compare two sets.
list.toSet()removes duplicates.Set.unmodifiablecannot change.- Queue (from
dart:collection):addFirst,addLast,removeFirst,removeLast.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
var s = {}; for a set | {} is an empty set | {} is an empty Map | Set methods like add behave like a Map's. |
houses[0] | Sets have indexes | Use first, or toList() first | An error. |
| Expecting duplicates to be counted | A set counts every add | A set keeps one copy | length is smaller than expected. |
| Mixing up difference order | a.difference(b) is the same as b.difference(a) | The order matters | The wrong students are listed. |
Ignoring add's result | add always adds | It gives false for a duplicate | You miss that a value was already there. |
Changing a const set | const only fixes the name | A const set never changes | An Unsupported operation error. |
No import 'dart:collection'; | Queue is built in | It lives in dart:collection | Undefined name 'Queue'. |
queue[0] | Queues have indexes | Use first, or elementAt | An error. |
removeFirst() on an empty queue | It gives null | It crashes | Bad state: No element. |
Changing the queue in its for-in | The loop sees the change | Use while (isNotEmpty) | ConcurrentModificationError. |
| A queue for lookups by position | Queues are like lists | Use a List for indexes | Slow code. |
Reading first to take the item | first removes it | first only looks | The item is processed twice. |
Check Yourself
Exercises
What to Remember
- A set never holds the same value twice.
{}alone is a map, so write<Type>{}for an empty set.- Use union, intersection and difference to compare groups.
toSet()is the quickest way to remove duplicates from a list.- A Queue is the right tool when you add and remove at both ends.
Next: Maps. You will learn to store pairs, like a subject and its marks, and find any value by its key.