Collections
Overview
A collection is a finite group of some variable number of items (possibly zero) of the same type. Objects in a collection are called elements.
Collections in Kotlin are contained in the kotlin.collections package, which is part of the Kotlin Standard Library.
These collection classes exists as generic containers for a group of elements of the same type e.g. List
Kotlin offers functional processing operations (e.g. filter, map and so on) on each of these collections.
fun main() {
val list = (1..10).toList() // generate list of 1..10
println( list.take(5).map{it * it} ) // square the first 5 elements
}
Under-the-hood, Kotlin uses Java collection classes, but provides mutable and immutable interfaces to these classes. Kotlin best-practice is to use immutable for read-only collections whenever possible (since mutating collections is often very costly in performance).
Collection Classes
Collection Class | Description |
---|---|
Pair | A tuple1 of two values. |
Triple | A tuple of three values. |
List | An ordered collection of objects. |
Set | An unordered collection of objects. |
Map | An associative dictionary of keys and values. |
Array | An indexed, fixed-size collection of objects. |
Pair
A Pair is a tuple of two values. Use var
or val
to indicate mutability. Theto
keyword can be used to indicate a Pair.
fun main() {
// mutable
var nova_scotia = "Halifax Airport" to "YHZ"
var newfoundland = Pair("Gander Airport", "YQX")
var ontario = Pair("Toronto Pearson", "YYZ")
ontario = Pair("Billy Bishop", "YTZ") // reassignment is ok
// immutable, mixed types
val canadian_exchange = Pair("CDN", 1.38)
// accessing elements
val characters = Pair("Tom", "Jerry")
println(characters.first)
println(characters.second)
// destructuring
val (first, second) = Pair("Calvin", "Hobbes") // split a Pair
println(first)
println(second)
}
Pairs are extremely useful when working with data that is logically grouped into tuples, but where you don’t need the overhead of a custom class. e.g. Pair for 2D points.
List
A List is an ordered collection of objects.
fun main() {
// define an immutable list
var fruits = listOf( "advocado", "banana")
println(fruits.get(0))
// advocado
// add elements
var mfruits = mutableListOf( "advocado", "banana")
mfruits.add("cantaloupe")
mfruits.forEach { println(it) }
// sorted/sortedBy returns ordered collection
val list = listOf(2,3,1,4).sorted() // [1, 2, 3, 4]
list.sortedBy { it % 2 } // [2, 4, 1, 3]
// groupBy groups elements on collection by key
list.groupBy { it % 2 } // Map: {1=[1, 3], 0=[2, 4]}
// distinct/distinctBy returns unique elements
listOf(1,1,2,2).distinct() // [1, 2]
}
Set
A Set is a generic unordered collection of unique elements (i.e. it does not support duplicates, unlike a List which does). Sets are commonly constructed with helper functions:
val numbersSet = setOf("one", "two", "three", "four")
val emptySet = mutableSetOf<String>()
A Map is an associative dictionary containing Pairs of keys and values.
fun main() {
// immutable reference, immutable map
val imap = mapOf(Pair(1, "a"), Pair(2, "b"), Pair(3, "c"))
println(imap)
// {1=a, 2=b, 3=c}
// immutable reference, mutable map (so contents can change)
val mmap = mutableMapOf(5 to "d", 6 to "e")
mmap.put(7,"f")
println(mmap)
// {5=d, 6=e, 7=f}
// lookup a value
println(mmap.get(5))
// d
// iterate over key and value
for ((k, v) in imap) {
print("$k=$v ")
}
// 1=a 2=b 3=c
// alternate syntax
imap.forEach { k, v -> print("$k=$v ") }
// 1=a 2=b 3=c
// `it` represents an implicit iterator
imap.forEach {
print("${it.key}=${it.value} ")
}
// 1=a 2=b 3=c
}
Array
Arrays are indexed, fixed-sized collection of objects and primitives. We prefer other collections, but these are offered for legacy and compatibility with Java.
fun main() {
// Create using the `arrayOf()` library function
arrayOf(1, 2, 3)
// Create using the Array class constructor
// Array<String> ["0", "1", "4", "9", "16"]
val asc = Array(5) {
i -> (i*i).toString()
}
asc.forEach { println(it) }
}
You can access array elements through using the []
operators, or the get()
and set()
methods.
Collection Functions
Collection classes (e.g. List, Set, Map, Array) have built-in functions for working with the data that they contain. These functions frequently accept other functions as parameters.
Filter
filter
produces a new list of those elements that return true from a predicate function.
val list = (1..100).toList()
val filtered = list.filter { it % 5 == 0 }
// 5 10 15 20 ... 100
val below50 = filtered.filter { it in 0..49 }
// [5, 10, 15, 20]
Map
map
produces a new list that is the results of applying a function to every element that it contains.
val list = (1..100).toList()
val doubled = list.map { it * 2 }
// 2 4 6 8 ... 200
Reduce
reduce
accumulates values starting with the first element and applying an operation to each element from left to right.
val strings = listOf("a", "b", "c", "d")
println(strings.reduce { acc, string -> acc + string }) // abcd
Zip
zip
combines two collections together, associating their respective pairwise elements.
val foods = listOf("apple", "kiwi", "broccoli", "carrots")
val fruit = listOf(true, true, false, false)
// List<Pair<String, Boolean>>
val results = foods.zip(fruit)
// [(apple, true), (kiwi, true), (broccoli, false), (carrots, false)]
A more realistic scenario might be where you want to generate a pair based on the results of the list elements:
val list = listOf("123", "", "456", "def")
val exists = list.zip(list.map { !it.isBlank() })
// [(123, true), (, false), (456, true), (def, true)]
val numeric = list.zip(list.map { !it.isEmpty() && it[0] in ('0'..'9') })
[(123, true), (, false), (456, true), (def, false)]
ForEach
forEach
calls a function for every element in the collection.
val fruits = listOf("advocado", "banana", "cantaloupe" )
fruits.forEach { print("$it ") }
// advocado banana cantaloupe
We also have helper functions to extract specific elements from a list.
Take
take
returns a collection containing just the first n
elements. drop returns a new collection with the first n elements removed.
val list = (1..50)
val first10 = list.take(10)
// 1 2 3 ... 10
val last40 = list.drop(10)
// 11 12 13 ... 50
First, Last, Slice
first
and last
return those respective elements. slice
allows us to extract a range of elements into a new collection.
val list = (1..50)
val even = list.filter { it % 2 == 0 }
// 2 4 6 8 10 ... 50
even.first() // 2
even.last() // 50
even.slice(1..3) // 4 6 8
-
A tuple is a data structure representing a sequence of n elements. ↩︎