Exceptions
In Kotlin there’s no difference between checked and unchecked exceptions. Everything’s unchecked so you don’t need to specify any throws or handle any exceptions.
Try as an expressions
Just like if
and when
, try
introduces an expression.
The returned value is either the last expression in the try block or the last expression in the catch block(s).
val a: Int? = try { parseInt(input) } catch(e: NumberFormatException) { null }
Collections
Kotlin has the same collection interfaces as Java, but it adds two new options.
Queue a collection used to hold multiple elements prior to processing (FIFO with the exception of priority queue)
Deque a collection used to hold multiple elements prior to processing (FIFO or LIFO)
Interfaces | Hash table impl. | Resizable Array Impl. | Tree Impl. | Linked List Impl. | Hash table + Lined List Impl. |
---|---|---|---|---|---|
Set | HashSet | TreeSet | LinkedHashSet | ||
List | ArrayList | LinkedList | |||
Queue | |||||
Deque | ArrayDeque | ||||
Map | HashMap | TreeMap | LinkedHashMap |
Kotlin Collection Classes
Kotlin relies fully on Java standard library classes, extending them with additional functions.
Immutable and Mutable Collections
A mutable collection can be updated in place by adding, removing or replacing an element.
An immutable collection, while it provides the same operations-addition, removal or replacement via the operator functions, will end up producing a brand-new collection, leaving the original one untouched.
The most basic interface for working with collections is kotlin.Collection
(List, Set)
The kotlin.MutableCollection
interface allows you to modify the data in a collection.
Benefits of Mutable vs Immutable
You should try to use read-only interfaces everywhere in your code. Use the mutable variants only if you need it.
Sequences
A sequence returns values through an iterator. They’re great for scenarios when the size of the collection is not known in advance. It’s like a list that goes on and on.
You can convert any collection to a sequence as such
people.asSequence()
.map(Person::name)
.filter { it.startsWith("A") }
.toList()
Lists
Kotlin doesn’t have dedicated syntax constructs for creating lists or sets
Collection Type | Read-only | Mutable |
---|---|---|
List | listOf() | arrayListOf(); |
Set | setOf() | HashSetOf(); linkedSetOf(); sortedSetOf(); |
Map | mapOf() | hashMapOf(); linkedMapOf(); sortedMapOf(); |
List extension functions that doesn’t exist in Java
val planets = listOf(...)
planets.first()
planets.last()
planets.asReversed()
planets.elementAtOrNull(9)
Sometimes you have a mutable list and you want to return a snapshot of a collection at a particular point in time, that’s guaranteed not to change.
class Controller {
private val _items = mutableListOf<String>()
val items: List<String> get() = _items.toList()
}
Read-only view of mutable collections
Any changes made to the underlying collection would be reflected in the view automatically.
val carManufacturers: MutableList<String> = mutableListOf(...)
// creating a read-only view
val carsView: List<String> = carManufacturers
println("cars view: $carsView")
Sets
LinkedHashSet
defines the iteration ordering, which is the order in which elements were inserted
HashSet
does not guarantee that the order will remain constant over time
TreeSet
elements are ordered using their natural ordering, or by a comparator
// contains 1,21,2,6,3
val intSet: Set<Int> = setOf(1, 21, 21, 2, 6, 3, 2)
// contains 0,8,9,11
val sortedInts: java.util.TreeSet<Int> = sortedSetOf(11,0,9,11,9,8)
// contains a,x,z
val charSet: java.util.LinkedhashSet<Char> = linkedSetOf('a', 'x', 'a', 'z', 'a')
Set extension functions
val intSet: Set<Int> = setOf(1, 21, 2, 6, 3)
// 6.6
intSet.average()
Manipulating Collections
Filter
The filter function transforms a collection and filters out elements that don’t satisty the given predicate.
The result is a new collection that contains only the elements from the input collection that satisfy the predicate.
It cannot filter, it can only modify.
data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
people.filter { it.age > 30 }
Map
The map function applies the given function to each element in the collection and collects the results into a new collection.
val list = listOf(1,2,3,4)
list.map { it * it } // 1, 4, 9, 16
There’re also the methods filterKeys() / mapKeys()
& filterValues() / mapValues()
.
Quantifiers
All, Any, Count & Find
A common task is to check whether all or some elements in a collection match a certain condition.
Here’s a predicate that will check whether a person is 27 or younger
val people = listOf(Person("Alice", 27), Person("Bob", 31))
val canBeInClub27 = { p: Person -> p.age <= 27 }
println(people.all(canBeInClub27)) // false
println(people.any(canBeInClub27)) // true
println(people.count(canBeInClub27)) // 1
// a synonym of find is firstOrNull
println(people.find(canBeInClub27)) // Person(name=Alice, age=27)
count vs size. count only tracks the number of matching elements, not all the elements.