Boris
Boris

Reputation: 31

Why isn't there a sorted() function for MutableList that returns a MutableList?

Kotlin's indexed collections List and MutableList can be sorted using a set of functions BUT some of them like sorted() return a List, instead of List and MutableList respectively. What's the reason behind casting the sorted MutableList to a List inside the function and then we manually have to recast it again to MutableList if we want to use it as an argument in a function? Why can't there be an extension function that does that?

public fun <T : Comparable<T>> MutableList<T>.sorted(): MutableList<T> {
    // sort the Mutable List
}

P.S. I had to add this here as an edit to the original post as it was too long by >300 characters to be posted under one of the answers as a comment.

I am studying Kotlin with the help of a website that doesn't mention sort() neither in the topic "Ordering elements in collection" - https://hyperskill.org/learn/step/22630 nor in the topic "Work with MutableList" - https://hyperskill.org/learn/step/14902, a site that was recommended by https://developer.android.com/. In both cases they used sorted(). So I had the impression that sorted() when used on a List returns a List, which would be an entirely new List but sorted, and when used on MutableList returns MutableList, which is itself but sorted. Up until I read the answers I had no idea that a function called sort() existed, that can sort a MutableList. So when I had to use sorted() on a MutableList while practicing I was very surprised to learn that it returned a List instead of MutableList. Then I looked into the source code for sorted() on github and I was puzzled even more, none of it made any sense.

Upvotes: 2

Views: 151

Answers (2)

Tenfour04
Tenfour04

Reputation: 93834

  1. It would be super confusing to overload the function and give it a different behavior. You would have to keep careful track of whether your MutableList has been upcast to a List or other Iterable anywhere in you code/chain of functions before reaching the sorted call to be able to know what behavior you’re going to get.

  2. Kotlin has a naming convention for functions. “____ed” gerunds are used to describe functions that return a copy of whatever came out of the iterable after the operation. If the function has a regular verb, then you are mutating it. In the case of a MutableList, you can use sort().

  3. It would be unusual to need to copy a mutable list into another mutable list and start mutating that. Usually, copying into a list is trivial. If your code is in a section where performance is critical enough that you need to be using MutableLists, then you would probably want to be doing all your operations in place in the same list rather than be using functional chains.

Side note: you typically should never cast your List to a MutableList. This is not a safe operation. There is no promise that the underlying List is a MutableList and it theoretically could be changed in the future, which would change your working code into code that would crash. The current implementation actually returns read-only lists in some cases. It’s also possible with other classes you come across in other libraries that they pass you a read-only list to read from but that is still being mutated internally so it would break the behavior of the class if you started mutating it from elsewhere.

Upvotes: 4

Ryan M
Ryan M

Reputation: 20177

Because the result of sorting a list may not be mutable (and there's an easy way to convert it if that's what you need).

...and in fact, if you look at the implementation of sorted(), that's often the case:

public fun <T : Comparable<T>> Iterable<T>.sorted(): List<T> {
    if (this is Collection) {
        if (size <= 1) return this.toList()
        @Suppress("UNCHECKED_CAST")
        return (toTypedArray<Comparable<T>>() as Array<T>).apply { sort() }.asList()
    }
    return toMutableList().apply { sort() }
}

For Collection subclasses on the JVM, that .asList() call ends up calling Arrays.asList, which returns "a fixed-size list backed by the specified array" (emphasis added).

This means that...

What's the reason behind casting the sorted MutableList to a List inside the function and then we manually have to recast it again to MutableList

...you can't do this cast safely, because you may not have a MutableList (or at least, not one that allows length changes, which is probably something you expect a MutableList to allow).

Instead, you should do:

someMutableList.sorted().toMutableList()

This will guarantee that what you get is a normal MutableList.

Upvotes: 3

Related Questions