Sets and Intervals of Kotlin Advancement

Keywords: Java snapshot


aggregate

Unlike most languages, Kotlin distinguishes between variable and immutable sets (list, sets, maps, etc.). Precise control over when collections can be edited helps reduce BUG and well-designed API s.

Before that, it is necessary to understand the difference between read-only variable sets and actual invariant sets. Both are easy to create, but the type system expression is different.

Kotlin's List < out T > type is an interface that provides read-only operations such as size, get, and so on. Like Java, Collection < T > inherits the Collection < T > interface and Collection < T > inherits Iterable < T >. MutableList < T > adds methods to modify arrays. Similarly, there are Set < out T >/ MutableSet < T > and Map < K, out V >/ MutableMap < K, V >

Basic usage of List and Set

val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers
println(numbers) // Output "[1, 2, 3]"
numbers.add(4)   
println(numbers) // Output "[1, 2, 3, 4]"
readOnlyView.clear() // Not compiled

val strings = hashSetOf("a", "b", "c", "c")
assert(strings.size == 3)

Kotlin does not have a specific syntax for creating lists or sets. It can be created using listOf(), mutableListOf(), setOf(), mutable Set Of (). In non-performance-critical code, you can use simple Idiomatic grammar Implementation: mapOf(a) to b, c to d).

The readOnlyView variable points to the same list and changes as the basic List changes. If the only reference points to a read-only variable List, the collection as a whole is considered immutable.

A simple way to create an immutable set:

val items = listOf(1, 2, 3)

Currently, the listOf method is implemented using an array List, but in the future it may return a completely immutable set type with higher storage efficiency.

The read-only type is Covariant quantity This means that List < Rectangle > is assigned to List < Shape > (assuming that Rectangle inherits Shape). Variable collections are not allowed because they may allow errors at runtime.

Sometimes it is desirable to return a snapshot of the collection to the caller in time to ensure that the collection remains unchanged.

class Controller {
    private val _items = mutableListOf<String>()
    val items: List<String> get() = _items.toList()
}

The toList extension function is just a copy of the List, and the returned List is guaranteed to remain unchanged.

You should also be familiar with some useful extensions of List and Set

val items = listOf(1, 2, 3, 4)
items.first() == 1
items.last() == 4
items.filter { it % 2 == 0 } // Return [2, 4]

val rwList = mutableListOf(1, 2, 3)
rwList.requireNoNulls()        // Return [1, 2, 3]
if (rwList.none { it > 6 }) println("No items above 6")  // Output "No items above 6"
val item = rwList.firstOrNull()

Similarly, there are other practical methods that may be needed, such as sorting, compression, folding, reduction, etc.

Map takes the same pattern and can be instantiated and accessed as follows.

val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)
println(readWriteMap["foo"]) // Output "1"
val snapshot: Map<String, Int> = HashMap(readWriteMap)

Section

Using rangeTo function line interval expression, you can use the operator in the form of... and use in or! In to complete. Kotlin defines an interval for any comparison type and has its own optimal implementation for the basic type of integer. Such as:

if( i in 1..10){  // Equal to 1 <= I & & I <= 10
    println(i)
}

The IntRange (Long Range, CharRange) has an additional feature: it can iterate iteratively. The compiler can turn the interval into a for loop similar to Java without additional overhead.

for ( i in 1..4) print(i) // Output "1234"
for (i in 1..4) print(i) //  No output

If you want an inverse loop, you can use the downTo function defined in the standard library

for (i in 4 downTo 1) print(i) // Output "4321"

The loop specifies the number of steps in a single loop through the step function

for(i in 1..4 step 2) print(i) // Output "13"
for(i in 4 downTo 1 step 2) print(i) // Output "42"

Using until functions to create cyclic iterations that do not include end elements

for( i in 1 until 10 ){ // I in [1, 10] does not include 10
    println(i)
}

Realization principle

A General Interface in Interval Implementation Library: ClosedRange<T>

ClosedRange < T > denotes a closed interval in mathematical sense and is defined as a comparative type. There are two endpoints: the beginning and the end, both of which are contained in the interval. The main operator is contains, using form in and!

Int Progression (Long Progression, Char Progression) represents the arithmetic queue. Sequences are defined by starting elements, ending elements and non-zero steps. The first element is the start element, the sub-queue element is the previous element plus steps, and the last element is always hit in a loop (unless the queue is empty).

Queues are listed as Iterable < N > subclasses, N denotes Int, Long, Char, so they can be used in for loops and functions like map, filter, etc. Queue Loop Approximation and Index for Loop in Java

for (int i = first; i != last; i += step) {
  // ...
}

For integers,... Operators create objects that implement ClosedRange < T > and * Progression interfaces. For example, IntRange implements ClosedRange < Int > and inherits IntProgression, so all operators defined for IntProgression are used for IntRange. The return values of downTo() and step() functions are * Progression.

Queues constructed using the fromClosedRange function are defined in their associated objects:

IntProgression.fromClosedRange(start, end, step)

The end-of-queue element is used to calculate the maximum value that does not exceed the end value when the search step is positive, or the minimum value (last-first)% Step = 0 when the step is negative.

Instrumental function

rangeTo()

The rangeTo() operator of the integer is the constructor that calls * Range

class Int{
    //...
    operator fun rangeTo(other: Long): LongRange = LongRange(this, other)
    //...
    operator fun rangeTo(other: Int): IntRange = IntRange(this, other)
    //...
}

Float (Double) does not define the rangeTo operator and uses the generic Comparable type provided by the standard library instead.

public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>

The return interval of this function cannot be used for cyclic iteration.

downTo()

The downTo() extension function defined for integers, such as:

fun Long.downTo(other: Int): LongProgression {
    return LongProgression.fromClosedRange(this, other.toLong(), -1L)
}

fun Byte.downTo(other: Int): IntProgression {
    return IntProgression.fromClosedRange(this.toInt(), other, -1)
}

reversed()

Define the reversed() extension function for each * Progression class, returning the reversed sequence

fun IntProgression.reversed(): IntProgression {
    return IntProgression.fromClosedRange(last, first, -step)
}

step()

Define the step() extension function for * Progression, and return the filtered sequence using the step value. The step value is always positive, so this does not change the direction of the loop.

fun IntProgression.step(step: Int): IntProgression {
    if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
    return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}

fun CharProgression.step(step: Int): CharProgression {
    if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
    return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}
The end value of the sequence returned by the function may be different from the end value of the original queue, guaranteeing (last - first)% step = 0.
(1..12 step 2).last == 11  // Queues [1, 3, 5, 7, 9, 11]
(1..12 step 3).last == 10  // Queues [1, 4, 7, 10]
(1..12 step 4).last == 9   // Queues [1, 5, 9]

Posted by Cerberus_26 on Wed, 22 May 2019 12:42:47 -0700