The Programmers Guide To Kotlin - Arrays & Strings
Written by Mike James   
Monday, 10 July 2017
Article Index
The Programmers Guide To Kotlin - Arrays & Strings
String Literals

Arrays and strings are the most basic of the more advanced data types. They are so familiar that you might think you don't need to look at them, but Kotlin has its own way of doing most things.

Programmer's Guide To Kotlin Third Edition

kotlin3e360

You can buy it from: Amazon

Contents

  1. What makes Kotlin Special
  2. The Basics:Variables,Primitive Types and Functions 
  3. Control
         Extract: If and When 
  4. Strings and Arrays
  5. The Class & The Object
  6. Inheritance
  7. The Type Hierarchy
  8. Generics
  9. Collections, Iterators, Sequences & Ranges
        Extract: Iterators & Sequences 
  10. Advanced functions 
  11. Anonymous, Lamdas & Inline Functions
  12. Data classes, enums and destructuring
        Extract: Destructuring 
  13. Exceptions, Annotations & Reflection
  14. Coroutines
        Extract: Coroutines 
  15. Working with Java
        Extract: Using Swing
  16. Compose Multiplatform
        Extract: Compose Layout ***NEW!

<ASIN:B0D8H4N8SK>

The array and the string are the two most basic data structures - you almost can't write a program without using one of the two. Arrays in Kotlin are based on Java's array object, but the implementation is different enough to confuse. Similarly the Kotlin string is essentially the same as the Java string, but with some interesting additions. In this chapter we look at these two fundamental data structures.

Notice that there are many programmers who are of the opinion that you shouldn't use a simple array unless performance is an issue. Instead you should use a more sophisticated data structure from the collection library - for example, List or MutableList. These are not only more flexible but in theory less error prone than the simple array. It doesn't make any real difference if you do adopt the "use a List in place of array" approach because the array is still the place where it all begins.

Arrays

Arrays in Kotlin aren't native objects, but instances of the Array class. This is a generic class, see Chapter 8 for a full account of Generics, and you can create arrays of any other type. Creating an array is slightly different in Kotlin in that you have to call a constructor or a factory function that makes the array. Kotlin arrays my be slightly different, but they are JVM Java arrays and work in exactly the same way under the cover. 

The arrayOf function takes a list of values all of the same type and returns them as an array.

For example to create an array of three integers you would use:

var a=arrayOf(1,2,3)

The array type has get and set functions which accept and index but these are also mapped to the [] operator and so you can write:

Int b=a[0]
a[0]=5

or

Int b=a.get(0)
a.set(0,5)

You can create an array of any type in the same way. For example:

var a=arrayOf(1.2,2.2,3.3)

creates an array of floats and

var a=arrayOf('a','b','c')

creates an array of chars.

This is a completely general mechanism in that you really can use it to create an array of any type. For example suppose we have the following simple class, see Chapter 5:

class myClass(val v:Int) {
    var myProperty = v
}

then the function call:

var a=arrayOf(myClass(0),myClass(1),myClass(2))

creates an array of three instances of myClass. 

If you want arrays of primitive types then it is more efficient to use byteArrayOf, intArrayOf, shortArrayOf, longArrayOf, booleanArrayOf, charArrayOf, floatArrayOf and doubleArrayOf. The objects created by these functions have the same methods as the array type, but they do not inherit from it, i.e. they are not subtypes of Array. They also create arrays of unboxed elements. That is, if you create an array using:

var a=intArrayOf(1,2,3)

then the elements of the array are raw integers and not boxed integers, i.e. Int objects. This is usually more efficient in use. 

Larger Arrays

You can see that arrayOf is very useful, but defining arrays by specifying a list of values is very limited. For example, how can you create an array of 1000 integers?

In many other languages you would do this by creating an uninitialized array and then arrange to initialize it at some later time - usually via a for loop. Of course, in Kotlin you either have to initialize a variable or declare it to be nullable and so the casual approach of just creating an array of uninitialized arrays isn't going to work. 

Kotlin provides two approaches.

The first is to use a factory function to create an array of nullable types all initialized to null. To do this you have to make use of the generic function:

arrayOfNulls<type>(size)

This creates an array of the specified type, as a nullable type, of the size specified. For example:

var a= arrayOfNulls <Int>(1000)

This creates an array of 1000 Ints all set to null. You can assign any value to one of the elements, but if you try to use an element in an expression you either have to assign to another nullable or test to make sure that the element isn't null, i.e. you have to play by the rules for nullable types. For example:

var a= arrayOfNulls <Int>(1000)
    var b:Int=0
    b=a[0]

generates a compile time warning that b isn't a nullable type. Changing b's declaration to: 

var b?:Int=0

makes it work. 

A much better alternative in most cases is to initialize a non-nullable type. This can be done using the Array constructor which accepts a size parameter and a function to do the initialization:

Array(size, intializer)

The type of the array is determined by the type the initializer returns. The function is passed a single Int, the index, and the value it returns is stored in that element. For example:

var a = Array(1000, { i -> i * 2 })

creates an array of 1000 Ints initialized so that a[i]=2*i. The initialization function can be as complicated as you like but the most common is:

 var a = Array(1000, {  0 })

which zeros the array. Notice that the function simply returns zero and doesn't need the value of the parameter. 

A more sophisticated example creates 1000 instances of an object:

var a = Array(1000, { i -> myClass(i)  })

Limitations

There are some limitations of Kotlin arrays that you need to keep in mind if you are familiar with any language other than Java. The first is that a Kotlin array is static in the sense that you cannot change its size once it has been declared. If you want a dynamic array that can change its size you need to use List and MutableList - see Chapter 9 on collections. A second limitation is that Kotlin arrays are invariant. Exactly what this means is explained in the chapter on collections, but essentially you cannot cast an array of one type of element to an array of another type, even if the element is a super class. That is, you cannot use:

var a = arrayOf(1,2,3,4)
var b:Array<Any>   
b=a

which you might well expect to be allowed given that an array of Any can have elements of any type.  For example:

var b=Array<Any>(10,{0})
b[0]="mystring"
b[1]=1.1
println(b[0])
println(b[1])
println(b[2])

works perfectly and prints a string, a float and an integer. If you are working with an array of Any you clearly have to be careful to check the type of any element you are going to work with.

It is also worth noting that Kotlin has no array slicing facilities, i.e. the ability to specify a sub-array, that are found in languages such as Python. There is a good chance that these will be added in the future. 

Array processing

A very standard idiom is to use a for loop to process the elements stored in an array. The for loop was introduced in the Chapter 3 but it is worth describing the different ways in which an array and a for loop fit together. 

The simplest array for loop is:

for(i in b.indices){
      println(b[i])
  }

In this case the for loop runs from 0 to size-1. That is, it is equivalent to:

for(i in (0..b.size-1)){
      println(b[i])
  }

As well as the standard for loop, arrays also have forEach and forEachIndexed functions. Notice that these are not control structures that are part of the language but array methods. 

The forEach function accepts a function with a single parameter. The function is called repeatedly with the parameter set to each element of the array in turn. For example:

b.forEach({a->println(a)})

The forEachIndexed method works in the same way, but its function accepts two parameters, the element and the index. For example;

b.forEachIndexed({a,i->println(a);println(i)})

Creating multidimensional arrays is implemented in the same way as in most other modern languages. A 2-D array, for example, is just an array of arrays. For example:

var a=arrayOf(arrayOf(1,2,3),arrayOf(4,5,6),arrayOf(7,8,9))

creates a 3 x 3 array. The array is 1-D array of three elements in which each element is in its turn a three-element array.

How do you write a for loop for this:

 for(i in a.indices){
        for(j in a[i].indices)
            println(a[i][j])
    }

There are, of course, many variations on how to create a multidimensional array.  For example to create a 100 x 100 array you can use:

var b=Array(100, {Array(100,{0})})

which creates an array of 100 arrays of 100 Ints set to zero. 

Whatever method of creating a multidimensional array you use, it is important not to fall into the trap of:

var col=arrayOf(1,2,3)
var b=arrayOf(col,col,col)

This appears to create a 3 x 3 array. It does, but the three columns are the same object so if you change b[0][0] you will find that b[1][0] and b[2][0] have been changed to the same value. 

The array object has a very large number of methods which can be used to manipulate it. It has all of the Java methods and a few new ones. There are too many to go into the use of each one but consult the documentation of the standard library before you start to write a function to do anything that might possibly be a common operation. 



Last Updated ( Monday, 10 July 2017 )