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 [email protected]. I’d greatly appreciate your feedback! :)