Manage Fixed-Size Collections with Array

There are a couple of ways of creating an array in Kotlin. We might, for example, use the arrayOf() function as follows:

		val array1 = arrayOf("Red", "Green", "Blue")

We can iterate through an array with a simple for loop such as the one shown in figure 90.

			for (item in array1) {
				println(item)
			}
Figure 90 - a simple for loop being used to iterate through an array

We can also use the arrayOf() function to create an array with different types of element. Note that an array can only contain elements of the type specified for it so if we, for instance, specify an array of string objects, we can only put string objects in the array.

However, in Java, we can specify that the type is Object and such an array can contain any objects and in Kotlin we can do something similar with a type of Any (Any is the highest level object in the Kotlin hierarchy).

			val mixed: Array<Any> = arrayOf("A String", 12)
			for (item: Any in mixed) {
				println(item)
			}
Figure 91 - an example of an array of type any

Figure 91 shows an example which is similar to the previous example but in this case we have specified a type of Any. Note that in this case, I have specified it explicitly but if the type was omitted, Kotlin would infer a type of Any.

We can also create an array of fixed size without initialising any of the values (in other words, each element will initially contain a null value) we can do that with:

		val nulls = arrayOfNulls<String>(3)

We can iterate through this array and print each value, as we did for the previous arrays and in this case, the result will be null printed three times.

We can now set the value of each individual element using simple array syntax. For instance, if we want to set the first element to “Red”, we would do that with:

		nulls[0] = "Red"

We can also set a value using the set() function and this takes two parameters, the index and the value. So, let’s say we want to set the value of the second element to “Blue”, we would do that with:

		nulls.set(1, "Blue")

Note that the index, as we would expect with an array, starts at 0 so if we were to try to execute code such as:

		nulls.set(3, "Blue")

this would generate an “out of bounds” error.

To create an array of integers, we can use the arrayOf() function and provide a list of integers as in:

		val intArray = arrayOf(3,4,5)

or we can use the intArrayOf() function as in:

		val intArray2 = intArrayOf(6,7,8)

Figure 92 shows the decompiled bytecode for these two methods of creating an array.

			public final class MainKt {
			   public static final void main(@NotNull String[] args) {
				  Intrinsics.checkParameterIsNotNull(args, "args");
				  Integer[] intArray = new Integer[]{3, 4, 5};
				  int var4 = intArray.length;

				  int item;
				  for(item = 0; item < var4; ++item) {
					 int item = intArray[item];
					 boolean var5 = false;
					 System.out.println(item);
				  }

				  int[] intArray2 = new int[]{6, 7, 8};
				  int var8 = intArray2.length;

				  for(var4 = 0; var4 < var8; ++var4) {
					 item = intArray2[var4];
					 boolean var6 = false;
					 System.out.println(item);
				  }

			   }
			}
Figure 92 - the decompiled bytecode showing the difference (such as it is) of the two methods of creating an array of integers seen above

If we compare the lines:

		Integer[] intArray = new Integer[]{3, 4, 5};

and

		int[] intArray2 = new int[]{6, 7, 8};

which are the two lines in which the arrays are created, the first one corresponding to arrayOf() and the second to intArrayOf(), it seems that the first one is using the Integer class and the second is using the primitive int. To the best of my knowledge, this distinction doesn’t exist in Kotlin which suggests to me that there is no difference at all between the two methods.

From Stack Overflow, see here or here, this seems to be the case and it is noted here that an array of primitives, unlike an array of member of the Integer class, have a default initial value of zero which suggests that intArrayOf() would, if necessary, initialise all elements to zero whereas arrayOf() wouldn’t. In the examples shown above, we are providing the initial values for each element of both the arrays so this is not relevant.

This might be something worthy of further investigation later!

For the remainder of this section, anything relating to arrays can be assumed to work for either type of array.

Elements of an array can be sorted using the sort() function as in:

		intArray2.sort()

An array can be sorted in reverse order using the function sortedArrayDescending() and this actually returns an array so we might use something like:

		val intArray3 = intArray2.sortedArrayDescending()

So we are not sorting the array in this instance, it will remain unchanged but we are creating a new array with all of the elements of the previous array sorted in descending order. The type is determined by the original array so we are creating a new array of the same type.

In the example shown, this is an array of integers but the syntax works equally well for an array of strings and, I imagine, any type of data which can be ordered.

These examples show arrays being used with the built-in types, but they can also work with more complex types including your own custom types so we could have, for instance, an array of ClothingItem or Person objects.

Manage Ordered Collections with Lists

While arrays are static, that is, once declared, the size of an array is fixed and it is not possible to add or remove elements, Kotlin supports a number of dynamic collections types including lists, sets and maps.

As in Java, we use them by creating concrete instances of the interfaces, but whereas Java allows us to choose the concrete class (I guess this means that you can choose a class to implement list, for example), Kotlin doesn’t. Instead, we create the instances using a variety of functions as we did with arrays.

We will start this example by creating a list with:

		val colourList = listOf("Red", "Green", "Red")

The list interface has a useful toString() function built-in so we can display the list using simple object syntax in a comma delimited list simply with:

		print(colourList)

and this gives the output:

			[Red, Green, Red]

We can use the lists size method to obtain the length of a list as in:

		val length = colourList.size

In creating our list, we have created a concrete instance of the class, ArrayList and we can verify with a line of code such as:

		println(colourList::class.simpleName)

This is, as it happens, how Kotlin is implementing a list but there is no guarantee that this will not change. The safest way to create a list is therefore to use a function such as listOf() so you don’t have to worry about the implementation details.

As with arrays, we can specify the type of object to be included in the list, so our list declaration would become:

		val colourList = listOf<String>("Red", "Green", "Red")

and IntelliJ shows that this is not required. However, if we now try to add a different type of object to the list, we will see two things. First, we will get a type error since the new item is not a string and second, we will now see that the type declaration is required.

Again, we can simply remove the type declaration and Kotlin will now allow the inclusion of a number or any other type of object in our list.

Alternatively, if we want our type declaration to be clear and unambiguous, we can simply specify the type as Any so the list declaration becomes:

		val colourList = listOf<Any>("Red", "Green", "Red", 12)

We can add an item to a list with a statement such as:

		colourList.add("Yellow")

but this actually gives us an error and this is because our list is actually immutable. To allow items to be added or removed, the list must be declared specifically as immutable. We do this with something like:

		val colourList2 = mutableListOf<String>()

and we can then add items with a statement such as:

		colourList2.add("Yellow")

In this case, of course, we could simply have amended the declaration of our original list to make it mutable. In either case, we now have a mutable list so we can add or remove items but bear in mind the fact that a list is an ordered collection so the items in the list will remain in the same order as they are declared or added. However, it is possible to sort the list using the sort() method or sort in descending order by using the sortedDescending() method and this works in exactly the same way as sorting with arrays.

In particular, note that sortedDescending() returns a new list so we will normally create a list and assign it the result of the sortedDescending() function as in:

		val sortedList = colourList2.sortedDescending()

As with add, the remove function requires that the list be mutable and assuming it is, we can then remove elements based on their position in the list or their value. To demonstrate this, let’s assume that the contents of colourList2 are:

		[Yellow, Green, Pink, Purple, Orange]

We now execute the commands:

		colourList2.remove("Pink")
		colourList2.removeAt(2)

The first of these statements removes “Pink” and the second one removes “Purple”. Note that “Pink” had an index value of 2 but after the first statement has been executed, the remaining elements may have new index values. For instance, “Yellow” has an index value of 0 and this does not change. On the other hand, “Orange” has an index value of 4 which becomes 3 and finally 2 as these two statements are executed.

Manage Unordered Collections with Set

A set in Kotlin is a set, just as it is in Java. That is, it is an unordered list of items where each item is unique. As with lists and arrays, we can create mutable and immutable sets depending on the method used to create the set.

For instance, we can create a mutable set with:

		var colourset = mutableSetOf("Red")

Again, type will be inferred if it is not specified, in the example above this will be string so we can’t add any other type of item. If we want to be able to do that, we either provide a different type of value along with string in the initialisation which will result in the inference of type Any or we can explicitly specify the type in the declaration as in:

		var colourset = mutableSetOf<Any>("Red")

We can add values with statements such as:

	
		colourset.add("Green")
	  colourset.add("Blue")

Similarly, we could remove elements using their value as in:

		colourset.remove("Green")
	  colourset.remove("Blue")

Since a set is an unordered list, we cannot add or remove items based on their index – elements in a set do not have an index value.

Note that it is perfectly possible for us to add an element to the set if it already exists as a member of the set but it will be ignored.

For instance, let’s say that we have a main class as shown in figure 93.

			fun main(args: Array<String>) {

				var colourset = mutableSetOf<Any>("Red")

				colourset.add("Green")
				colourset.add("Blue")
				colourset.add("Red")

				println(colourset.size)
				println(colourset)
			}
Figure 93 - code to create a set and add some elements to it

Here, we have created a set with one value and then added three further values, including the initial value – we have tried to add “Red” to the set but it is already an element of the set.

If we now print the size of the set or the set itself, we will see that it has three elements and these are:

		[Red, Green, Blue]

Note that we can create a collection such as a mutable list as a copy of our set and we can add duplicate values or add elements based on index value:

			val colourlist = colourset.toMutableList()
			colourlist.add("Green")
			colourlist.add("Red")
			colourlist.add(0, "Blue")

			println(colourlist.size)
			println(colourlist)
Figure 94 - copying a set to a list allows us to, for instance, add duplicates

The code to do this is shown in figure 94 and it is worth noting here that we are simply using the set to provide the initial values of the set and the results displayed will show that our list has a size of 6 and the list is:

		[Blue, Red, Green, Blue, Green, Red]

Slightly more interesting is to reverse this and copy the list to a set. For instance, with the list here we can easily look at the elements and see that there are three unique elements but there may be instances when we need to calculate how many unique instances there are in a list (or indeed, in an array).

			val newSet = colourlist.toMutableSet()
			println("The size of our list of colours is ${colourlist.size} including    duplicates")
			println("The size of our list of colours is ${newSet.size} NOT including duplicates")
Figure 95 - code to convert a list to a set in order to determine the number of unique elements in the set

With the code shown in figure 95, we have copied our list which contains 6 elements, 3 of which are unique, to a set.

We have then printed the lengths of both the list and the set and the results are as follows:

		The size of our list of colours is 6 including duplicates
	The size of our list of colours is 3 NOT including duplicates

Determining whether an element is already in the set is seems to be fairly straightforward and is perhaps only a little bit more complex if we are dealing with more complex objects.

			val itemSet = mutableSetOf<ClothingItem>()

			itemSet.add(ClothingItem("Shirt", "XL", 14.99))
			itemSet.add(ClothingItem("Shirt", "XL", 14.99))
Figure 96 - creating a set where the type is ClothingItem and adding two duplicate elements to this set

As we can see, in the code in figure 96, we are adding one element to the set and then adding what seems to be the same element again. In this case, if we then check the size of the set we will find that it is one. This is because the set checked each property of the ‘new’ elements against each existing element in the set and found a match therefore denoting that this is a duplicate.

			val itemSet = mutableSetOf<ClothingItem>()

			itemSet.add(ClothingItem("Shirt", "XL", 14.99))
			itemSet.add(ClothingItem("Shirt", "M", 14.99))
Figure 97 - the same code as shown in figure 96 but this time, the size in the second clothing item has changed to 'M'

If we execute the code in figure 97, we will find that this set has two elements because each element has the same description and price, but the size is different and therefore these are unique elements.

Manage Key-Value Pairs with Map

A map, as in Java, is a collection of key value pairs where the key must be unique. Determining uniqueness is similar to the method used to determine uniqueness of the elements in a set. That is, the keys can be of any type and if that type is string, for instance, it’s a simple case of is the string the same as any string already acting as a key.

If the key is a complex object like a ClothingItem, uniqueness is determined by comparing each property of the items and if they are all the same, these cannot both be used as keys.

As with sets, it is possible to add a duplicate item (that is, a key value pair where the key already exists in the map) but duplicates are ignored.

			val colourMap = mapOf(
					Pair("Red", 0xFF0000),
					Pair("Green", 0x00FF00),
					Pair("Blue", 0x0000FF)
			)

			println(colourMap)
Figure 98 - the code to create a map and add three elements to it

Figure 98, shows a snippet of code where we have created a map called colourMap and then used the Pair method to add three elements to the map.

Note that we have not specified the type but this has been inferred as string for the key and integer for the value. Actually, when we print the contents of colourMap, we can see that the values are indeed printed as decimal integers rather than the hex values we had given them.

Again, if we want to be explicit with the types, we can declare the colourMap as

		val colourMap = mapOf<String, Int>()

which imposes this restriction on any element subsequently added or we can declare one or both of the key and value to be of type Any.

Recall that when we looked at lists, we used a line of code to check the name of the class we were instantiating and we can do that again here with:

		println(colourMap::class.simpleName)

From this, we can see that we have created an instance of a LinkedHashMap and again, using the top level method, mapOf() means that we don’t have to worry about the implementation details and whether they change.

So this is a map instance and it is immutable. If we want to create a mutable map, we can do that with the code shown in figure 98.

			val stateMap = mutableMapOf<String, String>()

			stateMap.put("New York", "Albany")
			stateMap.put("Oregon", "Salem")
			stateMap.put("Washington", "Olympia")
			stateMap.put("California", "Sacramento")

			println(stateMap)
Figure 99 - code to create a mutable map, add several elements and then print the map

Note that in order to add an element to a mutable map, we are using the put() method which takes two parameters corresponding to a valid key value pair (in this case, both have been explicitly declared to be strings.

With an immutable map, obviously it will only include those elements it was initialised with using the keyword Pair, but we can also use this to initialise our mutable map with one or more values. If we wanted to do this, our line of code to declare stateMap would become:

	val stateMap = mutableMapOf<String, String>(
		Pair("Alaska", "Juneau")
	)

Note that the spacing here is for readability only and we could equally have typed this as:

		val stateMap = mutableMapOf<String, String>(Pair("Alaska", "Juneau"))

without changing the functionality.

We can iterate through our mutable map using a Java style for loop with the code shown in figure 100.

			for (state in stateMap.keys) {
				println("The capital of $state is ${stateMap.get(state)}")
			}
Figure 100 - the code to iterate through a mutable map using a Java style for loop

There are a couple of interesting points to note here. First of all, we are iterating through the keys. In other words, our loop variable of state refers to the key of each element and we will see this in the print statement when state is replaced by the key in each iteration.

We are then using the key to ‘look-up’ the corresponding value and we do that with the get function which takes the key as a parameter and returns that corresponding value.

So this gives the output:

		The capital of Alaska is Juneau
		The capital of New York is Albany
		The capital of Oregon is Salem
		The capital of Washington is Olympia
		The capital of California is Sacramento

Kotlin, however, provides a slightly more elegant way to do this where we declare two loop variables rather than just one. This gives us the code shown in figure 101.

			for ((state, capital) in stateMap) {
				println("The capital of $state is $capital")
			}
Figure 101 - the slightly neater Kotlin method of iterating through a map with a for loop

In this case, for each iteration through the loop, we are referencing both the key and value for each element so we are not iterating only through the keys so we can remove the keyword keys.

In addition, because we have a variable (in this case capital) to represent the value in each iteration, the print statement is much more readable.

In both cases, the output is exactly the same.

These examples are fairly simple but as with other collections, we can also create maps using complex objects such as our ClothingItem objects. In fact, this makes a map an ideal collection type for a shopping cart because we can create key value pairs where the key is an item of clothing and the value is the quantity.

Note that we have to do it this way around because a particular item of clothing will be unique in the cart but the quantity will not (we might want to buy one shirt and one hat, for example).

The code in figure 102 shows the declaration of an appropriate map to define a shopping cart and several key map pairs.

			val cart = mutableMapOf<ClothingItem, Int>()
			cart.put(ClothingItem("Shirt", "XL", 14.99), 2)
			cart.put(ClothingItem("Pants", "32", 19.99), 4)
			cart.put(ClothingItem("Hat", "8.5", 24.00), 1)
			println(cart)
Figure 102 - code to declare a mutable map to act as a shopping cart and create three key value pairs

Challenge: Sum Up a Collection

The challenge relates to the shopping cart we created in the previous section. The ending point for that section is now the starting point for this challenge so we are starting with two files:

	•	The MainKt file with all the code not related to the shopping cart removed
	•	The ClothingItem data class which is inside a package called model

Figures 103 and 104 provide the code from these files respectively.

			import model.ClothingItem

			fun main(args: Array<String>) {

				val cart = mutableMapOf<ClothingItem, Int>()
				cart.put(ClothingItem("Shirt", "XL", 14.99), 2)
				cart.put(ClothingItem("Pants", "32", 19.99), 4)
				cart.put(ClothingItem("Hat", "8.5", 24.00), 1)
				println(cart)

			}
Figure 103 - the code in our MainKt file
			package model

			data class ClothingItem(var type: String?,
									val size: String,
									var price: Double)
Figure 104 - the ClothingItem data class which is in the package, model

So, if we run the code now we are going to have the shopping cart created and populated and then printed out but the display will be simply using the ClothingItem class's built-in toString() function. The resulting output is shown in figure 105.

{ClothingItem(type=Shirt, size=XL, price=14.99)=2, ClothingItem(type=Pants, size=32, price=19.99)=4, ClothingItem(type=Hat, size=8.5, price=24.0)=1}

In a real world example, we would expect a more user-friendly output, perhaps showing the total cost for each line as well as the total cost of the order. The challenge is to iterate through the loop, make the necessary calculations and then display the results in a nice and user-friendly format.

The simplest solution is to create a variable which I will call total and initialise with 0.00. To make the challenge a little bit more difficult, I want to display the amounts as neatly formatted currency items and to match the challenge, I want these to be displayed as US dollar amounts so the next steps are to set the locale to US and create a variable called formatter to create the correct format for my currency amounts.

Next, I want to iterate through the map using two variables (using the technique taught in the previous section) and these represent a clothing item and a quantity so for each iteration we are grabbing the corresponding key value pair.

Inside the loop I have two statements. The first is displaying a line of text corresponding to a line from the cart and this is displaying the item’s type, the cost for each individual item, and the total cost for that item taking into account the quantity ordered (so this is simply the item’s price multiplied by the quantity.

The next line adds the total amount (again, the item’s price multiplied by the quantity) to the overall order total (the variable labelled total).

When the loop terminates, I then print a line to separate the order total from the individual lines and then the actual order total as a nicely formatted US dollar amount.

The completed code is shown in figure 105. Note that during the challenge, I didn’t make any changes to the ClothingItem class so the finished code is as shown in figure 104 and is not repeated here.

			import model.ClothingItem
			import java.text.NumberFormat
			import java.util.*

			fun main(args: Array<String>) {

				val cart = mutableMapOf<ClothingItem, Int>()
				cart.put(ClothingItem("Shirt", "XL", 14.99), 2)
				cart.put(ClothingItem("Pants", "32", 19.99), 4)
				cart.put(ClothingItem("Hat", "8.5", 24.00), 1)

				var total = 0.00
				Locale.setDefault(Locale.US)
				val formatter = NumberFormat.getCurrencyInstance()

				for ((item, qty) in cart){
					println("Item ${item.type} @ ${formatter.format(item.price)} = ${formatter.format(item.price*qty)}")
					total+=item.price*qty
				}

				println("---------------------")

				println("Total: ${formatter.format(total)}")
			}
Figure 105 - the completed MainKt file for the Sum Up a Collection challenge

One point about this solution is that we can make it a little bit more elegant by adding a new variable to calculate the total cost for each line. This is adding to the total number of lines in the for loop but it has the advantage that we are not performing the calculation within the print statement or when adding this amount to the total.

Figure 106 shows the for loop with this modified approach.

			for ((item, qty) in cart){
				val line = item.price*qty
				println("Item ${item.type} @ ${formatter.format(item.price)} = ${formatter.format(line)}")
				total+=line
			}
Figure 106 - the for loop modified to make it look a little bit tidier

Additional note form the solution video, it is suggested that it might be a little bit tidier to replace the put statements used to populate the cart with simple assignment statements and you can do this with a simple IntelliJ intention action.

This gives us amended code as shown in figure 107.

			val cart = mutableMapOf<ClothingItem, Int>()
			cart[ClothingItem("Shirt", "XL", 14.99)] = 2
			cart[ClothingItem("Pants", "32", 19.99)] = 4
			cart[ClothingItem("Hat", "8.5", 24.00)] = 1
Figure 107 - the put statements from the challenge solution converted to simple assignment operations

I noticed that with the previous version, IntelliJ was displaying a warning to the effect that these should be converted to assignment operations but the code works equally well in either case.