# Topics

• Pairs & Triples
• Lists
• immutable
• mutable

# Pairs & Triples I

• Kotlin does not have a tuple type like Python, but it has pairs and triples.

>>> var p = Pair(2, 3.5)
>>> p = Pair(1, 2)
error: type inference failed. Expected type mismatch: inferred type is Pair<Int, Int>
but Pair<Int, Double> was expected
p = Pair(1, 2)
^
>>> p = Pair(1, 2.0)
• The components of a pair or triple can be accessed as the properties first, second, and third.

>>> p.first
1
>>> p.second
2.0

# Pairs & Triples II

• Note that pairs and triples are “generic types”, e.g., Pair(1, 2.0) is of type Pair<Int, Double>. The “type parameters” are written comma-separated inside angle brackets. Type parameters indicate the components' types.
• You can also write Pair(a, b) using infix notation as a to b.
• Kotlin implements pairs and triples as data classes. Thus, they come equipped with a nice toString() method, and the == and != operators work on them in the expected way,

>>> val p1 = Pair(1, 2.3)
>>> val p2 = 1 to 2.30
>>> p1 == p2
true
>>> p1 != p2
false
>>> p1
(1, 2.3)

# Destructuring declarations

• When declaring variables, you can “destructure” a pair or triple like in Python:

>>> val (n, d) = p
>>> n
1
>>> d
2.0
• The destructuring declaration val (n, d) = p declares two variables and give them values at once. It’s equivalent to writing

>>> val n = p.first
>>> val d = p.second
• Pairs and triples are useful for returning more than one value from a function, and destructuring allows one to concisely declare multiple variables to capture the return values of a function.

# Lists

• A List is a generic type containing objects of the same type arranged in a linear sequence that can be accessed through a nonnegative index. For any type “T”, List<T> is an immutable list of elements of type T.
• E.g.,

>>> val students = listOf("Alice", "Bob", "Charlie", "David")
>>> students
[Alice, Bob, Charlie, David]
>>> students.size
4
>>> students
Alice
>>> students
David
• The type of students is List<String>—every list element has type String. It contains students.size elements that can be indexed as students[i] for all indices from 0 to students.lastIndex, that is, L.lastIndex == students.size - 1 holds.

# List iteration

• You can iterate over a list using this for-loop construct:

>>> for (s in students)
...   println(s)
Alice
Bob
Charlie
David
• If you need to know the indices of elements while iterating over the list, you can write

>>> for (i in students.indices)
...   println("$i:${students[i]}")
0: Alice
1: Bob
2: Charlie
3: David

# List properties and functions I

• Some useful properties and functions of every list L follow.
• L.size gives the length of the list;
• L.lastIndex is the greatest valid index; it equals L.size-1;
• L.first() gives the initial element of the sequence;
• L.last() gives the final element of the sequence;
• L.isEmpty() is true iff L.size == 0;
• L.isNotEmpty() is true iff L.size != 0;
• e in L (or L contains e) is true iff L contains an element equal to e;
• L.take(n) returns a new list containing the first $$n$$ elements of L;
• L.drop(n) returns a new list containing all elements of L except the first $$n$$;
• L.takeLast(n) returns a new list containing the last $$n$$ elements of L;
• L.dropLast(n) returns a new list containing all elements of L except the last $$n$$;

# List properties and functions II

• More useful functions of every lists L, L1 and L2 follow.
• L.max(), L.min(), L.sum(), and L.average() returns the largest, smallest, sum, and average of the elements of the numeric list L;
• L.reversed() returns a new list with elements of L in reverse order;
• L.sorted() and L.sortedDescending() returns a new list with elements of L in nondecreasing and nonincreasing sorted order;
• L + e returns a new list containing elements of L1 with element e appended at end;
• L1 + L2 returns a new list containing the concatenation of L1 and L2;
• L.indexOf(e) and L.lastIndexOf(e) return the first and last index whose element is equal to e, or -1 if no such index exists;

# Mutable lists

• A mutable list is a list whose sequence of elements can be modified, i.e., added, removed, or replaced. It can do all that immutable lists can, but it also supports additional functions not supported by immutable lists.
• Here are some extra functions callable on mutable lists m, m1, m2:
• m.add(e) adds element e (of the right type) to the end of m;
• m.add(i, e) adds element e (of the right type) at index i of m;
• m1.addAll(m2) adds all elements of m2 to the end of m1;
• m1.addAll(i, m2) adds all elements of m2 at index i of m1;
• m.remove(e) removes an element equal to e from m, if one exists;
• m.removeAt(i) removes an element a valid index i from m;
• m.clear() removes all elements from m;
• m.reverse() reverses the order of elements in m;
• m.sort() and m.sortDescending() sorts the elements of m in nondecreasing and nonincreasing order;
• m.shuffle() rearranges the elements of m in a random order;

# Example usage of mutable lists

• Here is an example usage of mutable lists.

>>> val m1 = mutableListOf<String>()
>>> m1
[]
>>> val m2 = mutableListOf("Alice", "Bob", "Ben", "Charlie")
>>> m2
[Alice, Bob, Ben, Charlie]
[Ben, Alice, Bob, Charlie]