Skip to main content

Kotlin Sets

Introduction​

Sets are one of the fundamental collection types in Kotlin. A Set is an unordered collection that doesn't allow duplicate elements. This unique characteristic makes sets particularly useful when you need to ensure you're working with distinct elements or when you need to perform mathematical set operations.

In Kotlin, sets come in two main varieties:

  • Immutable sets (read-only)
  • Mutable sets (modifiable)

Let's dive into the world of Kotlin sets and explore how they can make your code more efficient and expressive.

Creating Sets in Kotlin​

Creating an Immutable Set​

The standard way to create a read-only set is using the setOf() function:

kotlin
val fruitSet = setOf("apple", "banana", "orange", "apple")
println(fruitSet) // Output: [apple, banana, orange]
println(fruitSet.size) // Output: 3

Notice that even though we added "apple" twice, it only appears once in the set. That's because sets automatically eliminate duplicates.

Creating a Mutable Set​

When you need to modify a set after creation, use mutableSetOf():

kotlin
val mutableFruitSet = mutableSetOf("apple", "banana", "orange")
mutableFruitSet.add("mango")
mutableFruitSet.remove("banana")
println(mutableFruitSet) // Output: [apple, orange, mango]

Empty Sets​

You can create empty sets as well:

kotlin
val emptyImmutableSet = emptySet<String>()
val emptyMutableSet = mutableSetOf<Int>()

Note the type parameter is required for empty sets to specify what type of elements the set will contain.

Set Implementation Types​

Kotlin provides specialized implementations of sets for different use cases:

HashSet​

The most commonly used implementation, optimized for fast lookups:

kotlin
val hashSet = hashSetOf(1, 2, 3, 4)
println(hashSet) // Output: [1, 2, 3, 4] (order may vary)

HashSets offer constant-time performance for basic operations like add, remove, and contains, but don't maintain any specific order.

LinkedHashSet​

Preserves the insertion order of elements:

kotlin
val linkedHashSet = linkedSetOf("first", "second", "third")
println(linkedHashSet) // Output: [first, second, third]

SortedSet (TreeSet)​

Keeps elements in their natural order (or using a custom comparator):

kotlin
val sortedSet = sortedSetOf(5, 2, 3, 1, 4)
println(sortedSet) // Output: [1, 2, 3, 4, 5]

// With custom comparator (descending order)
val descendingSet = sortedSetOf(compareByDescending { it }, 5, 2, 3, 1, 4)
println(descendingSet) // Output: [5, 4, 3, 2, 1]

Basic Set Operations​

Checking if an Element Exists​

kotlin
val numbers = setOf(1, 2, 3, 4, 5)
println(3 in numbers) // Output: true
println(numbers.contains(6)) // Output: false

Finding Set Size​

kotlin
val colors = setOf("red", "green", "blue")
println(colors.size) // Output: 3
println(colors.isEmpty()) // Output: false

Iteration​

You can iterate through sets just like any collection:

kotlin
val languages = setOf("Kotlin", "Java", "Python")
for (language in languages) {
println("I like $language")
}
// Output:
// I like Kotlin
// I like Java
// I like Python

Mathematical Set Operations​

One of the most powerful features of sets is their built-in support for mathematical set operations.

Union​

Combines two sets (all elements from both sets, without duplicates):

kotlin
val set1 = setOf(1, 2, 3)
val set2 = setOf(3, 4, 5)
val union = set1.union(set2)
println(union) // Output: [1, 2, 3, 4, 5]

Intersection​

Finds common elements between two sets:

kotlin
val set1 = setOf(1, 2, 3, 4)
val set2 = setOf(3, 4, 5, 6)
val intersection = set1.intersect(set2)
println(intersection) // Output: [3, 4]

Difference​

Returns elements in the first set that aren't in the second:

kotlin
val set1 = setOf(1, 2, 3, 4)
val set2 = setOf(3, 4, 5, 6)
val difference = set1.subtract(set2)
println(difference) // Output: [1, 2]

Symmetric Difference​

Elements that are in either set, but not in both:

kotlin
val set1 = setOf(1, 2, 3, 4)
val set2 = setOf(3, 4, 5, 6)
// Symmetric difference can be calculated using union - intersection
val symmetricDiff = set1.union(set2).subtract(set1.intersect(set2))
println(symmetricDiff) // Output: [1, 2, 5, 6]

Practical Examples​

Removing Duplicates from a List​

One of the most common uses of sets is to eliminate duplicates from a collection:

kotlin
val userInputs = listOf("apple", "banana", "apple", "orange", "banana", "mango")
val uniqueInputs = userInputs.toSet()
println(uniqueInputs) // Output: [apple, banana, orange, mango]

// If you need a list back:
val uniqueList = userInputs.toSet().toList()

Checking for Unique Elements​

kotlin
fun hasUniqueCharacters(str: String): Boolean {
return str.length == str.toSet().size
}

println(hasUniqueCharacters("hello")) // Output: false (duplicate 'l')
println(hasUniqueCharacters("world")) // Output: true (all characters unique)

Set as a Dictionary Lookup​

Sets can be used for efficient lookups:

kotlin
val validUsers = setOf("alice", "bob", "charlie")

fun isValidUser(username: String): Boolean {
return username.lowercase() in validUsers
}

println(isValidUser("Alice")) // Output: true
println(isValidUser("Dave")) // Output: false

Tracking Visited Elements​

Sets are excellent for keeping track of visited nodes in graph algorithms:

kotlin
fun depthFirstSearch(graph: Map<String, List<String>>, start: String) {
val visited = mutableSetOf<String>()

fun dfs(node: String) {
if (node in visited) return

visited.add(node)
println("Visiting: $node")

for (neighbor in graph[node] ?: emptyList()) {
dfs(neighbor)
}
}

dfs(start)
}

// Example usage
val graph = mapOf(
"A" to listOf("B", "C"),
"B" to listOf("A", "D"),
"C" to listOf("A", "D"),
"D" to listOf("B", "C")
)

depthFirstSearch(graph, "A")
// Output:
// Visiting: A
// Visiting: B
// Visiting: D
// Visiting: C

Performance Considerations​

It's important to understand the performance characteristics of different set implementations:

  • HashSet: O(1) for add, remove, and contains operations (on average)
  • LinkedHashSet: O(1) for add, remove, and contains operations, with additional overhead for maintaining order
  • TreeSet: O(log n) for add, remove, and contains operations

Choose the appropriate set implementation based on your specific requirements for ordering, performance, and memory usage.

Summary​

Sets in Kotlin offer a powerful way to work with collections of unique elements. They provide:

  • Automatic duplicate elimination
  • Fast membership testing
  • Mathematical set operations
  • Multiple implementations with different performance characteristics

When to use sets:

  • When you need to ensure uniqueness of elements
  • When you need to perform set operations like union, intersection, or difference
  • When you need efficient lookups (contains operations)
  • When the order of elements isn't important (or when you want a specific ordering)

Exercises​

  1. Create a function that takes two lists and returns a list of elements that appear in both lists (using sets).

  2. Write a program that counts the number of unique words in a text file.

  3. Implement a function that determines if two strings are anagrams of each other using sets.

  4. Create a simple spell checker that uses a set of known words and suggests corrections for misspelled words.

  5. Implement a function that finds all pairs of numbers in a list that sum to a given target value, using a set for efficient lookup.

Additional Resources​



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)