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.
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:
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
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 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.
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 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
Let's have a look at how to declare an
ArrayList and then describe
some important methods of the
We can initialize the collection like this:
val array = arrayListOf(1, 2, 3)
Or we can use:
val array = mutableListOf(1, 2, 3)
ArrayList collection corresponds to the one in Java. Kotlin
is using it. [hint]
ArrayList methods and other members
ArrayList implements the
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.
trueif the list contains a given element
size- Returns the element count of the list
- Returns the element at a given position
+=- Adds a new element at the end of the list or at a given index (and shifts the other elements).
-=- 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.
removeAt()- Removes the element at a specific index.
Although it might be confusing,
+= do not return a new
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
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?
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
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
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
Despite the fact that we've already used the
Array like 1000
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 code above creates an
ArrayList of the
1 element, then adds a
2 element and then
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.
ArrayList adds a few extra methods, let's describe them
+=- Adds elements from a given array to the List. Similarly, we call the
-=. 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
Arrayinstance. 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
Comparableinterface, 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
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.