Instruction 3

Arrays

Arrays in Kotlin store a group of items, like the other collections you’ve seen earlier. They’re represented by the Array class. The primary differences lie in its properties. Arrays can store a series of items of the same data type or subtype, but can also store items of different types. Also, arrays are are immutable.

fun main() {
  val oceans = arrayOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
  println(oceans.toList()) // To see the items in the Array, you convert it to a List
}

Run the above code. The output contains the following:

[Pacific Ocean, Southern Ocean, Arctic Ocean, Atlantic Ocean, Indian Ocean]

Note: The contents of the array in the displayed output are in square braces, [], like the List and Set. Also, an Array, like the List, can contain duplicates.

To create an empty array, Kotlin assigns an area of memory for the array. Your guess is as good as mine on where that will be. Use emptyArray:

fun main() {
  val oceans = emptyArray<String>()
  println(oceans)
}

Run the above code and note the output. The array is represented only as an address in memory. Your assigned memory location will probably be different:

[Ljava.lang.String;@34a245ab

You could also create an Array using its constructor, specifying its type, Array<Int>. Here, you are declaring the array using its gereric syntax: Array<T>. This means you can specify the type of elements the array can contain. When you use generic syntax, you need to specify the size and initial data upfront. The following code will create an array of Int pre-populated with the initial data, 0. Additionally, it will create an Array with same number of elements as specified for the size, 3:

fun main() {
  val books = Array<Int>(3) { 0 }
  println(books.toList())
}

Run the above code. The output contains the following:

[0, 0, 0]

The initialization code for the constructor has an implicit it variable that holds the index value of the items in the array. With this knowledge, recreate the books array, but with a factor of 2.

fun main() {
  val books = Array<Int>(3) { it * 2 }
  println(books.toList())
}

Run the above code. The output contains the following:

[0, 2, 4]

You can rename this implicit parameter to suit your use case by using the arrow function, ->, following:

fun main() {
  val books = Array(3) { index -> index * 2 }
  println(books.toList())
}

Run the above code. The output is the same as the it reference:

[0, 2, 4]

Arrays have many optimization methods, like the arrow function above. These methods make Arrays suitable for holding a fixed group of items. But they lack many of the convenience methods that allow you to manage a List.

Arrays access items based on the item index. The index starts from position zero as the first item, like Lists. An Array accesses elements faster than a List can. Also, arrays have fixed size and, in this way, behave like an immutable List. Here’s how you access items by index in an Array:

fun main() {
  val oceans = arrayOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
  println(oceans[1]) // Prints "Southern Ocean"
}

To update an item, specify it by its position and assign the new value:

fun main() {
  val oceans = arrayOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean")
  println(oceans.toList())
  oceans[3] = "Indian Ocean"
  println(oceans.toList())
}

Run the above code. The output contains the following:

[Pacific Ocean, Southern Ocean, Arctic Ocean, Atlantic Ocean]
[Pacific Ocean, Southern Ocean, Arctic Ocean, Indian Ocean]

Comparing Arrays

To compare arrays, use contentEquals(). The items will have to be the same in both arrays and at the same position for the two arrays to be equal:

fun main() {
  val oceans1 = arrayOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
  val oceans2 = arrayOf("Pacific Ocean", "Southern Ocean", "Arctic Ocean", "Atlantic Ocean", "Indian Ocean")
  println(oceans1.contentEquals(oceans2))
}

Run the above code. The output contains the following:

true

Using Sequences

Sequences work a bit differently from collections. Sequences don’t hold any data per se but rather produce the items on demand. Sequences in Kotlin are represented by the Sequence interface. First, create an empty sequence with the in-built emptySequence() function:

fun main() {
  val weekdays = emptySequence()
  println(weekdays.toList())
}

Run the above code. The output contains the following:

Not enough information to infer type variable T

Update the code by supplying the type used in the Sequence:

fun main() { val weekdays = emptySequence() println(weekdays.toList()) }

Run the above code. The output shows the code succeeds and contains the following:

[]

Use sequenceOf() in-built function to create a sequence:

fun main() {
  val weekdaysSequence = sequenceOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
  println(weekdaysSequence.toList())
}

Run the above code. The output contains the following:

[Monday, Tuesday, Wednesday, Thursday, Friday]

Sequences can be created from collections, too. In the next example, a sequence of days is created from a list of days:

fun main() {
  val weekdaysList = listOf("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
  val weekdaysSequence = weekdaysList.asSequence()
  println(weekdaysSequence.toList())
}

Run the above code. The output contains the same as the sequenceOf() code:

[Monday, Tuesday, Wednesday, Thursday, Friday]

Note: You’ve used toList() many times already. In the above code, you’re converting the List into a Sequence - the opposite conversion of Sequence to a List.

There’s a generateSequence() function that generates, well, a Sequence. generateSequence() takes a seed which is the initial value. It also takes a function that defines how the numbers in the sequence are generated. In the example below, the function generates a sequence starting at the number 10. The generator adds 2 on every iteration, it + 2, creating a sequence of even numbers starting at 10. The code then takes the first 6 elements, prints the count, and then prints the list:

fun main() {
  val evens10to20 = generateSequence(10) { it + 2 }
  println(evens10to20.take(6).count())
  println(evens10to20.take(6).toList())
}

Run the above code. The output contains the following:

6
[10, 12, 14, 16, 18, 20]

Sequences produce elements while you iterate over them. This brings about some interesting distinctions between sequences and collections.

When you define or request the items from a sequence, you need to ensure that the operation is terminal. It should have an end. The example above limits the number of iterations to 5, then joins the items into a string and prints it out. The sequence itself, by definition, is infinite. If you don’t limit it, your program will likely crash since there’s no end. In Kotlin Playground, you’ll get an error like:

Evaluation stopped while it's taking too long

A Collection loads all items into memory. The items are accessed using indexes or keys. A Sequence computes the items while you iterate over them. This means that for a large number of items, a Collection will likely occupy a lot of memory. Compare this to a Sequence, which takes less memory. But, for a small number of items, on-demand fetching with a Sequence can be inefficient. It incurs extra overhead iterating over the items from the beginning of the Sequence.

You can’t access items by indexes or keys when using sequences. You’ll have to convert the sequence to a collection if you want to manage it as one. Sequences are best for managing a large number of items. It could use fewer operations and consume less memory than a collection.

See forum comments
Download course materials from Github
Previous: Instruction 2 Next: Demo 1