Lists, Dictionaries, Iterations & More

Python uses dictionary, list and tuple to hold collections information, with set also available (but not used as quite as often).

Kotlin provides List and Map as collections, and their mutable forms, MutableList and MutableMap as the main solutions for collections equivalent to Python. 

Further, Set and MutableSet are available matching the python ‘set’. 

‘List’ is equivalent to python ‘tuple’, as a tuple is an immutable list.  In kotlin, there are many other options, some inherited from java, and the most common and mostly used for compatibiliy with Java but they all have a logical role:

tuple -> List, Pair, data class

The python tuple is an immutable list, and the simple ability to use tuples  has a special role in the language with any expression with a comma creating a tuple.

Background: The good and bad of the ubiquetous python tuple

The good and bad of tuples can be seen in this :

>>> a, b = 1, 2 
>>> a, b = b, a 
>>> a, b 
(2, 1)

The good is that tuples have almost zero syntactic overhead, all you need is a ‘,’ (comma). The bad is that it can be confusing when there is tuple, as opposed to other uses of a ‘,’ in python (e.g. to separate parameters).  Note that on the left of the assignment, it looks like a tuple, but the left of an assignment is special syntax and not a tuple.  While a tuple can hold values, it cannot hold names.  So construct like the one below:

>>> a, b = 1, 2
>>> my_tuple = a, b
>>> *(my_tuple), = 6, 7
>>> a, b  # see, a and b are not changed
(1, 2)

Does not assign to a name appearing on the left of the assignment. In this case the ‘,’  is not indicating a tuple, but indicating ‘destructured assignment’.  Not every ‘,’  in python indicates a tuple even though a comma alone may indicate a tuple.  The comma syntax being brief is very useful most of the time, but can lead to some quirks as to whether a ‘,’ means a tuple or has another use.

>>> a = 3
>>> type(a)
<class 'int'>
>>> a = 3,
>>> type(a) #just one comma and a is tuple
<class 'tuple'>
>>> def test(a):
       print(type(a)
>>> test(1,) # does the comma mean a tuple or not?
<class 'int'>
>>> test((1)) # add brackets to make a tuple?
<class 'int'>
>>> test((1,),) # finally, now we have a single parameter that is a tuple
<class 'tuple'>

coding python tuples in kotlin

Kotlin List is close to a direct replacement for tuple, but unlike tuples and lists in python, there is no special syntax with using special brackets. “:List” for the type, “listOf()” to instance the list.

var (a1,b1) = listOf(1,2) // destructured assign a1 = 1, b1 = 2
var (a2,b2) = Pair(1,2)  // alternative destructured assign
data class XY(val x:Int, val y:Int)
var (a3,b3) = XY(1,2)  // destructured assign using data class

Note the ‘listOf() syntax to create a literal, but type is ‘List’ in a declaration.

Even in a declaration, a list can be used as a ‘drop in replacement’ for a tuple.  The syntax of declaring from a list is not as brief as python, and is not really within the kotlin ‘idiom’ to use List for a destrutured declaration.

In reality, a destructured declaration is a clear indicator that each element of the data is distinct in nature, and not really a collection.  The most common alternative to a data class in kotlin for destructured declarations are the ‘Pair’ or ‘Triple’, which are actually data classes, but without usage specific property names.

So while List is a direct equivalent to tuple, consider Pair or even a data class as the best substitute depending on the usage.

list -> MutableList

The python list can also be used in situations that are not really usage as a collection, but this tends to occur less than with lists. If there are the indexes to the list are literals, or the items in the list are not all of the same type, then consider if the list should be replaced by something other than a MutableList.  A true collection will be indexed mostly within loops. Use ‘mutableListOf() to instance a MutableList type.

common list operations:

val myList = mutableListOf(1,2) // myList cannot be reassigned, but list is mutable
val added = myList.add(33)  // add the value 3 to the (end of the) list
//  not 'add' return true if successful
myList.add(1,55) // insert into list at index 1 (2nd location)
//  list is now 1,55,2,33
remove(55)  // first the first entry matching 55 and remove (return true if successful)
val popped = removeAt(2) // remove the value found at index 2 and return that value

dictionary -> Map or MutableMap (or data class)

The direct replacement for dictionary is the MutableMap, but with no python equivalent to the Map, dictionaries are often used as maps.  If the dictionary is declared with literal values in place, then a Map (declared as mapOf( key to value) ) will be the replacement.  If the dictionary is declared empty, then a MutableMap ( declared with mutableMap() )  is likely to be the substitute.   Take care that dictionaries with literals strings for keys a probably really object substitutes and best replaced by a data class.

Common map operations.

val myMap = mutableMapOf<String,Int>()
//declared an empty map, type cannot be inferred without data
myMap.keys()
myMap.values()
myMap.toList()  // equivalent to python .items()




‘*’ and **

In python, any iterable can be used to provide parameters to a function.  Any dictionary can be used to provide keyword arguments.  kotlin has the concept of ‘varag’ which can have values provided by the ‘*’ prefix to an iterable, just as with python.  However, there is currently no equivalent to the ‘**”.  (more notes to be added on the * to be added)

list comprehension and other iterations vs map, filter, reduce

Python added map, filter and reduce together with lambda around 1994 , and then list comprehensions around 2000 with python 2.0. In that link Guido (creator of python)  presents strong arguments for list comprehensions, but notes that some people have suggested limitations to python lambda syntax is part of why in python comprehensions are favoured.  An argument is also presented on performance advantages, which in reality applies to python, but not to languages such as kotlin where compilation can support inline functions and other optimizations. It such languages, efficiencies depend on the compiler, not the technique itself.

Iterations vs map filter reduce, in both performance and appeal, is an argument that comes down to implementation details and personal taste.  Python has much stronger implementation of iterations in terms of both performance and appeal.  Kotlin has a map implementation with more performance than python list comprehensions,  but appeal is a personal choice.  Clearly, kotlin has better lambda than python, and that gives better map and filter, but comparing kotlin map and filter to the preferred python technique of iterations, cannot objectively produce a winner.   Just take the move from iterations in python to map and filter in kotlin with an open mind.

# first python
new_list = [map_func(it) for it in old_list]
//now kotlin
val newList = oldList.map{ mapFunc(it) }
#python for squaring list
new_list = [it * it for it in old_list]
//and kotlin
val new_list = oldList.map{ it*it }

# now version filtering out odd numbers
new_list = [it * it for it in old_list if it % 2 == 0]
//kotlin
newList = oldList.filter{ it % 2 == 0 }.map{ it*it }

With kotlin, a more complex, multi statement expression is possible without resorting to a ‘mapFunc’  (an external function to calculate the new value), but the two systems are similar.

Python added tuple and dictionary comprehension in python 2.7 in around 2012. Tuple comprehensions are basically identical to list comprehensions but with round brackets in place of square brackets.  In kotlin, map syntax is unchanged between immutableList (python list equivalent) and List (python tuple equivalent).

When the code is clearly cleaner for python is for a dictionary comprehension.  Starting with a map to produce a new map is case I have yet to find clean kotlin code for:

#python
new_dict = {k + '2': v * 2 for k, v in old_dict.items()}
// kotlin
val newMap = oldMap.toList().associateBy({it.first+"2"},{it.second*2})

There is more optimised code for mapping the values of a map, or mapping the keys, but mapping both at once, as you can see from above, is a little clumsy.  There may be a cleaner solution, but if so, I have not yet found it.

Sets

Sets are largely the same in both languages, with kotlin once again adding an immutable variant. Sets are not used as substitutes for other things, and the uses of a set are generally the same in both languages.  Common set operations:

val mySet = mutableSetOf<Int>()
mySet.add(3) // add value 3 to set

Arrays.

Lists, Maps and Sets, as well as data class objects, are all object stored as described in variables and objects page, as having a reference stored in static (or stack) memory to an object in heap/dynamic memory.  The ultimate in flexibility, but not the ultimate in performance.

Arrays are effectively an alternative to a List, but with restrictions that enable greater performance.  Arrays are fixed in size, and constructed in place. This fixed sized list, enables storage of data directly in static memory or on the stack, as opposed to the heap, at the expense of flexibility.  For Arrays of basic types, think of allocating a block of memory to hold a fixed number items of the basic type.  If there are 10 four byte integers, then 40 bytes is required, and each integer can be addressed directly with no lookup required.  For other types, the static memory will be a block of reference to each object in dynamic memory.

In addition to efficiency, Arrays also allow interoperability with Java programs which make use of these data structures.  I will return to this section, but for now, these are features which have no direct equivalence in python.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s