1. Overview

There are various ways to rotate a collection of items with Kotlin. Understanding their differences is important to choose the right way for our use case.

In this tutorial, we’ll explore how to rotate a List efficiently in Kotlin, covering different methods and use cases.

2. Collections.rotate()

The most efficient way to rotate a list is to use the JVM’s built-in rotate() method from the Collections class. Its advantage is that it doesn’t create any intermediate collections and moves each element by swapping it in memory. It additionally facilitates rotation in both left and right directions by indicating the distance. A negative value will rotate to the left, while a positive value will rotate to the right:

@Test
internal fun `rotates a list left using rotate`() {
    // given
    val list = listOf(1, 2, 3, 4, 5)

    // when
    Collections.rotate(list, -2)

    // then
    assertThat(list).isEqualTo(listOf(3, 4, 5, 1, 2))
}

Although this method is efficient and achieves the rotation without any by-product objects, it mutates the original collection, which may not always be the behavior we require. We must also mention that this method only works on mutable List collections.

3. subList()

Kotlin’s built-in method subList() should be preferred if we don’t want to mutate the original list. We can use this method to implement rotation by creating two new lists and merging them, resulting in a rotated copy of the original list. We can write a simple extension method for this use case:

internal fun <E> List<E>.rotateLeftUsingSubList(distance: Int): List<E> {
    return this.subList(distance, this.size) + this.subList(0, distance)
}

The distance parameter indicates the rotation for each element. The algorithm generates a new list by merging the end of the list, starting from the specified distance index to the end, with the beginning of the original list to the index specified by the distance parameter:

@Test
internal fun `rotates a list left using subList`() {
    // given
    val list = listOf(1, 2, 3, 4, 5)

    // when
    val listRotatedLeft = list.rotateLeftUsingSubList(2)

    // then
    assertThat(listRotatedLeft).isEqualTo(listOf(3, 4, 5, 1, 2))
}

As this method creates two intermediate lists, it’s less memory efficient and should be used cautiously for lists holding larger objects. The subList() method creates a view of the original list. Any changes to the original list will affect the result of subList(). Due to this behavior, we should prefer this approach when working with immutable lists.

We should be careful to not mutate the original list between using subList() and rotating the list itself. One way to avoid this behavior is to use slice(), which works similarly and uses subList() internally, though it calls toList() — avoiding this view behavior by shallow-copying all elements from the list.

4. drop() and take()

Alternatively, we can use Kotlin’s built-in methods for collections, drop() and take(). The drop() method returns a sublist of the original list by omitting the first number of elements, while the take() method does the inverse by returning a list consisting of only the first number of elements. By joining these two lists, we can achieve collection rotation to the left:

internal fun <T> Iterable<T>.rotateLeftUsingDrop(distance: Int) =
    this.drop(distance) + this.take(distance)

Let’s test our function:

@Test
internal fun `rotates a list left using drop and take`() {
    // given
    val list = setOf(1, 2, 3, 4, 5)

    // when
    val listRotatedLeft: List<Int> = list.rotateLeftUsingDrop(2)

    // then
    assertThat(listRotatedLeft).isEqualTo(listOf(3, 4, 5, 1, 2))
}

Notice that although we may use any Iterable with this method, which is advantageous if we need to rotate other types of Collections, we see in the above example that the result of drop() and take() is a List.

5. takeLast() and dropLast()

Rotating right is similar to the above method, but we’ll use the takeLast() and dropLast() methods in the opposite order:

internal fun <T> List<T>.rotateRightUsingDrop(n: Int) =
    this.takeLast(n) + this.dropLast(n)

However, these two methods only work on List types, so it may not be the perfect choice for every situation.

6. Conclusion

In this article, we’ve explored a few different ways of rotating a List in Kotlin. In short, if we don’t mind the mutation of the original list, using Collections.rotate() is the way to go. Otherwise, if we require a new instance for the rotated list, a combination of drop() and take() will work great.

As usual, the source code of all these examples is available over on GitHub.

Comments are closed on this article!