The Programmers Guide To Kotlin - Collections
Written by Mike James   
Monday, 24 February 2020
Article Index
The Programmers Guide To Kotlin - Collections
Mutable List

Kotlin has all of Java's collections and some. In this extract from the book on Kotlin by Mike James, we look at and its approach to collections and how to work with them.

Programmer's Guide To Kotlin

Now Available as a Print Book

cover

You can buy it from: Amazon

Contents

Some Chapters Already Available On The Web

  1. What makes Kotlin Special (Book Only)
  2. The Basics: Variables, Primitive Types and Functions
  3. Control
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy 
            Extract  Type and its problems
            Extract  Smart Casts
  8. Generics
            Extract Basic Generics
            Extract Covariance & Contravariance
  9. Collections, Iterators, Sequences & Ranges
            Extract     Collections
            Extract Iterators & Sequences
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
            Extract    Annoymous and Lambda Functions
            ExtractInline Functions
  12. Data classes, enums and destructuring
            Extract  Data Classes
            Extract      Enums & Sealed Classes 
            Extract 
         Delegated Properties 
            Extract      Destructuring  **NEW!**
  13. Exceptions, Annotations & Reflection
            Extract Exceptions
            Extract Annotation & Reflection 
  14. Working with Java
            Extract Using Swing  

In the previous chapter we dug deep into generics, but the most common use of generics is to build collection classes, and in this respect Kotlin has everything Java has and some more.

Collection Basics

Generics were invented largely to allow us to implement collections. In this particular case most of the problems of using generic types simply vanish because all we want to do is store a reference to an object and perhaps move it around. In general, the question of calling methods defined on the object involved in the generic only arises as a side issue. In other words, collections store objects without worrying too much about what they actually do.

A collection organizes a set of objects.

The objects are stored as a set of references.

In Java, collections are distinct from simple arrays. In Kotlin, an array is a class like other collection classes, but it is implemented as a primitive Java array when compiled, for reasons of efficiency.

In other words, a Kotlin array behaves a little like a collection, but it has all of the characteristics of a Java array because that is what it is.

However, this said, there are still important differences between a Kotlin array and the collection classes. In particular, an array is static and it cannot change its allocated size. The other collection classes are dynamic and can expand and contract according to what is stored in them. An array also stores primitive types as values, this is why it is efficient. Collection classes, on the other hand, store references to objects. If you do use primitive types in a collection class, then it is boxed by a wrapper class.

Another big difference is that Kotlin provides two versions of the Java collections – a mutable one, which is like the corresponding Java class; and an immutable one, which is new in Kotlin. If you simply use a collection class then you will get the immutable one by default. To use the mutable version you have to change the name to have the word Mutable in front of it e.g. List is immutable and MutableList is mutable.

It is important to understand that the immutable collections that Kotlin provides aren't really immutable in the strongest possible sense. They are simply the standard Java mutable collections with the restriction that they are read-only. Hence here are no efficiency gains to be had from using immutable collections, i.e. no memory optimizations, and there are ways that immutable collections can be modified by casting to a mutable type.

What this means is that in Kotlin, by providing read-only mutable objects, simply provide a level of protection against accidental changes to data.

Immutable collections are also covariant and you can assign a collection of a given type to a collection of its base type.

Finally, it is worth pointing out that while Kotlin collections are based on Java collections, Kotlin adds many methods, using extension methods, see the next chapter, to the collection classes. This extends what you can do without losing compatibility with Java.

List & MutableList

Although the Collection class is the base class for all the collection classes, the List and MutableList are the pair that best serve as an introduction. You can't create an instance of the Collection class; it is mostly useful as the base type for any additional collections you might want to create.

The List is just like an array but it can change its size. The elements of a List have a fixed type, the immutable version is covariant and the mutable version is invariant. As the List doesn't have a set function, it cannot change its size and so it really is just like a covariant version of a standard array. Notice that at the moment List is implemented not as a Java array but as a Java ArrayList, which isn't as efficient because it is still a dynamic data structure.

The first problem with using any of the collections is how to create an instance. This follows the pattern introduced with the array class. That is, each collection class has its collectionOf function which converts a comma separated list of values into an object of the collection’s type. 

For example:

var a=listOf("x","y","z")

creates a List<String> with three elements.

The second way is to use the constructor and supply a function which can be used to initialize the collection. That is:

List(size, init)

creates a list of the specified size and uses the init function to initialize each element in turn.

For example:

var a=List(10,{""})

creates a List<String> with 10 elements all set to the null string. The {“”} is a lambda function that returns the null string.

You can, of course, use a more complicated initialization function.

For example:

var b = List<String>(10,{i->(i.toString())})

creates a list of 10 elements initialized to "0" to "9".

As List is read-only, it is covariant which means you can cast it to a List of a super type. For example:

var b = List<String>(10,{i->(i.toString())})
var c:List<Any>
c=b

Notice that the system still "knows" that c is of type List<String> at run time.

List behaves like an array and it is only when we start to use MutableList that we get any really new behavior.



Last Updated ( Monday, 24 February 2020 )