Get up to 80 % extra points for free! More info:

Lesson 2 - Lists with arrays in Kotlin

In the previous lesson, Introduction to collections and genericity in Kotlin, we made an introduction to collections and showed what genericity is. In today's Kotlin tutorial, we'll learn more about Lists, which represent one type of collection that we've already encountered before.

Array

First and foremost, let's take a quick peek back to arrays, which were the first collection we learned about throughout the courses. One of the main characteristics of an array is that it has a fixed number of elements and we can never change its length. If we want to add something to the array, we have to create a new one. For this reason, it's not even considered a collection by some sources. Let's have an example:

val sampleArray = arrayOf(1, 2, 3)
val sampleArray2 = sampleArray + 4 // sampleArray2 is a new instance and have no relationship to the sampleArray2 variable
println(sampleArray.size) // the size of the original array is still 3, because we can't change the size of the current instance

As you can see, Kotlin smartly gets around this limitation caused by the fixed array size, using an immutable class wrapping the original JVM array. The elements in an array are indexed numerically, starting from zero.

Since the data is of the same type (either exactly the same or of a common ancestor) they take up the same space in memory. Individual array items are stored in memory as an uninterrupted sequence, like in a row. We can imagine an array of integers as something like this:

Array in Kotlin - Collections in Kotlin

If we wanted to access the 5th item, we would simply access the beginning of the array and then jump forward by 4 times the type size (in this case, the size of an Int). Reading and writing at array indexes are done within a constant time complexity. If you are confused by this term, you may see it as a way of writing into array indexes immediately and so that we can read them.

The main disadvantage to using arrays is that we can't add or delete items during runtime. Unfortunately, we often need to do this. Imagine creating new array instances (its copies) of more than 1000 elements over and over again; the program would be slowed down radically.

Lists

Lists are collections that allow us to add and delete items at runtime. They can be indexed numerically as arrays, but they don't have to. Generally, there are two types of lists: Array lists and Linked lists. Kotlin doesn't support linked lists; if we ever needed one, we'd have to use standard Java collections.

Array lists

Lists often take advantage of the fact that we are able to create new arrays during runtime (keep in mind that the size of an array cannot be changed during runtime).

In this case, the list is a class that contains methods for adding and removing elements (and many other useful methods, which aren't relevant to us at the moment). The class essentially wraps the array and contains an extra variable where the number of elements is stored. When the instance is being created, an array of a given amount of elements is created inside, and the variable carrying the number of elements is set to 0. When we add the first element, it's stored at the 1st index in the array and the number of elements is incremented. We would be able to add elements like this until we fill the array. Once the array is full, we simply create a new array, let's say, twice as big. We copy the elements from the old array into the new array and then throw away the old one. Once this new array is filled as well, the process will be repeated. This is the way the Kotlin ArrayList collection, which we haven't met yet, internally works. We can imagine an ArrayList like this:

The ArrayList collection in Kotlin - Collections in Kotlin

The ArrayList in the picture contains eight elements. The elements are stored in an internal array of 12 elements. The last four elements are unused and from the outside look as if they aren't even there.

The advantage here is the fast random access to the elements using numeric indexes thanks to the inner array. The downside to it is the time required to create the new array and copy the elements into it, although this process doesn't occur too often. Another less painful drawback is that the collection occupies more memory than necessary. Anyway, this list type is still the most used collection in Java and is fairly well optimized.

Array list is, in Kotlin, represented by the ArrayList class. Let's have a look at how to declare an ArrayList and then describe some important methods of the ArrayList class:

We can initialize the collection like this:

val array = arrayListOf(1, 2, 3)

Or we can use:

val array = mutableListOf(1, 2, 3)

The ArrayList collection corresponds to the one in Java. Kotlin is using it. [hint]

ArrayList methods and other members

Java's ArrayList implements the List interface. Kotlin does use Java's ArrayList, but also adds its own methods wrapped around this Java's ArayList to make it look more like a Kotlin collection. The basic methods are:

  • clear() - Removes all the elements.
  • contains() - Returns true if the list contains a given element
  • size - Returns the element count of the list
  • get() or [] - Returns the element at a given position
  • add() or += - Adds a new element at the end of the list or at a given index (and shifts the other elements).
  • remove() or -= - Removes a given element. This feature is very useful when we store instances of a class in the list (e.g. users). We don't have to keep their numerical indexes, we just call e.g. list.remove(carl).
  • removeAt() - Removes the element at a specific index.

Although it might be confusing, -= and += do not return a new ArrayList instance.

List

We can also use listOf() which is immutable and used quite often in Kotlin. Every addition or deletion returns a new instance. Because it doesn't really make sense to have methods such as add() returning Unit (nothing) on an immutable collection, we won't find them here. However, we do have overloaded operators (+=, -=) which always return a new instance.

When should I use List and when ArrayList?

Both ArayList and List are lists which we can use for the same purpose. Where is the difference then? As I've already mentioned, a variable of the List type is immutable (not changing) and every operation results in a new List instance. On the other side, ArrayList can be changed. Consider having some kind of a 2D game with the game board represented by a 2D array (e.g. chess or tic-tac-toe). In this case, ArrayList would be a better choice because it's unnecessary to create new instances over and over again, especially when we change only one object in it. If we moved a stone one place forward, we'd created a completely new "array". Isn't that unnecessary?

ArrayList has its disadvantages as well. For example, we can't simply delete elements when iterating through it. It's because we would be deleting them from the very same instance that is currently being iterated through.

Usually, List is a solid choice. If you want to change the elements often, using ArrayList is definitely a way to go. However, when working with threads, absence of immutability may cause further problems.

Example

Despite the fact that we've already used the Array like 1000 times and ArrayList isn't much different, for completeness' sake, here's an example of its use:

val array = arrayListOf<Int>(1)
array += 2
array -= 1 // We can delete!
println(array)

The output:

[2]

The code above creates an ArrayList of the Int type with a 1 element, then adds a 2 element and then deletes the 1 element. The resulting list is then printed to the console. We work with indexes as if we worked with an array, however, we can add or delete elements on the same instance at runtime.

The ArrayList adds a few extra methods, let's describe them too:

  • addAll() or += - Adds elements from a given array to the List. Similarly, we call the removeRange() method or -=. It's a good idea to use this method since since it saves us from having to use an additional loop.
  • toTypedArray() - Returns a read-only Array instance. Useful to encapsulate the elements in the collection.
  • size - A property storing the number of elements in the list.
  • lastIndexOf() - The indexOf() method alternative, returns the index of the last occurrence of a given element in the list.
  • removeAll() - Removes all the elements passed in an array as argument.
  • reverse() - It's important that the elements implement the Comparable interface, otherwise, this method will throw an exception. All of the basic classes and structures in Kotlin implement Comparable, but we have to implement it manually in our own classes.

We can add range using the += operator as well:

array += (1..5)

That's enough for now. As soon as we get into lambda functions, we can have a look at more methods, such as filter() or map().

Make sure to try out other methods like sort(), searching, and so on. We'll get to more detailed work with collections further.

In the next lesson, Multidimensional arrays in Kotlin, we'll discuss multidimensional arrays, if you have skipped them when learning, and also dictionaries and sets in Kotlin.


 

Previous article
Introduction to collections and genericity in Kotlin
All articles in this section
Collections in Kotlin
Skip article
(not recommended)
Multidimensional arrays in Kotlin
Article has been written for you by Samuel Kodytek
Avatar
User rating:
No one has rated this quite yet, be the first one!
I'm intereseted in JVM languages and Swift
Activities