1. Overview
In this quick tutorial, we’ll introduce the Kotlin’s Collections API, and we’ll discuss the different collection types in Kotlin and some common operations on collections.
2. Collection vs. Mutable Collection
First, let’s take a look at different types of collections in Kotlin. We will see how to initialize basic types of collections.
The Collection interface supports read-only methods while MutableCollection support read/write methods.
2.1. List
We can create a simple read-only List using method listOf() and read-write MutableList using mutableListOf():
val theList = listOf("one", "two", "three") val theMutableList = mutableListOf("one", "two", "three")
2.2. Set
Similarly we can create a read-only Set using method setOf() and read-write MutableSet using mutableSetOf():
val theSet = setOf("one", "two", "three") val theMutableSet = mutableSetOf("one", "two", "three")
2.3. Map
We can also create a read-only Map using method mapOf() and read-write MutableMap using mutableMapOf():
val theMap = mapOf(1 to "one", 2 to "two", 3 to "three") val theMutableMap = mutableMapOf(1 to "one", 2 to "two", 3 to "three")
3. Useful Operators
Kotlin’s Collections API is much richer than the one we can find in Java – it comes with a set of overloaded operators.
3.1. The “in” Operator
We can use expression “x in collection” which can be translated to collection.contains(x):
@Test fun whenSearchForExistingItem_thenFound () { val theList = listOf("one", "two", "three") assertTrue("two" in theList) }
3.2. The “+” Operator
We can an element or entire collection to another using “+” operator:
@Test fun whenJoinTwoCollections_thenSuccess () { val firstList = listOf("one", "two", "three") val secondList = listOf("four", "five", "six") val resultList = firstList + secondList assertEquals(6, resultList.size) assertTrue(resultList.contains("two")) assertTrue(resultList.contains("five")) }
3.3. The “-“ Operator
Similarly, we can remove an element or multiple elements using “-” operator:
@Test fun whenExcludeItems_thenRemoved () { val firstList = listOf("one", "two", "three") val secondList = listOf("one", "three") val resultList = firstList - secondList assertEquals(1, resultList.size) assertTrue(resultList.contains("two")) }
4. Other Methods
Finally, we will explore some common methods for collection. In Java, if we wanted to leverage advanced methods, we would need to use Stream API.
In Kotlin, we can find similar methods available in the Collections API.
We can obtain a sublist from a given List:
@Test fun whenSliceCollection_thenSuccess () { val theList = listOf("one", "two", "three") val resultList = theList.slice(1..2) assertEquals(2, resultList.size) assertTrue(resultList.contains("two")) }
We can easily remove all nulls from a List:
@Test fun whenFilterNullValues_thenSuccess () { val theList = listOf("one", null, "two", null, "three") val resultList = theList.filterNotNull() assertEquals(3, resultList.size) }
We can filter collection items easily using filter(), which works similarly to the filter() method from Java Stream API:
@Test fun whenFilterNonPositiveValues_thenSuccess () { val theList = listOf(1, 2, -3, -4, 5, -6) val resultList = theList.filter{ it > 0} assertEquals(3, resultList.size) assertTrue(resultList.contains(1)) assertFalse(resultList.contains(-4)) }
We can drop first N items:
@Test fun whenDropFirstItems_thenRemoved () { val theList = listOf("one", "two", "three", "four") val resultList = theList.drop(2) assertEquals(2, resultList.size) assertFalse(resultList.contains("one")) assertFalse(resultList.contains("two")) }
We can drop items first few items if they satisfy the given condition:
@Test fun whenDropFirstItemsBasedOnCondition_thenRemoved () { val theList = listOf("one", "two", "three", "four") val resultList = theList.dropWhile{ it.length < 4 } assertEquals(2, resultList.size) assertFalse(resultList.contains("one")) assertFalse(resultList.contains("two")) }
We can group elements:
@Test fun whenGroupItems_thenSuccess () { val theList = listOf(1, 2, 3, 4, 5, 6) val resultMap = theList.groupBy{ it % 3} assertEquals(3, resultMap.size) assertTrue(resultMap[1]!!.contains(1)) assertTrue(resultMap[2]!!.contains(5)) }
We can map all elements using the provided function:
@Test fun whenApplyFunctionToAllItems_thenSuccess () { val theList = listOf(1, 2, 3, 4, 5, 6) val resultList = theList.map{ it * it } assertEquals(4, resultList[1]) assertEquals(9, resultList[2]) }
We can use flatmap() to flatten nested collections. Here, we are converting Strings to List<String> and avoiding ending up with List<List<String>>:
@Test fun whenApplyMultiOutputFunctionToAllItems_thenSuccess () { val theList = listOf("John", "Tom") val resultList = theList.flatMap{ it.toLowerCase().toList() } assertEquals(7, resultList.size) }
We can perform fold/reduce operation:
@Test fun whenApplyFunctionToAllItemsWithStartingValue_thenSuccess () { val theList = listOf(1, 2, 3, 4, 5, 6) val finalResult = theList.fold(0, {acc, i -> acc + (i * i)}) assertEquals(91, finalResult) }
5. Conclusion
We explored Kotlin’s Collections API and some of the most interesting methods.
And, as always, the full source code can be found over on GitHub.