Kotlin Collections – Lists

Using Lists in Kotlin.

// Kotlin often uses Java Collections, but even then you can do more with them
// Completely interoperable with Java
// You can operate on an immutable Collection (add, remove items), but the result will be a brand new instance
// All Collections take a generic type and all the read-only interfaces are covariant (e.g. you can assign a List of BigDecimal to a List of Any)
// If you look at the Collections declaration you will see it only has e.g. isEmpty() and contains(), and is covariant <out E>. MutableCollection adds add(), remove() etc and is not covariant <E>. Cannot assign mutable Collection of e.g. BigDecimal to Any.
// List and Set Collections. Array is considered a Collection but is in kotlin package, not kotlin/Collections. Doesn't implement any Collection interfaces.
fun main() {
    val strings = listOf("spring", "summer", "autumn", "winter") // This is wrong - produces List not ArrayList
    println(strings.javaClass) // returns java.utils.Arrays$ArrayList - can't add or remove anything but can change one of the elements (.set), therefore mutable.
    // Kotlin makes it immutable by not having any functions in the Kotlin List interface that can change the ArrayList
    // However, there are ways around this if the List is passed to Java code so need to be aware!

    val mutableSeasons = strings.toMutableList() // Creates a mutable List from an immmutable one
    mutableSeasons.add("Another season")
    println(mutableSeasons)

    val emptyList = emptyList<String>()
    println(emptyList.javaClass) // returns kotlin.collections.EmptyList. Not much you can do with this list so no need for Java class. Maybe you would return it where your function would usually return an immutable List.
//    println(emptyList[0]) // Still lets you call get() resulting in IndexOutOfBoundsException. Check if empty first if potentially receiving an empty List.

    val notnullList = listOfNotNull("hello", null, "goodbye") // You can use this technique to filter out any potential null results...
    println(notnullList) // ... returning a List of non-null values

    //We can get a standard Java ArrayList by specifically requesting it:
    val arrayList = arrayListOf(1, 2, 4)
    println(arrayList.javaClass) // Mutable List of class java.util.ArrayList

    val mutableList = mutableListOf<Int>(1, 2, 3)
    println(mutableList.javaClass) // Also mutable List of class java.util.ArrayList

    println(mutableList[2]) // We can use [] to get and set items
    mutableList[1] = 20
    println(mutableList)
    mutableList.set(0, 40) // Or we can use the getters and setters
    mutableList.get(2)

    val array = arrayOf("black", "white", "green")
    val colourlist = listOf(array) // Creates a List with one Array item
    println(colourlist) // We probably would have wanted an array of 3 elements, so:
    val actualColourList = listOf(*array)
    println(actualColourList)// or even easier...
    val actualColourList2 = array.toList()
    println(actualColourList2)

    val ints = intArrayOf(1, 2, 3)
    println(ints.toList()) // Converts a Kotlin Array to a primitive array for passing to Java

    // To manipulate Lists:
    println(strings.last()) // Get the last element
    println(strings.asReversed()) // Print them out backwards

    // The long way:
    if (strings.size > 5) {
        println(strings[5])
    }
    // The Kotlin way:
    println(strings.getOrNull(5)) // Will get element 5 or return null

    val ints1 = listOf(1, 2, 3, 4, 5)
    println(ints1.max()) // Gets largest value
    println(actualColourList.zip(strings)) // Only creates pairs for as long as pairs can be made

    val mergedLists = listOf(actualColourList, strings) // Creates a List of 2 Lists
    println(mergedLists)

    val combinedList = actualColourList + strings // Concatenates the 2 Lists
    println(combinedList)

    // To combine two lists and exclude duplicates:
    val strings2 = listOf("spring", "summer", "autumn", "summer", "winter")
    val colourList2 = listOf("black", "white", "red", "black", "red")

    val noDupsList = colourList2.union(strings2)
    println(noDupsList)

    // To remove duplicates without having to combine the List with anything else:
    val noDupColours = colourList2.distinct() // Returns a new list
    println(noDupColours)
}

 

Kotlin Arrays

Kotlin’s handling of arrays

fun main() {
    val names = arrayOf("John", "Jane", "Jill", "Joe") // Assumes Strings

    val longs1 = arrayOf(1L, 2L, 3L) // State Long by use of L
    val longs2 = arrayOf<Long>(1, 2, 3, 4) // Or by declaring datatype in arrayOf<>
    val longs3 = arrayOf(1, 2, 3, 4) // Otherwise will assume Ints

    println(longs2 is Array<Long>)
//    println(longs3 is Array<Long>) // Will fail because is Int
    println(longs3 is Array<Int>)

    println(longs1[2])

    val evenNumbers = Array(16) {i -> i*2} // Use lambda expression to get even numbers up to 30
    for (number in evenNumbers) {
        println(number)
    }

    val lotsOfNumbers = Array(100001) {i -> i} // Numbers from 0 to 100,000
    val allZeroes = Array(100) {0} // One hundred zeroes

    var someArray: Array<Int> // If you don't want to initialise it till later
    someArray = arrayOf(1, 2, 3, 4)
    for (number in someArray) {
        println(number)
    }

    someArray = Array(6) {i -> (i + 1) * 10}
    for (number in someArray) {
        println(number)
    }

    val mixedArray = arrayOf("hello", 22, BigDecimal(10.5), 'a') // Can have array of mixed datatypes i.e. Any
    for (element in mixedArray) {
        println(element)
    }

    val myIntArray = arrayOf(3, 99, 234, 54)
//    DummyClass().printNumbers(myIntArray) // Fails because it expects IntArray but found Array<Int>
    val myIntArray2 = intArrayOf(3, 99, 45, 756) // Use specific Array types
    DummyClass().printNumbers(myIntArray2) // Works!

//    var someOtherArray = Array<Int>(5) // Fails. By declaring the size (5) we are actually trying to instantiate the array, so we would need to add values
    var someOtherArray: Array<Int> // This is okay
    var anotherArray = IntArray(5) // However, primitive type arrays allow this as they're initialised to default, in this case all zeroes
    DummyClass().printNumbers(anotherArray)

    // So can you instantiate an array of fixed size? An array of nulls. Potentially risky as could lead to NullPointerExceptions, so you have to explicitly ask for them.
    // Not a problem with primitive arrays as they are initialised with defaults e.g. zeroes for IntArray
    // However, array of Objects is potentially null, so we use ArrayOfNulls to warn compiler
    val nullableInts = arrayOfNulls<Int>(5)
    for (i in nullableInts) {
        println(i)
    }
    // Try nullableInts[0]. and see how few options are available
    nullableInts[3] = 5
    val intValue = nullableInts[3]?.rem(2) // Now options are available provided you use ?


    // If you want pass an Array<Int> to Java you can convert it:
    DummyClass().printNumbers(evenNumbers.toIntArray()) // evenNumbers was an Array<Int>
    // And vice versa
    val convertedIntArray = myIntArray2.toTypedArray() // Converts a primitive IntArray to Array<Int>
}
public class DummyClass {

    public String isVacationTime (boolean onVacation) {
        return onVacation ? "I'm on vacation" : "I'm working";
    }

    public void printNumbers (int[] numbers) {
        for (int number: numbers) {
            System.out.println(number);
        }
    }
}