Search code examples
listkotlincollectionsslice

Difference between List.subList and slice in Kotlin


I recently realised there are two very similar functions in Kotlin for getting a portion of a List, but I'm unsure of the difference:

The documentation for List.subList says:

Returns a view of the portion of this list between the specified fromIndex (inclusive) and toIndex (exclusive). The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.

Structural changes in the base list make the behavior of the view undefined.

whereas the documentation for slice says:

Returns a list containing elements at indices in the specified indices range.

Or

Returns a list containing elements at specified indices.

It seems that the key differences are that the first one returns a "view of the portion" of the list, and whether non-structural changes are reflected? However I'm not quite sure what this means.

I looked at the source code for the slice function:

public fun <T> List<T>.slice(indices: IntRange): List<T> {
    if (indices.isEmpty()) return listOf()
    return this.subList(indices.start, indices.endInclusive + 1).toList()
}

But it returns a list from the subList function.

Could someone explain the differences between these two functions and when you might want to use one over the other?


Solution

  • The key in List<T>.slice function is the .toList() at the end, which will create a new List with the elements, using a shallow element copy.

    For summary:

    • .slice() will create a new List with the shallow copy of the subset of elements; if the original list contains objects, changes to values within the objects will be seen in the slice, but the replacement of an object or scalar in the original list will not be seen in the slice
    • .subList() is only a view of the original List, which will reflect changes to both the objects/scalars in the list as well as changes to the values within the objects in the list

    You can see differences here: https://pl.kotl.in/-JU8BDNZN

    fun main() {
        val myList = mutableListOf(1, 2, 3, 4)
        val subList = myList.subList(1, 3)
        val sliceList = myList.slice(1..2)
        println(subList) // [2, 3]
        println(sliceList) // [2, 3]
        
        myList[1] = 5
        println(subList) // [5, 3]
        println(sliceList) // [2, 3]
    }