12
votes

Why there is such emptyList constructor in Kotlin? It was a immutable List, so there is no way to add or remove its elements and it was empty! So, what is the function of this emptyList?

4
Are you asking why someone would need an empty list? Or why there is a named function to create one such? Or why it is a function and not a constant value?Thilo
FWIW, Java also has Collections.emptyList()Thilo
Actually i want to know why people using emptyList? What for?willy wijaya
In Java you even have Collections.singletonList().ice1000

4 Answers

26
votes

The emptyList is not a constructor but a function that returns and immutable empty list implementation.

The main reason such function exists is to save allocations. Since emptyList returns the same singleton instance every time it is called so one can use it in allocation free manner. Note that the very same object is returned regardless of the element type i.e. emptyList<String>() === emptyList<Int>() is true.

IMHO emptyList also reads a bit better than listOf when used as e.g. a default parameter value:

data class Person(val friends:List<Person> = emptyList())
4
votes

To address

Actually i want to know why people using emptyList? What for?

more directly: you need it e.g. to pass to methods taking lists, or when you have a list which is empty in one branch of your code and not another:

val commandLineOptions: List<String> = when {
    x > 0 -> listOf(...)
    else -> emptyList()
}

runCommand(command, commandLineOptions)
3
votes

@miensol provides a good answer addressing the lower-level details of using a singleton empty list object, minimizing allocations. In my opinion, the allocation of an empty list in JVM-based language is only marginally significant. You will be losing a few bytes of memory or wasting only a few clock cycles when instantiating via listOf(). Of course, if you create empty lists all of the time, this may add up, but it's difficult for me to see where this is applicable.

While I do not know the design intent of the API, I definitely see a semantic reason for its inclusion. As @miensol already stated, emptyList() appears like a good candidate for a default parameter. The reason this is true is because it adheres to the well-known Null-Object Pattern.

Briefly, the pattern suggests a design where we use semantically meaningful types to represent our empty, default, or null values. In languages susceptible to null-pointer exceptions, this is useful because a proper object allows the client to call any of the available class members.

Consider the example below, where we use a default of null.

class AcademicProfile(val courseGradesAsPercent : List<Double>? = null) {

    fun displayAverage() {
        if (courseGradesAsPercent == null)
            println("No average to calculate, please sign-up for a course!")
        else {
            val sum = courseGradesAsPercent.reduce { accumulator, value -> accumulator + value }
            val average = sum / courseGradesAsPercent.count()
            println("Your unweighted average is $average")
        }
    }
}

The intent expressed by this class is that the list of course grades can be in any of the following states:

  • null
  • not-null, but empty
  • filled with elements

Now, suppose we used emptyList() instead as the default:

class AcademicProfile(val courseGradesAsPercent : List<Double> = emptyList()) {

    fun displayAverage() {
        if (courseGradesAsPercent.isEmpty())
            println("No average to calculate, please sign-up for a course!")
        else {
            val sum = courseGradesAsPercent.reduce { accumulator, value -> accumulator + value }
            val average = sum / courseGradesAsPercent.count()
            println("Your unweighted average is $average")
        }
    }
}

In this scenario, there is no confusion between null and empty. If there are no grades, then the list is empty. These semantics are carried into the function by allowing us to call a member on the null-object's type (i.e. List<Double>.isEmpty()). Working with null expresses less about the domain and instead relies on the language's mechanics.

The performance benefit of using the singleton empty list is just icing on the cake!

-1
votes

Actually,even if you declare an empty list,you can still add elements to this empty list by using the "+", because Kotlin override some operators in its libraries:

var l = emptyList<Int>() 
print(l + 1) // output [1]