3

Basically, this code works:

var optionsList = ArrayList<String>()
        optionsList.add("Alpha")
        optionsList.add("Bravo")
        optionsList.add("Charlie")
        optionsList[2] = "Foxtrot"// Assignment works (only) if initialization has been done previously through add()

However, the following makes the application crash (with no compile time errors or warnings):

var optionsList = ArrayList<String>(3)
        optionsList[0] = "Alpha"
        optionsList[1] = "Bravo"
        optionsList[2] = "Charlie"

This seems unexpected and I can't figure out the reason for the crash. In my understanding, both ways should be equivalent - in the first we are allocating memory for and initializing only one element at a time, whereas in the second we are allocating the memory for all elements together at the beginning and later changing values.

Can someone please help me understand what might be going on under the hood here? I have only just started learning Kotlin and have used C++ in the past, so that might be affecting how I am thinking about this.

4
  • set() (overloaded operator for [n] =) can only be used to replace value, not to add new values. It throws IndexOutOfBoundException if there is no element at specified index geeksforgeeks.org/arraylist-set-method-in-java-with-examples Commented Jun 15, 2020 at 2:53
  • Thanks a lot for your answer @AnimeshSahu. The thing is, in that case I struggle to understand what is the point of setting the size of the ArrayList at 3 at the time of declaration. Shouldn't that allocate space, and hence avoid the IndexOutOfBoundException? In other words, what difference does the size declaration make? Commented Jun 15, 2020 at 3:00
  • 1
    @AnimeshSahu your comment looks more like an answer, mind changing it into an answer? Commented Jun 15, 2020 at 3:00
  • Allocating the size first prevents it from having to resize the backing array until it grows beyond the given capacity. Each time it reaches its capacity, under the hood it has to allocate a new, bigger backing array and copy all the values over to it. Commented Jun 15, 2020 at 5:09

1 Answer 1

2

In Kotlin list[n] is a short hand for list.get(n), and with a assignment operator list[n] = value it is translated to list.set(n, value).

The optionsList[2] = "Foxtrot" tries to override the value at 2 and return the old value (that is never null) as defined in KDocs, so if there was no value at that index what will it return?

public E set(int index, E element) {
    Objects.checkIndex(index, size);
    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

It is true that the space is allocated in the memory for the values in

var optionsList = ArrayList<String>(3)

But since operator fun set(index: Int, element: E): E requires to return the old value which is not possible because the value at that index never existed, so it fails to do the operation.

Edit: The point of allocation of space is to reduce the time taken to add a bulk of values, for instance if you wanted to add a large amount of values like 10000 then the pre-allocation (0.6ms) takes around 3.5x less time than dynamically allocated ArrayList (2ms). See: https://pl.kotl.in/iV3ZNNjOC

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot for the detailed answer. This gives me a fairly good idea of what is going on. So if I understand right, this means the only way to initialize elements in an ArrayList at a given (previously unused) index is by using the add() function sequentially, and before that the set() function or the [] operator cannot be used at any index. Would that be the correct understanding?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.