List and MutableList

List is one of the most commonly used collection data types. It is an implementation of the Collection interface used to work with a group of ordered data.

The data in a list may be ordered based on when it was added (like if we add 3 after 4 to an Int List, then 4 will appear in the list before 3, much like an array) or may even be ordered based on other ordering algorithms.

As we mentioned earlier, Kotlin distinguishes between mutable and read-only collection types; so, the List interface, which is immutable, contains only the read-only functions, as follows:

  • fun get(index: Int):E: This method is used to get an element from the list at the given index.
  • fun indexOf(element: @UnsafeVariance E):Int: This method is used to identify the index of an element in the list. This method will search for the specified element inside the whole list and return the position of the element if it's in the list. Otherwise, it will return -1.
  • fun listIterator(): ListIterator<E>: In case you want to get an instance of ListIterator (this will be covered later in this chapter, while we discuss Iterator and Iterable).
  • fun subList(fromIndex: Int, toIndex: Int): List<E>: Returns a portion of the list with the specified fromIndex and toIndex value.

So considering this, it contains only read-only functions, how can we have a list with data? While you cannot put data into an immutable list after it gets created, you can definitely create an immutable list with pre-populated data (obviously, otherwise there wouldn't be any purpose in having immutable lists). You can achieve that in many ways, but the most popular one is to use the listOf function.

The listOf function declaration looks like the following (which can be found inside Collections.kt, in the kotlin.collections package):

public fun <T> listOf(vararg elements: T): List<T> 

As we can see in the function declaration, the function takes a vararg parameter of a generic type as an element; the function will return a list instance containing those elements. As you already know, the significance of a vararg argument is that it can contain 0 to almost 64K arguments (if each argument is of 1 byte, a function can have a maximum of 64K bytes allocation, so actually it would be less) within it; so, while creating list with the listOf function, you can call it even without parameters to create an empty list, or call the function with as many arguments as you need (assuming you don't need more than 64K bytes) to create the read-only list with them.

The following program is an example of the listOf function:

fun main(args: Array<String>) { 
    val list = listOf<Int>(1,2,3,4,5,6,7,8,9,10) 
     
    for (i in list) { 
        println("Item $i") 
    } 
} 

In the preceding program, we created a list value containing numbers 1 through 10. We then used a for loop to loop through each element in the list value and print it.

Let's have a look at the following output to validate that:

The i in list inside the braces of the for loop, tells the for loop to iterate through all the elements inside the list value and copy the element to a temporary variable i for each of the iterations.

We will look at more ways to work with collections later in this chapter, but first let us learn different types of collections.

So, continuing our discussions on lists, we've seen how to create an immutable list with pre-defined elements; now, we will look at how to create and work with mutable lists, but before that let's have a look at the ways to create an empty list.

Let's go through the following program:

fun main(args: Array<String>) { 
    val emptyList1 = listOf<Any>()    val emptyList2 = emptyList<Any>() 
     
    println("emptyList1.size = ${emptyList1.size}") 
    println("emptyList2.size = ${emptyList2.size}") 
} 

So, in the preceding program, we created empty lists, one with a listOf function with no arguments, another one is with an emptyList function. Please note that the listOf function, if called without any arguments, calls an emptyList function internally.

The following is the screenshot of the output:

So, we've seen how to work with immutable lists with a pre-defined bunch of elements, but what if we need to add items to the list value dynamically? Kotlin provides you with mutable lists for this purpose.

The following example will help you understand immutable lists:

fun main(args: Array<String>) { 
    val list = mutableListOf(1,2,4)//(1) 
 
    for (i in list) { 
        println("for1 item $i") 
    } 
 
    println("-----Adding Items-----") 
 
    list.add(5)//(2)    list.add(2,3)//(3)    list.add(6)//(4) 
 
    for (i in list) { 
        println("for2 item $i") 
    } 
} 

The following is the output of the program:

Now, let us explain the program. So, at first, we created the list value with mutableListOf function on comment (1), with the items 12, and 4. Note, we skipped the type parameter here, it's not important if you pass the elements to the function, as Kotlin has type interference. We printed the list values before moving ahead to add items.

For listOf or any other collections function, type interference is an issue. So, you'll not need to specify the generic type of the collection in use if you pass the elements or if you've provided the type of the collection itself.

On comment (2), we added item 5 to the list, with the List$add() function, which appends the provided item to the list array.

Then, on comment (3), we used the add function with an index parameter, to add item 4 to the second position (counting from 0, as usual).

Then, again we appended the list array with 5.

So, we added elements to a list array and accessed all the items through the for loop, but what about accessing a single element? Let's have an example to access and modify single elements in Kotlin. Go through the following example:

fun main(args: Array<String>) { 
    val list = listOf( 
            "1st Item", 
            "2nd Item", 
            "3rd Item", 
            "4th Item", 
            "5th Item" 
    ) 
 
    println("3rd Item on the list - ${list.get(2)}") 
    println("4rd Item on the list - ${list[3]}") 
} 

We accessed the third element with index 2 and the fourth element with index 3. The reason is simple and straightforward as, with arrays and in lists, counting begins at 0.

The thing to notice here is that Kotlin provides out-of-the-box support for lists and provides you with a square bracket operator ([]) to access elements of the list value just like an array. In the first get statement, we used the get function with an index to get the element of that index; in the second get statement, we used the square brackets, which, in turn, call that get function.

As lists store items as per their order/index, it's easy to get items from a list with an index; you can easily skip a loop if you want just a particular element from that list and if you know the index for the element you want. Just pass the element to the get function and you have the element.
This get element by index is not supported by other collection interfaces like set (though OrderedSet supports them), which doesn't support the ordering of elements.

So, as we have got a little grip on lists, let us move ahead and have a look at the sets.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.133.160