├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── src ├── main │ └── kotlin │ │ ├── util │ │ ├── CharUtil.kt │ │ ├── DoubleUtil.kt │ │ ├── SetUtil.kt │ │ ├── MapUtil.kt │ │ ├── io │ │ │ ├── PrintWhenPropertyIsAccessedExample.kt │ │ │ ├── SimulatedStandardInput.kt │ │ │ ├── FastScanner.kt │ │ │ ├── StandardInputUtil.kt │ │ │ ├── BufferedReaderWrapper.kt │ │ │ └── StandardOutputUtil.kt │ │ ├── UrlUtil.kt │ │ ├── FlowUtil.kt │ │ ├── IterableUtil.kt │ │ ├── text │ │ │ └── JapaneseUtil.kt │ │ ├── datetime │ │ │ ├── LegacyJavaDateUtil.kt │ │ │ ├── TimeUtil.kt │ │ │ ├── LocalDateUtil.kt │ │ │ └── LocalDateTimeUtil.kt │ │ ├── TimerExample.kt │ │ ├── CollectionUtil.kt │ │ ├── NormalDistributionUtil.kt │ │ ├── DecibinaryUtil.kt │ │ ├── YearMonthUtil.kt │ │ ├── TimerUtil.kt │ │ ├── FileUtil.kt │ │ ├── ListUtil.kt │ │ ├── StringUtil.kt │ │ ├── NumberUtil.kt │ │ ├── Array2dUtil.kt │ │ ├── ArrayUtil.kt │ │ ├── RxUtil.kt │ │ └── MathUtil.kt │ │ ├── enum │ │ ├── Fruit.kt │ │ ├── ExtendedFruit.kt │ │ ├── Animal.kt │ │ └── MyColor.kt │ │ ├── Main.kt │ │ ├── sort │ │ ├── CountingSort.kt │ │ ├── InsertionSort.kt │ │ ├── QuicksortRecursive.kt │ │ ├── SelectionSort.kt │ │ ├── BubbleSort.kt │ │ ├── Quicksort.kt │ │ ├── MergeSort.kt │ │ ├── RadixSort.kt │ │ └── Heapsort.kt │ │ ├── example │ │ ├── RegexExample.kt │ │ ├── DeprecationExample.kt │ │ ├── MySealedInterface.kt │ │ └── MySealedClass.kt │ │ ├── converter │ │ └── Compressor.kt │ │ ├── graph │ │ ├── DfsSample.kt │ │ └── BfsSample.kt │ │ ├── rx │ │ ├── example │ │ │ ├── MaybeExample.kt │ │ │ ├── SingleExample.kt │ │ │ ├── CompletableExample.kt │ │ │ ├── ObservableExample.kt │ │ │ └── SubjectExample.kt │ │ └── kotlinobservable │ │ │ └── KotlinObservableExample.kt │ │ ├── json │ │ ├── MySerializable.kt │ │ ├── kotlinserialization │ │ │ ├── datetime │ │ │ │ ├── DateTimeSerializationExample.kt │ │ │ │ ├── LocalDateTimeSerializer.kt │ │ │ │ └── NullableLocalDateTimeSerializer.kt │ │ │ └── KotlinSerializationExample.kt │ │ └── MoshiConverter.kt │ │ ├── data │ │ ├── Queue.kt │ │ ├── ArrayDequeStack.kt │ │ ├── JavaStack.kt │ │ ├── DisjointSets.kt │ │ └── TreeUtil.kt │ │ ├── flow │ │ ├── FlowRetryExample.kt │ │ ├── FlowExtensions.kt │ │ ├── MutableStateFlowExample.kt │ │ ├── MutableSharedFlowExample.kt │ │ ├── FlowExample.kt │ │ ├── WhyYouShouldUseLaunchIn.kt │ │ ├── CallbackFlowExample.kt │ │ └── IntervalFlowExample.kt │ │ ├── coroutine │ │ ├── LaunchExample.kt │ │ ├── CancellationExceptionExample.kt │ │ ├── AsyncAwaitExample.kt │ │ ├── coRunCatching.kt │ │ └── SafeLaunch.kt │ │ └── uistate │ │ └── UiState.kt └── test │ └── kotlin │ ├── PerMethodTestInstanceDemo.kt │ ├── MyKotlinTest.kt │ ├── MyReflectionTest.kt │ └── coroutines │ └── VirtualTimeTest.kt ├── data ├── comprehensive-array.json └── comprehensive-object.json ├── markdown ├── scope-functions.md ├── rxjava.md ├── misc.md ├── best-practices.md └── coroutines-flow.md ├── gradlew.bat ├── gradlew └── README.md /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tatsuyafujisaki/kotlin-playground/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/kotlin/util/CharUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | object CharUtil { 4 | fun getAlphabeticalIndex(c: Char) = c - 'a' 5 | } 6 | -------------------------------------------------------------------------------- /src/main/kotlin/enum/Fruit.kt: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | enum class Fruit(val emoji: String) { 4 | APPLE("🍎"), 5 | ORANGE("🍊"); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.coroutineScope 2 | 3 | private suspend fun main() = coroutineScope { 4 | println(KotlinVersion.CURRENT) 5 | } 6 | -------------------------------------------------------------------------------- /src/main/kotlin/enum/ExtendedFruit.kt: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | enum class ExtendedFruit(emoji: String) { 4 | APPLE(Fruit.APPLE.emoji), 5 | ORANGE(Fruit.ORANGE.emoji), 6 | BANANA("🍌"); 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/util/DoubleUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | object DoubleUtil { 4 | private fun getBigInteger(x: Double) = x.toBigDecimal().toBigInteger() 5 | private fun roundFormat(x: Double, decimals: Int) = "%.${decimals}f".format(x) 6 | } 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/kotlin/util/SetUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | object SetUtil { 4 | fun addOrRemove1(set: Set, element: E) = if (element in set) { 5 | set - element 6 | } else { 7 | set + element 8 | } 9 | 10 | fun addOrRemove2(set: Set, element: E) = set.toMutableSet().apply { 11 | add(element) || remove(element) 12 | }.toSet() 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/CountingSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object CountingSort { 4 | fun sort(xs: IntArray, max: Int = xs.maxOrNull()!!) { 5 | val counts = IntArray(max + 1) 6 | for (x in xs) counts[x]++ 7 | var i = 0 8 | counts.forEachIndexed { j, x -> 9 | repeat(x) { 10 | xs[i++] = j 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/InsertionSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object InsertionSort { 4 | fun sort(xs: IntArray) { 5 | for (i in 1 until xs.size) { 6 | val x = xs[i] 7 | var j = i - 1 8 | while (0 <= j && x < xs[j]) { 9 | xs[j + 1] = xs[j] 10 | j-- 11 | } 12 | xs[j + 1] = x 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/QuicksortRecursive.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object QuicksortRecursive { 4 | fun sort(xs: IntArray): IntArray { 5 | if (xs.isEmpty()) return xs 6 | val pivot = xs[xs.size / 2] 7 | return sort(xs.filter { it < pivot }.toIntArray()) + 8 | xs.filter { it == pivot } + 9 | sort(xs.filter { it > pivot }.toIntArray()) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/kotlin/example/RegexExample.kt: -------------------------------------------------------------------------------- 1 | private fun main() { 2 | val s = "🍏🍊🍏" 3 | 4 | // Replaces all 🍏 with 🍎. 5 | println(s.replace("🍏", "🍎")) // 🍎🍊🍎 6 | 7 | // Replaces the first character with 🍎 if it is 🍏. 8 | println(s.replace("^🍏".toRegex(), "🍎")) // 🍎🍊🍏 9 | 10 | // Replaces the last character wth 🍎 if it is 🍏. 11 | println(s.replace("🍏$".toRegex(), "🍎")) // 🍏🍊🍎 12 | } -------------------------------------------------------------------------------- /src/main/kotlin/util/MapUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | object MapUtil { 4 | fun > sortedByValue(map: Map) = map.toList().sortedBy { it.second }.toMap() 5 | 6 | fun > sortedByValueDescending(map: Map) = 7 | map.toList().sortedByDescending { it.second }.toMap() 8 | 9 | fun > getEntryOfMaxValue(map: Map) = map.maxByOrNull { it.value } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/converter/Compressor.kt: -------------------------------------------------------------------------------- 1 | package converter 2 | 3 | import java.util.SortedSet 4 | 5 | class Compressor>(set: SortedSet) { 6 | private val compressed = set.mapIndexed { i, x -> x to i }.toMap() 7 | private val decompressed = set.mapIndexed { i, x -> i to x }.toMap() 8 | fun getCompressedIndex(x: T) = compressed[x]!! 9 | fun getItem(compressedIndex: Int) = decompressed[compressedIndex]!! 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/util/io/PrintWhenPropertyIsAccessedExample.kt: -------------------------------------------------------------------------------- 1 | package util.io 2 | 3 | class PrintWhenPropertyIsAccessedExample { 4 | var x = "" 5 | get() { 6 | println("${object {}::class.java.enclosingMethod?.name}: $field") 7 | return field 8 | } 9 | set(value) { 10 | println("${object {}::class.java.enclosingMethod?.name}: $value") 11 | field = value 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/enum/Animal.kt: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | enum class Animal(emoji: String) { 4 | Cat(emoji = "🐈") { 5 | override val face = "🐱" 6 | override fun cry() { 7 | println("Meow!") 8 | } 9 | }, 10 | Dog(emoji = "🐕") { 11 | override val face = "🐶" 12 | override fun cry() { 13 | println("Bark!") 14 | } 15 | }; 16 | 17 | abstract val face: String 18 | abstract fun cry() 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/util/UrlUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.net.URLEncoder 4 | 5 | object UrlUtil { 6 | /** 7 | * Instead, use Uri.encode in Android for simplicity. 8 | * https://developer.android.com/reference/kotlin/android/net/Uri#encode 9 | */ 10 | private fun encode(url: String) = URLEncoder.encode(url, Charsets.UTF_8.name()) 11 | 12 | fun getNthLevelDomain(domain: String, level: Int) = domain.split(".").takeLast(level).joinToString(".") 13 | } 14 | -------------------------------------------------------------------------------- /src/main/kotlin/util/io/SimulatedStandardInput.kt: -------------------------------------------------------------------------------- 1 | package util.io 2 | 3 | import java.io.File 4 | 5 | // Usage of SimulatedStandardInput 6 | fun main() { 7 | val stdin = SimulatedStandardInput("input.txt") 8 | fun readLine() = stdin.readLine() 9 | } 10 | 11 | /** @param pathname a sibling of the src folder */ 12 | class SimulatedStandardInput(pathname: String) { 13 | private var lines = File(pathname).readLines() 14 | private var i = 0 15 | fun readLine() = lines[i++] 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/graph/DfsSample.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | fun dfs(undirectedGraph: List>, startVertex: Int) { 4 | val visited = BooleanArray(undirectedGraph.size) 5 | 6 | fun dfs(vertex: Int) { 7 | visited[vertex] = true 8 | undirectedGraph[vertex] 9 | .filterNot { 10 | visited[it] 11 | } 12 | .forEach { 13 | dfs(it) 14 | } 15 | } 16 | 17 | dfs(startVertex) 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object SelectionSort { 4 | fun sort(xs: IntArray) { 5 | for (i in xs.indices) { 6 | var indexOfMin = i 7 | for (j in i + 1 until xs.size) { 8 | if (xs[j] < xs[indexOfMin]) indexOfMin = j 9 | } 10 | if (i != indexOfMin) xs.swap(i, indexOfMin) 11 | } 12 | } 13 | 14 | private fun IntArray.swap(i: Int, j: Int) { 15 | val temp = this[i] 16 | this[i] = this[j] 17 | this[j] = temp 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/example/MaybeExample.kt: -------------------------------------------------------------------------------- 1 | package rx.example 2 | 3 | import io.reactivex.rxjava3.core.Maybe 4 | import util.RxUtil.DoOn.print 5 | import util.RxUtil.SubscribeUtil.mySubscribe 6 | 7 | object MaybeExample { 8 | fun example1() { 9 | val maybe = Maybe.just("a") 10 | val disposable = maybe.mySubscribe() 11 | disposable.print() // true 12 | } 13 | 14 | fun example2() { 15 | val maybe = Maybe.error(Throwable("WTF")) 16 | val disposable = maybe.mySubscribe() 17 | disposable.print() // true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/comprehensive-array.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "null1": null, 4 | "boolean1": true, 5 | "boolean2": false, 6 | "number1": -1, 7 | "number2": 3.14, 8 | "string1": "", 9 | "string2": "a", 10 | "array1": [], 11 | "array2": [ 12 | -1, 13 | 3.14 14 | ] 15 | }, 16 | { 17 | "null1": null, 18 | "boolean1": true, 19 | "boolean2": false, 20 | "number1": -1, 21 | "number2": 3.14, 22 | "string1": "", 23 | "string2": "a", 24 | "array1": [], 25 | "array2": [ 26 | -1, 27 | 3.14 28 | ] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/example/SingleExample.kt: -------------------------------------------------------------------------------- 1 | package rx.example 2 | 3 | import io.reactivex.rxjava3.core.Single 4 | import util.RxUtil.DoOn.print 5 | import util.RxUtil.SubscribeUtil.mySubscribe 6 | 7 | object SingleExample { 8 | fun example1() { 9 | val single = Single.just("a") 10 | val disposable = single.mySubscribe() 11 | disposable.print() // true 12 | } 13 | 14 | fun example2() { 15 | val single = Single.error(Throwable("WTF")) 16 | val disposable = single.mySubscribe() 17 | disposable.print() // true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /data/comprehensive-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "object1": { 3 | "null1": null, 4 | "boolean1": true, 5 | "boolean2": false, 6 | "number1": -1, 7 | "number2": 3.14, 8 | "string1": "", 9 | "string2": "a", 10 | "array1": [], 11 | "array2": [ 12 | -1, 13 | 3.14 14 | ] 15 | }, 16 | "object2": { 17 | "null1": null, 18 | "boolean1": true, 19 | "boolean2": false, 20 | "number1": -1, 21 | "number2": 3.14, 22 | "string1": "", 23 | "string2": "a", 24 | "array1": [], 25 | "array2": [ 26 | -1, 27 | 3.14 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/kotlin/PerMethodTestInstanceDemo.kt: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test 2 | import kotlin.test.assertFalse 3 | import kotlin.test.assertTrue 4 | 5 | class PerMethodTestInstanceDemo { 6 | private var b = false 7 | 8 | @Test 9 | fun assertTrueTest1() { 10 | b = true 11 | assertTrue(b) 12 | } 13 | 14 | @Test 15 | fun assertFalseTest2() { 16 | // Although b is set to true in assertTrueTest1(), 17 | // b is false here because a new instance of the test class is created before each test method is executed. 18 | assertFalse(b) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/json/MySerializable.kt: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class MySerializable( 7 | val object1: MySerializable2, val object2: MySerializable2, 8 | ) 9 | 10 | @Serializable 11 | data class MySerializable2( 12 | val null1: String?, 13 | val boolean1: Boolean?, 14 | val boolean2: Boolean?, 15 | val number1: Double?, 16 | val number2: Double?, 17 | val string1: String?, 18 | val string2: String?, 19 | val array1: List?, 20 | val array2: List?, 21 | ) 22 | -------------------------------------------------------------------------------- /src/main/kotlin/util/FlowUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.onCompletion 5 | import kotlinx.coroutines.flow.onEach 6 | import kotlinx.coroutines.flow.onEmpty 7 | import kotlinx.coroutines.flow.onStart 8 | 9 | object FlowUtil { 10 | fun Flow.onMisc() = onStart { 11 | println("👀onStart") 12 | }.onEach { 13 | println("👀onEach: $it") 14 | }.onEmpty { 15 | println("👀onEmpty") // completes without emitting any elements. 16 | }.onCompletion { 17 | println("👀 onCompletion: $it") 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/data/Queue.kt: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import java.util.Stack 4 | 5 | class Queue { 6 | private val inbox = Stack() 7 | private val outbox = Stack() 8 | 9 | fun enqueue(item: T) { 10 | inbox.push(item) 11 | } 12 | 13 | fun dequeue(): T { 14 | prepare() 15 | return outbox.pop() 16 | } 17 | 18 | fun peek(): T { 19 | prepare() 20 | return outbox.peek() 21 | } 22 | 23 | private fun prepare() { 24 | if (outbox.isEmpty()) { 25 | while (!inbox.isEmpty()) outbox.push(inbox.pop()) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/example/CompletableExample.kt: -------------------------------------------------------------------------------- 1 | package rx.example 2 | 3 | import io.reactivex.rxjava3.core.Completable 4 | import util.RxUtil.DoOn.print 5 | import util.RxUtil.SubscribeUtil.mySubscribe 6 | 7 | object CompletableExample { 8 | fun example1() { 9 | val completable = Completable.complete() 10 | val disposable = completable.mySubscribe() 11 | disposable.print() // true 12 | } 13 | 14 | fun example2() { 15 | val completable = Completable.error(Throwable("WTF")) 16 | val disposable = completable.mySubscribe() 17 | disposable.print() // true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/BubbleSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object BubbleSort { 4 | fun sort(xs: IntArray) { 5 | var n = xs.size 6 | while (1 < n) { 7 | var lastSwappedIndex = 0 8 | repeat(n - 1) { 9 | if (xs[it] > xs[it + 1]) { 10 | xs.swap(it, it + 1) 11 | lastSwappedIndex = it + 1 12 | } 13 | } 14 | n = lastSwappedIndex 15 | } 16 | } 17 | 18 | private fun IntArray.swap(i: Int, j: Int) { 19 | val temp = this[i] 20 | this[i] = this[j] 21 | this[j] = temp 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/example/DeprecationExample.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | object DeprecationExample { 4 | @Deprecated( 5 | message = "Use myNewFunction(String) instead.", 6 | // The deprecated declaration is replaced by the following expression when saving a file 7 | // if Settings > "Actions on Save" > "Run code cleanup" is enabled. 8 | replaceWith = ReplaceWith(expression = "myNewFunction(String)"), 9 | ) 10 | fun myOldFunction(s: String) { 11 | println(s) 12 | } 13 | 14 | fun myNewFunction(s: String) { 15 | println(s) 16 | } 17 | } 18 | 19 | // private fun main() { 20 | // myOldFunction("Hello") 21 | // } 22 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/FlowRetryExample.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.flow.catch 5 | import kotlinx.coroutines.flow.flow 6 | import kotlinx.coroutines.flow.retry 7 | 8 | private suspend fun main() = coroutineScope { 9 | var retry = 0 10 | val maxRetryCount = 3L 11 | 12 | flow { 13 | if (retry < maxRetryCount) { 14 | retry++ 15 | throw Exception() 16 | } 17 | emit("a") 18 | }.retry(maxRetryCount) { 19 | println("retry! $it") 20 | true 21 | }.catch { 22 | println("catch! $it") 23 | }.collect { 24 | println("collect! $it") 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/flow/FlowExtensions.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | import kotlinx.coroutines.flow.flow 5 | 6 | /** 7 | * @see throttleFirst 8 | */ 9 | fun Flow.throttleFirst(windowDuration: Long, getCurrentTimeMillis: (() -> Long)? = null): Flow = flow { 10 | var lastEmissionTime = 0L 11 | collect { 12 | val currentTime = getCurrentTimeMillis?.invoke() ?: System.currentTimeMillis() 13 | if (windowDuration < (currentTime - lastEmissionTime)) { 14 | lastEmissionTime = currentTime 15 | emit(it) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /markdown/scope-functions.md: -------------------------------------------------------------------------------- 1 | # Scope functions 2 | 3 | A receiver is an object that invokes the method. 4 | 5 | Function | How to reference receiver | Can receiver be nullable? | Return value 6 | ----------|---------------------------|---------------------------|------------------- 7 | `apply` | `this` (optional) | yes | receiver (`this`) 8 | `also` | `it` | yes | receiver (`it`) 9 | `run` | `this` (optional) | yes | last expression 10 | `with` | `this` (optional) | no | last expression 11 | `let` | `it` | yes | last expression 12 | -------------------------------------------------------------------------------- /src/main/kotlin/data/ArrayDequeStack.kt: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | private class ArrayDequeStack { 4 | private val arrayDeque = ArrayDeque() 5 | 6 | fun push(element: T) { 7 | arrayDeque.addFirst(element) 8 | } 9 | 10 | fun peek() = arrayDeque.first() 11 | fun pop() = arrayDeque.removeFirst() 12 | fun peekOrNull() = arrayDeque.firstOrNull() 13 | fun popOrNull() = arrayDeque.removeFirstOrNull() 14 | } 15 | 16 | private fun main() { 17 | val stack = ArrayDequeStack() 18 | stack.push("a") 19 | stack.push("b") 20 | 21 | println(stack.peek()) // b 22 | println(stack.pop()) // b 23 | println(stack.pop()) // a 24 | println(stack.peekOrNull()) // null 25 | println(stack.popOrNull()) // null 26 | } 27 | -------------------------------------------------------------------------------- /src/main/kotlin/data/JavaStack.kt: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import java.util.Stack 4 | 5 | private class JavaStack { 6 | private val stack = Stack() 7 | 8 | fun push(item: T) = stack.push(item) 9 | fun peek() = stack.peek() // throws EmptyStackException if the stack is empty. 10 | fun pop() = stack.pop() 11 | fun peekOrNull() = stack.lastOrNull() 12 | fun popOrNull() = if (stack.empty()) null else stack.pop() // avoids an EmptyStackException. 13 | } 14 | 15 | private fun main() { 16 | val stack = JavaStack() 17 | stack.push("a") 18 | stack.push("b") 19 | 20 | println(stack.peek()) // b 21 | println(stack.pop()) // b 22 | println(stack.pop()) // a 23 | println(stack.peekOrNull()) // null 24 | println(stack.popOrNull()) // null 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/LaunchExample.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.CancellationException 4 | import kotlinx.coroutines.cancelAndJoin 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.launch 8 | 9 | private suspend fun main() = coroutineScope { 10 | val job = launch { 11 | try { 12 | println("try started!") 13 | delay(timeMillis = 100) // waits to avoid completing before being cancelled. 14 | println("try ended!") 15 | } catch (e: CancellationException) { 16 | println("catch started!") 17 | println(e) 18 | println("catch ended!") 19 | throw e 20 | } 21 | } 22 | job.cancelAndJoin() 23 | println("Done!") 24 | } 25 | -------------------------------------------------------------------------------- /src/main/kotlin/json/kotlinserialization/datetime/DateTimeSerializationExample.kt: -------------------------------------------------------------------------------- 1 | package json.kotlinserialization.datetime 2 | 3 | import kotlinx.serialization.Serializable 4 | import kotlinx.serialization.json.Json 5 | import java.time.LocalDateTime 6 | 7 | @Serializable 8 | private data class MySerializable( 9 | @Serializable(with = LocalDateTimeSerializer::class) val dateTime1: LocalDateTime, 10 | @Serializable(with = NullableLocalDateTimeSerializer::class) val dateTime2: LocalDateTime?, 11 | ) 12 | 13 | private fun main() { 14 | val json = """ 15 | { 16 | "dateTime1": "2025-01-02 12:34:56", 17 | "dateTime2": "" 18 | } 19 | """.trimIndent() 20 | 21 | val serializable = Json.decodeFromString(string = json) 22 | println(serializable) 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/util/IterableUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | object IterableUtil { 4 | fun tail(xs: Iterable) = xs.drop(1) 5 | fun addOrRemove(xs: Iterable, x: T) = if (xs.any { it == x }) xs.minus(x) else xs.plus(x) 6 | fun hasDuplicates(xs: Iterable) = xs.groupingBy { it }.eachCount().any { it.value > 1 } 7 | fun toZeroBased(xs: Iterable) = xs.map { it - 1 } 8 | fun prefixSum(xs: Iterable) = xs.scan(0) { acc, x -> acc + x } 9 | 10 | /** 11 | * Refer to [ListUtil.rotateRight] for rotating right. 12 | * 13 | * @param distance must be less than or equal to the length of the list. Otherwise, the output list will be the same as the input list. 14 | */ 15 | fun rotateLeft(xs: Iterable, distance: Int): List = xs.drop(distance) + xs.take(distance) 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/util/text/JapaneseUtil.kt: -------------------------------------------------------------------------------- 1 | package util.text 2 | 3 | object JapaneseUtil { 4 | private fun japaneseNumeral(n: Int, isOnesPlace: Boolean) = when (n) { 5 | 1 -> if (isOnesPlace) "一" else "" 6 | 2 -> "二" 7 | 3 -> "三" 8 | 4 -> "四" 9 | 5 -> "五" 10 | 6 -> "六" 11 | 7 -> "七" 12 | 8 -> "八" 13 | 9 -> "九" 14 | else -> "" 15 | } 16 | 17 | private fun japaneseNumerals(n: Int): String { 18 | val largeNumbers = listOf("", "十", "百", "千") 19 | val s = n.toString() 20 | var result = "" 21 | 22 | for (i in s.indices) { 23 | result += japaneseNumeral(s[i].digitToInt(), i == s.lastIndex) 24 | if (s[i] != '0') result += largeNumbers[s.lastIndex - i] 25 | } 26 | 27 | return result 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/MutableStateFlowExample.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.flow.Flow 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.asSharedFlow 7 | import kotlinx.coroutines.launch 8 | 9 | private suspend fun main() = coroutineScope { 10 | val mutableFlow = MutableStateFlow("a") 11 | val flow: Flow = mutableFlow.asSharedFlow() 12 | 13 | launch { 14 | flow.collect { 15 | println("Collect: $it") 16 | } 17 | } 18 | 19 | // No need to get delayed to ensure that a collector starts 20 | // because the collector can collect a value emitted before the collector starts. 21 | mutableFlow.emit("a") 22 | mutableFlow.emit("a") // MutableStateFlow CANNOT collect the same value as the previous one. 23 | } 24 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/kotlinobservable/KotlinObservableExample.kt: -------------------------------------------------------------------------------- 1 | package rx.kotlinobservable 2 | 3 | import io.reactivex.rxjava3.subjects.PublishSubject 4 | import io.reactivex.rxjava3.subjects.Subject 5 | import kotlin.properties.Delegates 6 | 7 | object KotlinObservableExample { 8 | private val mySubject: Subject = PublishSubject.create() 9 | private val myObservable = mySubject.hide().distinctUntilChanged() 10 | 11 | var s by Delegates.observable("") { _, _, new -> 12 | mySubject.onNext(new) 13 | } 14 | } 15 | 16 | object KotlinObservableExample2 { 17 | private val mySubject: Subject = PublishSubject.create() 18 | private val myObservable = mySubject.hide().distinctUntilChanged() 19 | 20 | var s = "" 21 | set(value) { 22 | field = value 23 | mySubject.onNext(value) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/util/datetime/LegacyJavaDateUtil.kt: -------------------------------------------------------------------------------- 1 | package util.datetime 2 | 3 | import java.time.LocalDateTime 4 | import java.time.ZoneOffset 5 | import java.util.Calendar 6 | import java.util.Date 7 | import kotlin.time.Duration.Companion.minutes 8 | 9 | object LegacyJavaDateUtil { 10 | fun create(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int): Date = Calendar.getInstance().apply { 11 | set(year, month - 1, day, hour, minute, second) 12 | }.time 13 | 14 | fun create(localDateTime: LocalDateTime): Date = Date.from(localDateTime.atZone(ZoneOffset.systemDefault()).toInstant()) 15 | fun isLessThanMinutesOld(pastDate: Date, minutes: Long) = pastDate.after(Date(System.currentTimeMillis() - minutes.minutes.inWholeMilliseconds)) 16 | } 17 | 18 | private fun main() { 19 | println(LegacyJavaDateUtil.create(2025, 1, 2, 3, 4, 5)) 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/CancellationExceptionExample.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.cancelAndJoin 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.launch 7 | import kotlin.coroutines.cancellation.CancellationException 8 | 9 | /** 10 | * https://stackoverflow.com/a/78683217/10867055 11 | */ 12 | private suspend fun main() = coroutineScope { 13 | val job = launch { 14 | repeat(5) { i -> 15 | try { 16 | println("job: I'm sleeping $i ...") 17 | delay(100) 18 | } catch (e: CancellationException) { 19 | println(e) 20 | throw e 21 | } 22 | } 23 | } 24 | delay(150) 25 | println("main: I'm tired of waiting!") 26 | job.cancelAndJoin() 27 | println("main: Now I can quit.") 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/example/MySealedInterface.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | sealed interface MySealedInterface { 4 | val myDynamicallyDefinedProperty: String 5 | } 6 | 7 | private data class MyDataClass1( 8 | override val myDynamicallyDefinedProperty: String, 9 | val property1: String, 10 | ) : MySealedInterface 11 | 12 | private data class MyDataClass2( 13 | override val myDynamicallyDefinedProperty: String, 14 | val property2: String, 15 | ) : MySealedInterface 16 | 17 | fun main() { 18 | val myDataClass1 = MyDataClass1( 19 | myDynamicallyDefinedProperty = "Dynamically defined value for MyDataClass1", 20 | property1 = "Property1!", 21 | ) 22 | 23 | val myDataClass2 = MyDataClass2( 24 | myDynamicallyDefinedProperty = "Dynamically defined value for MyDataClass2", 25 | property2 = "Property2!", 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/MutableSharedFlowExample.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.asSharedFlow 8 | import kotlinx.coroutines.launch 9 | 10 | private suspend fun main() = coroutineScope { 11 | val mutableFlow = MutableSharedFlow() 12 | val flow: Flow = mutableFlow.asSharedFlow() 13 | 14 | launch { 15 | flow.collect { 16 | println("Collect: $it") 17 | } 18 | } 19 | 20 | // Purposefully get delayed to ensure that a collector starts 21 | // because the collector cannot collect a value emitted before the collector starts. 22 | delay(1000) 23 | mutableFlow.emit("a") 24 | mutableFlow.emit("a") // MutableSharedFlow can collect the same value as the previous one. 25 | } 26 | -------------------------------------------------------------------------------- /markdown/rxjava.md: -------------------------------------------------------------------------------- 1 | # Observable 2 | 3 | is automatically disposed when `Subscriber#OnComplete()` or `Subscriber#onError(Throwable)` is called. 4 | 5 | # Observable#onErrorResumeNext 6 | 7 | * Immediately after `Observable#onErrorResumeNext()` is called, `Observer.onNext()` and `Observer.onComplete()` will be 8 | called instead of `Observer.onError()`. 9 | * When `Observable.onErrorResumeNext()` is inside `Observable.flatMap`, the outer `Observable` will continue as if no 10 | error occurred. 11 | 12 | # Observer#onComplete() 13 | 14 | Afterwards, `Observer#onNext()` will never be called. 15 | 16 | # Observer#onError(Throwable) 17 | 18 | Afterwards, `Observer#onNext()` and `Observer.onComplete()` will never be called. 19 | 20 | # Subscribe and dispose 21 | 22 | Even after a subscription's `Disposable` is disposed (e.g. by `Observer.onError()`), you can subscribe to the 23 | same `Observable` and it works as if no error occurred before. 24 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/Quicksort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object Quicksort { 4 | fun sort(xs: IntArray) { 5 | sort(xs, 0, xs.lastIndex) 6 | } 7 | 8 | private fun sort(xs: IntArray, low: Int, high: Int) { 9 | if (low < high) { 10 | val p = partition(xs, low, high) 11 | sort(xs, low, p - 1) 12 | sort(xs, p, high) 13 | } 14 | } 15 | 16 | private fun partition(xs: IntArray, low: Int, high: Int): Int { 17 | var i = low 18 | var j = high 19 | val pivot = xs[(i + j) / 2] 20 | while (true) { 21 | while (xs[i] < pivot) i++ 22 | while (pivot < xs[j]) j-- 23 | if (j < i) break 24 | xs.swap(i++, j--) 25 | } 26 | return i 27 | } 28 | 29 | private fun IntArray.swap(i: Int, j: Int) { 30 | val temp = this[i] 31 | this[i] = this[j] 32 | this[j] = temp 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/uistate/UiState.kt: -------------------------------------------------------------------------------- 1 | package uistate 2 | 3 | /** 4 | * https://kotlinlang.org/docs/sealed-classes.html#state-management-in-ui-applications 5 | * https://developer.android.com/codelabs/basic-android-kotlin-compose-getting-data-internet#6 6 | */ 7 | private sealed interface UiState { 8 | data object Loading : UiState 9 | data class Success(val data: T) : UiState 10 | data class Failure(val throwable: Throwable) : UiState 11 | } 12 | 13 | private fun printState(state: UiState) { 14 | when (state) { 15 | UiState.Loading -> println("Loading") 16 | is UiState.Success -> println("Success: ${state.data}") 17 | is UiState.Failure -> println("Failure: ${state.throwable}") 18 | } 19 | } 20 | 21 | private fun main() { 22 | printState(UiState.Loading) 23 | printState(UiState.Success("Success!")) 24 | printState(UiState.Failure(Throwable("Failure!"))) 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/json/kotlinserialization/datetime/LocalDateTimeSerializer.kt: -------------------------------------------------------------------------------- 1 | package json.kotlinserialization.datetime 2 | 3 | import kotlinx.serialization.ExperimentalSerializationApi 4 | import kotlinx.serialization.KSerializer 5 | import kotlinx.serialization.Serializer 6 | import kotlinx.serialization.encoding.Decoder 7 | import kotlinx.serialization.encoding.Encoder 8 | import java.time.LocalDateTime 9 | import java.time.format.DateTimeFormatter 10 | 11 | @OptIn(ExperimentalSerializationApi::class) 12 | @Serializer(forClass = LocalDateTime::class) 13 | object LocalDateTimeSerializer : KSerializer { 14 | private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 15 | 16 | override fun serialize(encoder: Encoder, value: LocalDateTime) { 17 | encoder.encodeString(value.format(formatter)) 18 | } 19 | 20 | override fun deserialize(decoder: Decoder): LocalDateTime = LocalDateTime.parse(decoder.decodeString(), formatter) 21 | } 22 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | coroutines = "1.10.2" # https://github.com/Kotlin/kotlinx.coroutines/releases 3 | kotlinx-serialization = "1.8.1" # https://github.com/Kotlin/kotlinx.serialization/releases 4 | legacy-moshi = "1.15.2" # https://github.com/square/moshi/blob/master/CHANGELOG.md 5 | legacy-rxjava = "3.1.10" # https://github.com/ReactiveX/RxJava/releases 6 | 7 | [libraries] 8 | coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } 9 | coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" } 10 | kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" } 11 | legacy-moshi-kotlin = { group = "com.squareup.moshi", name = "moshi-kotlin", version.ref = "legacy-moshi" } 12 | legacy-rxjava = { group = "io.reactivex.rxjava3", name = "rxjava", version.ref = "legacy-rxjava" } 13 | -------------------------------------------------------------------------------- /src/main/kotlin/util/TimerExample.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.time.LocalTime 4 | import java.util.Timer 5 | import kotlin.concurrent.schedule 6 | 7 | private fun main() { 8 | val timer = Timer() 9 | val timerTask1 = timer.schedule( 10 | delay = 1_000, 11 | period = 1_000 12 | ) { 13 | println("🍎" + LocalTime.now()) 14 | } 15 | val timerTask2 = timer.schedule( 16 | delay = 1_000, 17 | period = 1_000 18 | ) { 19 | println("🍊" + LocalTime.now()) 20 | } 21 | timerTask1.cancel() // only cancels timerTask1, while timerTask2 continues to work. 22 | timerTask2.cancel() // only cancels timerTask2, while timerTask1 continues to work. 23 | 24 | // cancels all the TimerTask(s). 25 | // After that, if you try to call `schedule()` on this canceled timer, 26 | // the "IllegalStateException: Timer already cancelled." error will be thrown. 27 | timer.cancel() 28 | 29 | println("Done!") 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/util/CollectionUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import util.CollectionUtil.isNeitherNullNorEmpty 4 | 5 | object CollectionUtil { 6 | fun Collection?.isNeitherNullNorEmpty() = !isNullOrEmpty() 7 | 8 | fun Collection.permute(): List> { 9 | fun Collection.permute(result: List = emptyList()): List> = if (isEmpty()) { 10 | listOf(result) 11 | } else { 12 | flatMap { 13 | (this - it).permute(result + it) 14 | } 15 | } 16 | return permute() 17 | } 18 | 19 | fun Collection.permuteWithoutDuplicates() = permute().distinct() 20 | } 21 | 22 | private fun main() { 23 | val xs: List? = listOf("a") 24 | val ys: List? = emptyList() 25 | val zs: List? = null 26 | 27 | println(xs.isNeitherNullNorEmpty()) // true 28 | println(ys.isNeitherNullNorEmpty()) // false 29 | println(zs.isNeitherNullNorEmpty()) // false 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/enum/MyColor.kt: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | enum class MyColor(val value: Int) { 4 | BLACK(0x000000), WHITE(0xffffff), UNKNOWN(Int.MIN_VALUE); 5 | 6 | companion object { 7 | // Verbose for illustrative purposes 8 | fun fromOrdinal(ordinal: Int): MyColor = entries[ordinal] 9 | fun fromOrdinalOrNull(ordinal: Int): MyColor? = entries.getOrNull(ordinal) 10 | fun fromOrdinalOrDefault(ordinal: Int): MyColor = entries.getOrElse(ordinal) { UNKNOWN } 11 | fun fromName(name: String): MyColor = valueOf(name) 12 | fun fromNameIgnoreCase(name: String): MyColor = valueOf(name.uppercase()) 13 | fun fromNameOrNull(name: String): MyColor? = entries.find { it.name == name } 14 | fun fromNameIgnoreCaseOrNull(name: String): MyColor? = entries.find { it.name == name.uppercase() } 15 | fun fromValue(value: Int): MyColor = entries.first { it.value == value } 16 | fun fromValueOrNull(value: Int): MyColor? = entries.find { it.value == value } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/util/NormalDistributionUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import kotlin.math.absoluteValue 4 | import kotlin.math.exp 5 | import kotlin.math.sqrt 6 | 7 | object NormalDistributionUtil { 8 | /** Cumulative distribution function for the normal distribution */ 9 | fun cdf(mean: Double, sd: Double, x: Double) = 0.5 * (1.0 + erf((x - mean) / (sd * sqrt(2.0)))) 10 | 11 | /** Error function from Numerical Recipes 3rd Edition (p.265) */ 12 | fun erf(x: Double): Double { 13 | val a0 = 1.26551223 14 | val a1 = 1.00002368 15 | val a2 = 0.37409196 16 | val a3 = 0.09678418 17 | val a4 = -0.18628806 18 | val a5 = 0.27886807 19 | val a6 = -1.13520398 20 | val a7 = 1.48851587 21 | val a8 = -0.82215223 22 | val a9 = 0.17087277 23 | val t = 2.0 / (2.0 + x.absoluteValue) 24 | (1 - t * exp(-x * x - a0 + t * (a1 + t * (a2 + t * (a3 + t * (a4 + t * (a5 + t * (a6 + t * (a7 + t * (a8 + t * a9)))))))))).let { 25 | return if (x >= 0) it else -it 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/json/kotlinserialization/datetime/NullableLocalDateTimeSerializer.kt: -------------------------------------------------------------------------------- 1 | package json.kotlinserialization.datetime 2 | 3 | import kotlinx.serialization.ExperimentalSerializationApi 4 | import kotlinx.serialization.KSerializer 5 | import kotlinx.serialization.Serializer 6 | import kotlinx.serialization.encoding.Decoder 7 | import kotlinx.serialization.encoding.Encoder 8 | import java.time.LocalDateTime 9 | import java.time.format.DateTimeFormatter 10 | 11 | @OptIn(ExperimentalSerializationApi::class) 12 | @Serializer(forClass = LocalDateTime::class) 13 | object NullableLocalDateTimeSerializer : KSerializer { 14 | private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 15 | 16 | override fun serialize(encoder: Encoder, value: LocalDateTime?) { 17 | if (value != null) { 18 | encoder.encodeString(value.format(formatter)) 19 | } 20 | } 21 | 22 | override fun deserialize(decoder: Decoder): LocalDateTime? = runCatching { 23 | LocalDateTime.parse(decoder.decodeString(), formatter) 24 | }.getOrNull() 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/FlowExample.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.asSharedFlow 8 | import kotlinx.coroutines.flow.catch 9 | import kotlinx.coroutines.flow.emitAll 10 | import kotlinx.coroutines.flow.map 11 | import kotlinx.coroutines.launch 12 | 13 | private suspend fun main() = coroutineScope { 14 | val mutableFlow = MutableSharedFlow() 15 | val flow: Flow = mutableFlow.asSharedFlow() 16 | 17 | launch { 18 | flow.map { 19 | if (it == "b") throw Throwable() else it 20 | }.catch { 21 | println("catch") 22 | emit("catch") 23 | // Prevent the flow from completing. 24 | emitAll(flow) 25 | }.collect { 26 | println("collect: $it") 27 | } 28 | } 29 | 30 | delay(1000) 31 | mutableFlow.emit("a") 32 | mutableFlow.emit("b") 33 | delay(1000) 34 | mutableFlow.emit("c") 35 | } 36 | -------------------------------------------------------------------------------- /src/main/kotlin/util/io/FastScanner.kt: -------------------------------------------------------------------------------- 1 | package util.io 2 | 3 | import java.io.Closeable 4 | import java.io.PrintWriter 5 | import java.util.StringTokenizer 6 | 7 | // Usage of FastScanner 8 | private fun main() { 9 | FastScanner().use { fs -> 10 | PrintWriter(System.out, false /* writes at once */).use { pw -> 11 | val n = fs.nextInt() 12 | val xs = fs.nextIntegers(n) 13 | pw.println("Hello, World!") 14 | } 15 | } 16 | } 17 | 18 | class FastScanner : Closeable { 19 | private val br = System.`in`.bufferedReader() 20 | private var st = StringTokenizer("") 21 | 22 | override fun close() { 23 | br.close() 24 | } 25 | 26 | private fun next(): String { 27 | while (!st.hasMoreTokens()) st = StringTokenizer(br.readLine()) 28 | return st.nextToken() 29 | } 30 | 31 | fun nextInt() = next().toInt() 32 | fun nextZeroBasedInt() = next().toInt() - 1 33 | 34 | fun nextIntegers(n: Int) = IntArray(n) { 35 | nextInt() 36 | } 37 | 38 | fun nextZeroBasedIntegers(n: Int) = IntArray(n) { 39 | nextZeroBasedInt() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/MergeSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object MergeSort { 4 | fun sort(xs: IntArray): IntArray { 5 | if (xs.size < 2) return xs 6 | val middle = xs.size / 2 7 | return merge(sort(xs.copyOfRange(0, middle)), sort(xs.copyOfRange(middle, xs.size))) 8 | } 9 | 10 | private fun merge(left: IntArray, right: IntArray): IntArray { 11 | var leftIndex = 0 12 | var rightIndex = 0 13 | val merged = mutableListOf() 14 | while (true) { 15 | if (left[leftIndex] <= right[rightIndex]) { 16 | merged.add(left[leftIndex++]) 17 | if (leftIndex == left.size) { 18 | right.copyOfRange(rightIndex, right.size).toCollection(merged) 19 | break 20 | } 21 | } else { 22 | merged.add(right[rightIndex++]) 23 | if (rightIndex == right.size) { 24 | left.copyOfRange(leftIndex, left.size).toCollection(merged) 25 | break 26 | } 27 | } 28 | } 29 | return merged.toIntArray() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/util/datetime/TimeUtil.kt: -------------------------------------------------------------------------------- 1 | package util.datetime 2 | 3 | object TimeUtil { 4 | private fun convertToHoursMinutesSeconds(totalSeconds: Long): Triple { 5 | val hours = totalSeconds / 3600 6 | val minutes = (totalSeconds % 3600) / 60 7 | val seconds = totalSeconds % 60 8 | return Triple(hours, minutes, seconds) 9 | } 10 | 11 | fun hhMmSs(totalSeconds: Long): String { 12 | val (hours, minutes, seconds) = convertToHoursMinutesSeconds(totalSeconds) 13 | return "%02d:%02d:%02d".format(hours, minutes, seconds) 14 | } 15 | 16 | fun mmSs(totalSeconds: Long): String { 17 | val (_, minutes, seconds) = convertToHoursMinutesSeconds(totalSeconds) 18 | return "%02d:%02d".format(minutes, seconds) 19 | } 20 | 21 | fun hhMmSsOrMmSs(totalSeconds: Long): String { 22 | val (hours, minutes, seconds) = convertToHoursMinutesSeconds(totalSeconds) 23 | return if (hours > 0) { 24 | "%02d:%02d:%02d".format(hours, minutes, seconds) 25 | } else { 26 | "%02d:%02d".format(minutes, seconds) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/kotlin/util/DecibinaryUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import util.NumberUtil.pow 4 | import kotlin.math.pow 5 | 6 | object DecibinaryUtil { 7 | /** 8 | * @param d the upper limit of the number of digits of a decibinary 9 | * @param s decimal 10 | * @return the number of decibinaries, which consist of [d] or less digits, and evaluate to [s] 11 | */ 12 | fun getDecibinaryCount(d: Long, s: Long): Long = when { 13 | d == 0L && s == 0L -> 1 14 | d == 0L && s != 0L -> 0 15 | else -> LongRange(0, 9).map { // .map(...).sum() cannot be replaced by sumBy(...) because the latter does support Long. 16 | getDecibinaryCount(d - 1L, s - it * 2L.pow(d.toInt() - 1)) 17 | }.sum() 18 | } 19 | 20 | fun toDecimal(deciBinary: Long): Long { 21 | var result = 0L 22 | var q = deciBinary 23 | var i = 0 24 | do { 25 | result += (q % 10) * 2.0.pow(i++.toDouble()).toInt() 26 | q /= 10 27 | } while (q > 0) 28 | return result 29 | } 30 | 31 | fun printAsDecimal(deciBinary: Long) { 32 | println(toDecimal(deciBinary)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/kotlin/MyKotlinTest.kt: -------------------------------------------------------------------------------- 1 | import org.junit.jupiter.api.Test 2 | import kotlin.test.assertContains 3 | import kotlin.test.assertEquals 4 | import kotlin.test.assertIs 5 | import kotlin.test.assertNull 6 | import kotlin.test.assertTrue 7 | 8 | class MyKotlinTest { 9 | @Test 10 | fun assertTrueTest() { 11 | val actual = true 12 | assertTrue(actual) 13 | } 14 | 15 | @Test 16 | fun assertNullTest() { 17 | val actual: String? = null 18 | assertNull(actual) 19 | } 20 | 21 | @Test 22 | fun assertIsTest() { 23 | val actual = "" 24 | assertIs(actual) 25 | } 26 | 27 | @Test 28 | fun assertEqualsTest() { 29 | val expected = "a" 30 | val actual = "a" 31 | assertEquals(expected, actual) 32 | } 33 | 34 | @Test 35 | fun assertCollectionContainsTest() { 36 | val iterable = listOf("a", "b", "c") 37 | val element = "b" 38 | assertContains(iterable, element) 39 | } 40 | 41 | @Test 42 | fun assertStringContainsTest() { 43 | val actual = "abc" 44 | val substring = "b" 45 | assertContains(actual, substring) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/RadixSort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object RadixSort { 4 | fun sort(xs: IntArray) { 5 | require(xs.all { 0 <= it }) 6 | var place = 1 7 | repeat(xs.maxOrNull()!!.toString().length) { 8 | countingSort(xs, place) 9 | place *= 10 10 | } 11 | } 12 | 13 | private fun countingSort(xs: IntArray, place: Int) { 14 | val sorted = IntArray(xs.size) 15 | val appearances = IntArray(10) 16 | for (x in xs) appearances[getDigit(x, place)]++ 17 | for (i in 1 until appearances.size) appearances[i] += appearances[i - 1] 18 | /** 19 | * At this point, 20 | * appearances[0] ... appearances of '0' 21 | * appearances[1] ... appearances of '0' + appearances of '1' 22 | * ⋮ 23 | * appearances[9] ... appearances of '0' + appearances of '1' + ... + appearances of '9' 24 | */ 25 | for (i in xs.lastIndex downTo 0) { 26 | val digit = getDigit(xs[i], place) 27 | sorted[appearances[digit] - 1] = xs[i] 28 | appearances[digit]-- 29 | } 30 | sorted.copyInto(xs) 31 | } 32 | 33 | private fun getDigit(x: Int, place: Int) = (x / place) % 10 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/util/YearMonthUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.time.LocalDate 4 | import java.time.YearMonth 5 | import java.time.format.DateTimeFormatter 6 | 7 | object YearMonthUtil { 8 | fun parseOrNull(text: String, pattern: String) = runCatching { 9 | YearMonth.parse(text, DateTimeFormatter.ofPattern(pattern)) 10 | }.getOrNull() 11 | 12 | /** 13 | * @return an empty list if n = 0. Last month if n = 1. Last two months if n = 2. 14 | */ 15 | fun getPastMonths(n: Int = 0): List { 16 | val now = YearMonth.now() 17 | return List(n) { 18 | now.minusMonths(it.toLong()) 19 | } 20 | } 21 | 22 | /** 23 | * @return the current month if n = 0. Last month if n = 1. Second last month if n = 2. 24 | */ 25 | fun getPastMonth(n: Int = 0): YearMonth = YearMonth.now().minusMonths(n.toLong()) 26 | 27 | fun convertToDateAtDayOne(yearMonth: YearMonth = YearMonth.now()): LocalDate = yearMonth.atDay(1) 28 | fun convertToDateAtEndOfMonth(yearMonth: YearMonth = YearMonth.now()): LocalDate = yearMonth.atEndOfMonth() 29 | } 30 | 31 | private fun main() { 32 | println(YearMonthUtil.getPastMonths(6)) 33 | println(YearMonthUtil.getPastMonth(6)) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/example/MySealedClass.kt: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | sealed class MySealedClass( 4 | private val myPrivatePredefinedProperty: String, 5 | val myPublicPredefinedProperty: String, 6 | ) { 7 | abstract val myDynamicallyDefinedProperty: String 8 | } 9 | 10 | private data class MyDataClass3( 11 | override val myDynamicallyDefinedProperty: String, 12 | val property1: String, 13 | ) : MySealedClass( 14 | myPrivatePredefinedProperty = "Predefined private value for MyDataClass3", 15 | myPublicPredefinedProperty = "Predefined public value for MyDataClass3", 16 | ) 17 | 18 | private data class MyDataClass4( 19 | override val myDynamicallyDefinedProperty: String, 20 | val property2: String, 21 | ) : MySealedClass( 22 | myPrivatePredefinedProperty = "Predefined private value for MyDataClass4", 23 | myPublicPredefinedProperty = "Predefined public value for MyDataClass4", 24 | ) 25 | 26 | fun main() { 27 | val myDataClass1 = MyDataClass3( 28 | myDynamicallyDefinedProperty = "Dynamically defined value for MyDataClass3", 29 | property1 = "Property1!", 30 | ) 31 | 32 | val myDataClass2 = MyDataClass4( 33 | myDynamicallyDefinedProperty = "Dynamically defined value for MyDataClass4", 34 | property2 = "Property2!", 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/data/DisjointSets.kt: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | /** 4 | * https://en.wikipedia.org/wiki/Disjoint-set_data_structure 5 | */ 6 | class DisjointSets(n: Int) { 7 | private val parents = IntArray(n) { it } 8 | private val ranks = IntArray(n) 9 | 10 | /** Using the technique "path compression" */ 11 | private fun find(x: Int): Int { 12 | if (parents[x] == x) return x 13 | parents[x] = find(parents[x]) 14 | return parents[x] 15 | } 16 | 17 | /** Using the technique "union by rank" */ 18 | fun union(x: Int, y: Int) { 19 | val xRoot = find(x) 20 | val yRoot = find(y) 21 | when { 22 | ranks[xRoot] < ranks[yRoot] -> parents[xRoot] = yRoot 23 | ranks[xRoot] > ranks[yRoot] -> parents[yRoot] = xRoot 24 | xRoot != yRoot -> { 25 | parents[yRoot] = xRoot 26 | ranks[xRoot]++ 27 | } 28 | } 29 | } 30 | 31 | val components: Collection> 32 | get() { 33 | // Make each parent the direct child of its root ancestor. 34 | for (i in parents.indices) parents[i] = find(parents[i]) 35 | return parents 36 | .mapIndexed { i, x -> x to i } 37 | .groupBy({ it.first }, { it.second }) 38 | .values 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/WhyYouShouldUseLaunchIn.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.collect 6 | import kotlinx.coroutines.flow.flowOf 7 | import kotlinx.coroutines.flow.launchIn 8 | import kotlinx.coroutines.flow.onEach 9 | import kotlinx.coroutines.launch 10 | import kotlin.system.measureTimeMillis 11 | 12 | private suspend fun main() { 13 | measureTimeMillis { 14 | coroutineScope { 15 | launch { 16 | flowOf(Unit) 17 | .onEach { delay(1_000) } 18 | .collect() 19 | flowOf(Unit) 20 | .onEach { delay(1_000) } 21 | .collect() 22 | } 23 | } 24 | }.let { 25 | println("$it milliseconds") // prints almost 2 seconds because the two flows run in sequence. 26 | } 27 | 28 | measureTimeMillis { 29 | coroutineScope { 30 | flowOf(Unit) 31 | .onEach { delay(1_000) } 32 | .launchIn(this) 33 | flowOf(Unit) 34 | .onEach { delay(1_000) } 35 | .launchIn(this) 36 | } 37 | }.let { 38 | println("$it milliseconds") // prints almost 1 second because the two flows run in parallel. 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/CallbackFlowExample.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.cancel 4 | import kotlinx.coroutines.channels.awaitClose 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.flow.callbackFlow 8 | import kotlinx.coroutines.flow.onCompletion 9 | import kotlinx.coroutines.launch 10 | 11 | private class CountDownTimer( 12 | val total: Long, 13 | val interval: Long, 14 | val onFinish: () -> Unit, 15 | ) { 16 | private var onTick: ((millisUntilFinished: Long) -> Unit)? = null 17 | 18 | val flow = callbackFlow { 19 | onTick = { trySend(it) } 20 | awaitClose {} 21 | } 22 | 23 | suspend fun start() { 24 | for (i in total downTo 0 step interval) { 25 | onTick?.invoke(i) 26 | delay(interval) 27 | } 28 | onFinish() 29 | } 30 | } 31 | 32 | private suspend fun main() { 33 | coroutineScope { 34 | val countDownTimer = CountDownTimer(total = 3_000, interval = 1_000, ::cancel) 35 | 36 | launch { 37 | countDownTimer 38 | .flow 39 | .onCompletion { 40 | println("onCompletion") 41 | }.collect { 42 | println("collect: $it") 43 | } 44 | } 45 | 46 | countDownTimer.start() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/kotlin/MyReflectionTest.kt: -------------------------------------------------------------------------------- 1 | import kotlin.reflect.jvm.jvmName 2 | import kotlin.test.Test 3 | import kotlin.test.assertEquals 4 | 5 | class MyReflectionTest { 6 | @Test 7 | fun printTypeTest() { 8 | val x = 0 9 | val y: Any = 0 10 | 11 | assertEquals("kotlin.Int", x::class.java.kotlin.qualifiedName) // Faster than x::class.qualifiedName 12 | assertEquals("kotlin.Int", y::class.java.kotlin.qualifiedName) // Faster than hy::class.qualifiedName 13 | 14 | assertEquals("Int", x::class.java.kotlin.simpleName) // Faster than x::class.simpleName 15 | assertEquals("Int", y::class.java.kotlin.simpleName) // Faster than y::class.simpleName 16 | 17 | assertEquals("int", x::class.java.kotlin.jvmName) // Faster than x::class.jvmName 18 | assertEquals("java.lang.Integer", y::class.java.kotlin.jvmName) // Faster than y::class.jvmName 19 | 20 | assertEquals("int", x::class.java.name) 21 | assertEquals("java.lang.Integer", y::class.java.name) 22 | 23 | assertEquals("int", x::class.java.simpleName) 24 | assertEquals("Integer", y::class.java.simpleName) 25 | 26 | assertEquals("int", x::class.java.typeName) 27 | assertEquals("java.lang.Integer", y::class.java.typeName) 28 | 29 | assertEquals("int", x::class.java.canonicalName) 30 | assertEquals("java.lang.Integer", y::class.java.canonicalName) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/util/TimerUtil.kt: -------------------------------------------------------------------------------- 1 | import java.time.LocalTime 2 | import java.util.Timer 3 | import java.util.TimerTask 4 | import kotlin.concurrent.schedule 5 | import kotlin.time.Duration 6 | import kotlin.time.Duration.Companion.seconds 7 | 8 | object TimerUtil { 9 | class OneTimeTimer { 10 | private var timerTask: TimerTask? = null 11 | 12 | fun schedule(delay: Duration, action: TimerTask.() -> Unit) { 13 | timerTask?.cancel() 14 | timerTask = Timer().schedule(delay.inWholeMilliseconds, action) 15 | } 16 | 17 | fun cancel() { 18 | timerTask?.cancel() 19 | } 20 | } 21 | 22 | class PeriodicTimer { 23 | private var timerTask: TimerTask? = null 24 | 25 | fun schedule( 26 | period: Duration, 27 | delay: Duration = period, 28 | action: TimerTask.() -> Unit, 29 | ) { 30 | timerTask?.cancel() 31 | timerTask = Timer().schedule( 32 | delay = delay.inWholeMilliseconds, 33 | period = period.inWholeMilliseconds, 34 | action = action 35 | ) 36 | } 37 | 38 | fun cancel() { 39 | timerTask?.cancel() 40 | } 41 | } 42 | } 43 | 44 | private fun main() { 45 | val timerUtil = TimerUtil.PeriodicTimer() 46 | timerUtil.schedule(period = 1.seconds) { println(LocalTime.now()) } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/kotlin/util/FileUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.io.File 4 | import java.io.ObjectInputStream 5 | import java.io.ObjectOutputStream 6 | import java.io.PrintWriter 7 | 8 | object FileUtil { 9 | /** @param pathname a sibling of the src folder */ 10 | fun readText(pathname: String) = File(pathname).readText() 11 | 12 | /** @param pathname a sibling of the src folder */ 13 | fun readLines(pathname: String) = File(pathname).readLines() 14 | 15 | fun readAndPrint(pathname: String, block: (String) -> T) { 16 | println(block(File(pathname).readText())) 17 | } 18 | 19 | /** @param pathname a sibling of the src folder */ 20 | fun writeLines(pathname: String, block: (PrintWriter) -> Unit) = File(pathname).printWriter().use { 21 | block(it) 22 | } 23 | 24 | /** @param pathname a sibling of the src folder */ 25 | fun writeLines(pathname: String, times: Int, x: String) { 26 | writeLines(pathname) { pw -> 27 | repeat(times) { 28 | pw.println(x) 29 | } 30 | } 31 | } 32 | 33 | @Suppress("UNCHECKED_CAST") 34 | fun File.read(): T { 35 | ObjectInputStream(inputStream()).use { 36 | return it.readObject() as T 37 | } 38 | } 39 | 40 | fun File.write(xs: List<*>) { 41 | ObjectOutputStream(outputStream()).use { 42 | it.writeObject(xs) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/kotlin/util/io/StandardInputUtil.kt: -------------------------------------------------------------------------------- 1 | package util.io 2 | 3 | object StandardInputUtil { 4 | fun readInt() = readln().toInt() 5 | fun readIntegers() = readln().split(' ').map(String::toInt) 6 | fun readZeroBasedIntegers() = readln().split(' ').map(String::toInt).map { it - 1 } 7 | 8 | fun safeReadIntegers() = readln().split(' ').filterNot(String::isEmpty).map(String::toInt) 9 | 10 | /** 11 | * val xs = " 1 2 ".split("\\s+".toRegex()).map(String::toInt) // throw a NumberFormatException because of leading and trailing spaces. 12 | * val ys = " 1 2 ".trim().split(' ').map(String::toInt) // throw a NumberFormatException because of more than one space between integers. 13 | * val zs = " 1 2 ".trim().split("\\s+".toRegex()).map(String::toInt) // succeeds. 14 | */ 15 | @Deprecated( 16 | message = "Use safeReadIntegers() because it is faster.", 17 | replaceWith = ReplaceWith("safeReadIntegers()") 18 | ) 19 | fun safeReadIntegersSlow() = readln().trim().split("\\s+".toRegex()).map(String::toInt) 20 | 21 | /** 22 | * Read a matrix when the standard input is as follows. 23 | * 3 <- number of rows 24 | * a00 a01 a02 a03 25 | * a10 a11 a12 a13 26 | * a20 a21 a22 a23 27 | */ 28 | fun readMatrix() = List(readln().toInt()) { 29 | readln().split(' ').map(String::toInt).toIntArray() 30 | } 31 | 32 | /** 33 | * Read all the lines from standard input (stdin). 34 | */ 35 | fun readLines() = generateSequence(::readLine) 36 | } 37 | -------------------------------------------------------------------------------- /src/main/kotlin/sort/Heapsort.kt: -------------------------------------------------------------------------------- 1 | package sort 2 | 3 | object Heapsort { 4 | fun sort(xs: IntArray) { 5 | // Build a binary max heap. i.e. Create a binary max heap except the deepest nodes. 6 | for (i in xs.size / 2 downTo 0) heapify(xs, xs.size, i) 7 | 8 | for (lastIndex in xs.lastIndex downTo 0) { 9 | // Move the largest node, which is at the first element of the array, to the end of the array. 10 | xs.swap(0, lastIndex) 11 | 12 | // Create a binary max heap using all the nodes before the last index. 13 | heapify(xs, lastIndex, 0) 14 | } 15 | } 16 | 17 | /** 18 | * Create a binary max heap. 19 | */ 20 | private fun heapify(xs: IntArray, nodeCount: Int, currentNodeIndex: Int) { 21 | var indexOfMax = currentNodeIndex 22 | val leftChildIndex = 2 * currentNodeIndex + 1 23 | val rightChildIndex = 2 * currentNodeIndex + 2 24 | if (leftChildIndex < nodeCount) { 25 | if (xs[indexOfMax] < xs[leftChildIndex]) indexOfMax = leftChildIndex 26 | if (rightChildIndex < nodeCount && xs[indexOfMax] < xs[rightChildIndex]) indexOfMax = rightChildIndex 27 | if (currentNodeIndex != indexOfMax) { 28 | xs.swap(currentNodeIndex, indexOfMax) 29 | heapify(xs, nodeCount, indexOfMax) 30 | } 31 | } 32 | } 33 | 34 | private fun IntArray.swap(i: Int, j: Int) { 35 | val temp = this[i] 36 | this[i] = this[j] 37 | this[j] = temp 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/kotlin/graph/BfsSample.kt: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | fun bfs(vertices: List>, startVertex: Int): IntArray { 4 | val unknown = -1 5 | val distancesFromStartVertex = IntArray(vertices.size) { unknown }.also { 6 | it[startVertex] = 0 7 | } 8 | val verticesToVisit = mutableListOf(startVertex) 9 | while (verticesToVisit.isNotEmpty()) { 10 | val vertex = verticesToVisit.removeFirst() 11 | vertices[vertex] 12 | .filter { 13 | distancesFromStartVertex[it] == unknown 14 | } 15 | .forEach { 16 | distancesFromStartVertex[it] = distancesFromStartVertex[vertex] + 1 17 | verticesToVisit.add(it) 18 | } 19 | } 20 | return distancesFromStartVertex 21 | } 22 | 23 | fun main() { 24 | val (vertexCount, edgeCount) = readln().split(' ').map(String::toInt) 25 | val undirectedGraph = List(vertexCount) { mutableSetOf() } 26 | repeat(edgeCount) { 27 | val (v1, v2) = readlnOrNull() 28 | .orEmpty() 29 | .split(' ') 30 | .map(String::toInt) 31 | .map { it - 1 } // converts to zero-based numbering. 32 | undirectedGraph[v1].add(v2) 33 | undirectedGraph[v2].add(v1) 34 | } 35 | val startVertex = readln().toInt() - 1 // converts to zero-based numbering. 36 | bfs(undirectedGraph.map { it.toSet() }, startVertex) 37 | .filterIndexed { i, _ -> 38 | i != startVertex 39 | } 40 | .joinToString(" ") 41 | .let(::println) 42 | } 43 | -------------------------------------------------------------------------------- /src/main/kotlin/util/io/BufferedReaderWrapper.kt: -------------------------------------------------------------------------------- 1 | package util.io 2 | 3 | import java.io.Closeable 4 | import java.io.PrintWriter 5 | 6 | class BufferedReaderWrapper : Closeable { 7 | private val br = System.`in`.bufferedReader() 8 | 9 | override fun close() { 10 | br.close() 11 | } 12 | 13 | fun readInt() = br.readLine().toInt() 14 | fun readIntegers() = br.readLine().split(' ').map(String::toInt) 15 | fun readZeroBasedIntegers() = br.readLine().split(' ').map(String::toInt).map { it - 1 } 16 | 17 | /** 18 | * val xs = " 1 2 ".split("\\s+".toRegex()).map(String::toInt) // throw a NumberFormatException because of leading and trailing spaces. 19 | * val ys = " 1 2 ".trim().split(' ').map(String::toInt) // throw a NumberFormatException because of more than one space between integers. 20 | * val zs = " 1 2 ".trim().split("\\s+".toRegex()).map(String::toInt) // succeeds. 21 | */ 22 | fun safeReadIntegers1() = br.readLine().trim().split("\\s+".toRegex()).map(String::toInt) 23 | fun safeReadIntegers2() = br.readLine().split(' ').filterNot(String::isEmpty).map(String::toInt) 24 | 25 | /** 26 | * Read a matrix when the standard input is as follows. 27 | * 3 <- number of rows 28 | * a00 a01 a02 a03 29 | * a10 a11 a12 a13 30 | * a20 a21 a22 a23 31 | */ 32 | fun readMatrix() = List(br.readLine().toInt()) { 33 | br.readLine().split(' ').map(String::toInt).toIntArray() 34 | } 35 | } 36 | 37 | private fun main() { 38 | BufferedReaderWrapper().use { br -> 39 | PrintWriter(System.out, false /* writes at once */).use { pw -> 40 | pw.println(br.readInt()) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/kotlin/util/datetime/LocalDateUtil.kt: -------------------------------------------------------------------------------- 1 | package util.datetime 2 | 3 | import java.time.Instant 4 | import java.time.LocalDate 5 | import java.time.ZoneId 6 | import java.time.format.DateTimeFormatter 7 | import java.time.temporal.ChronoUnit 8 | import java.util.Date 9 | 10 | object LocalDateUtil { 11 | fun create(year: Int, month: Int, day: Int) = LocalDate.of(year, month, day) 12 | fun create(date: Date) = LocalDate.ofInstant(Instant.ofEpochMilli(date.time), ZoneId.systemDefault()) 13 | 14 | /** 15 | * @param date yyyy-mm-dd 16 | */ 17 | fun createOrNull(date: String) = runCatching { LocalDate.parse(date) }.getOrNull() 18 | fun isLessThanDaysOld(pastDate: LocalDate = LocalDate.now(), days: Long) = pastDate.isAfter(LocalDate.now().minusDays(days)) 19 | fun formatToJapaneseDate(date: LocalDate = LocalDate.now()): String = date.format(DateTimeFormatter.ofPattern("y年M月d日")) 20 | fun japaneseDatePassed(date1: LocalDate, date2: LocalDate): String { 21 | val diffInDays = ChronoUnit.DAYS.between(date1, date2) 22 | return if (diffInDays > 0) "${diffInDays}日前" else "本日" 23 | } 24 | 25 | private fun getMonth(date: LocalDate = LocalDate.now(), monthDelta: Long = 0) = if (monthDelta >= 0) { 26 | date.plusMonths(monthDelta) 27 | } else { 28 | date.minusMonths(-1 * monthDelta) 29 | }.monthValue 30 | 31 | fun getMonthExample() { 32 | println("Next month: ${getMonth(monthDelta = -1)}") 33 | println("Current month: ${getMonth()}") 34 | println("Previous month: ${getMonth(monthDelta = 1)}") 35 | } 36 | } 37 | 38 | private fun main() { 39 | println(LocalDateTimeUtil.create(2025, 1, 2, 3, 4, 5)) 40 | println(LocalDateTimeUtil.create(Date())) 41 | } 42 | -------------------------------------------------------------------------------- /src/main/kotlin/json/kotlinserialization/KotlinSerializationExample.kt: -------------------------------------------------------------------------------- 1 | package json.kotlinserialization 2 | 3 | import json.MySerializable 4 | import json.MySerializable2 5 | import kotlinx.serialization.Serializable 6 | import kotlinx.serialization.encodeToString 7 | import kotlinx.serialization.json.Json 8 | import util.FileUtil 9 | 10 | @Serializable 11 | private data class Person(val name: String, val age: Int) 12 | 13 | private fun showExample1() { 14 | val person = Person("Jane", 18) 15 | println(person) 16 | 17 | val json = Json.encodeToString(person) 18 | println(json) 19 | 20 | println(Json.decodeFromString(json)) 21 | } 22 | 23 | private fun showExample2() { 24 | // Array of numbers 25 | println(Json.decodeFromString>(string = "[-1, 3.14]")) 26 | } 27 | 28 | private fun showExample3() { 29 | // Object of numbers 30 | println(Json.decodeFromString>(string = """{ "a": -1, "b": 3.14 }""".trimIndent())) 31 | } 32 | 33 | private fun showExample4() { 34 | // The lenient mode does not throw a JsonDecodingException for unquoted JSON keys and unquoted JSON string values. 35 | val lenientJson = Json { 36 | isLenient = true 37 | } 38 | // Object of numbers 39 | println(lenientJson.decodeFromString>(string = "{ A: a, B: b }".trimIndent())) 40 | } 41 | 42 | private fun showExample5() { 43 | FileUtil.readAndPrint("data/comprehensive-object.json") { 44 | Json.decodeFromString(it) 45 | } 46 | } 47 | 48 | private fun showExample6() { 49 | FileUtil.readAndPrint("data/comprehensive-array.json") { 50 | Json.decodeFromString>(it) 51 | } 52 | } 53 | 54 | private fun main() { 55 | showExample1() 56 | showExample2() 57 | showExample3() 58 | showExample4() 59 | showExample5() 60 | } 61 | -------------------------------------------------------------------------------- /src/main/kotlin/util/datetime/LocalDateTimeUtil.kt: -------------------------------------------------------------------------------- 1 | package util.datetime 2 | 3 | import java.time.Duration 4 | import java.time.Instant 5 | import java.time.LocalDateTime 6 | import java.time.ZoneId 7 | import java.time.ZoneOffset 8 | import java.util.Date 9 | import kotlin.time.toKotlinDuration 10 | 11 | object LocalDateTimeUtil { 12 | fun create(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int): LocalDateTime = LocalDateTime.of(year, month, day, hour, minute, second) 13 | fun create(epochSecond: Long): LocalDateTime = LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.UTC) 14 | fun create(date: Date): LocalDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.time), ZoneId.systemDefault()) 15 | fun convertToEpochSecond(dateTime: LocalDateTime) = dateTime.toEpochSecond(ZoneOffset.UTC) 16 | fun isLessThanHoursOld(pastDateTime: LocalDateTime = LocalDateTime.now(), hours: Long) = pastDateTime.isAfter(LocalDateTime.now().minusHours(hours)) 17 | fun convertObsoleteJavaUtilDateToLocalDateTime(date: Date = Date()): LocalDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime() 18 | fun formatJapanesePassedDateTime(dateTime1: LocalDateTime, date2: LocalDateTime): String { 19 | val duration = Duration.between(dateTime1, date2).toKotlinDuration() 20 | return when { 21 | duration.inWholeDays > 0 -> "${duration.inWholeDays}日前" 22 | duration.inWholeHours > 0 -> "${duration.inWholeHours}時間前" 23 | duration.inWholeMinutes > 0 -> "${duration.inWholeMinutes}分前" 24 | duration.inWholeSeconds > 0 -> "${duration.inWholeSeconds}秒前" 25 | else -> "" 26 | } 27 | } 28 | } 29 | 30 | private fun main() { 31 | println(LocalDateTimeUtil.create(2025, 1, 2, 3, 4, 5)) 32 | println(LocalDateTimeUtil.create(LocalDateTimeUtil.convertToEpochSecond(LocalDateTime.now()))) 33 | println(LocalDateTimeUtil.create(Date())) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/util/io/StandardOutputUtil.kt: -------------------------------------------------------------------------------- 1 | package util.io 2 | 3 | import kotlin.system.measureTimeMillis 4 | 5 | object StandardOutputUtil { 6 | fun IntArray.printVertically() { 7 | forEach { 8 | println(it) 9 | } 10 | } 11 | 12 | fun LongArray.printVertically() { 13 | forEach { 14 | println(it) 15 | } 16 | } 17 | 18 | fun DoubleArray.printVertically() { 19 | forEach { 20 | println(it) 21 | } 22 | } 23 | 24 | fun Array<*>.printVertically() { 25 | forEach { 26 | println(it) 27 | } 28 | } 29 | 30 | fun Iterable<*>.printVertically() { 31 | forEach { 32 | println(it) 33 | } 34 | } 35 | 36 | fun IntArray.printHorizontally() { 37 | println(joinToString(" ")) 38 | } 39 | 40 | fun LongArray.printHorizontally() { 41 | println(joinToString(" ")) 42 | } 43 | 44 | fun DoubleArray.printHorizontally() { 45 | println(joinToString(" ")) 46 | } 47 | 48 | fun Array<*>.printHorizontally() { 49 | println(joinToString(" ")) 50 | } 51 | 52 | fun Iterable<*>.printHorizontally() { 53 | println(joinToString(" ")) 54 | } 55 | 56 | fun Array.printMatrix() { 57 | forEach { 58 | println(it.joinToString(" ")) 59 | } 60 | } 61 | 62 | fun Array.printMatrix() { 63 | forEach { 64 | println(it.joinToString(" ")) 65 | } 66 | } 67 | 68 | fun Array.printMatrix() { 69 | forEach { 70 | println(it.joinToString(" ")) 71 | } 72 | } 73 | 74 | fun printTime(times: Int = 1, action: () -> Unit) { 75 | println( 76 | measureTimeMillis { 77 | repeat(times) { 78 | action() 79 | } 80 | } 81 | ) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/kotlin/flow/IntervalFlowExample.kt: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.FlowCollector 6 | import kotlinx.coroutines.flow.asFlow 7 | import kotlinx.coroutines.flow.flow 8 | import kotlinx.coroutines.flow.onCompletion 9 | import kotlinx.coroutines.flow.onEach 10 | import kotlinx.coroutines.flow.onStart 11 | import kotlinx.coroutines.flow.take 12 | import kotlin.time.Duration 13 | import kotlin.time.Duration.Companion.seconds 14 | 15 | /** 16 | * Notice the order of [delay] and [FlowCollector.emit]. i.e. The initial delay is the same as the periodic delay. 17 | */ 18 | private fun interval(period: Duration) = flow { 19 | while (true) { 20 | delay(period) 21 | emit(Unit) 22 | } 23 | } 24 | 25 | private fun interval(period: Duration, delay: Duration = Duration.ZERO) = flow { 26 | delay(delay) 27 | while (true) { 28 | emit(Unit) 29 | delay(period) 30 | } 31 | } 32 | 33 | private fun countUp(period: Duration, delay: Duration = period) = 34 | generateSequence(0, Long::inc).asFlow().onStart { delay(delay) }.onEach { delay(period) } 35 | 36 | private fun countDown( 37 | period: Duration, 38 | interval: Duration, 39 | delay: Duration = Duration.ZERO, 40 | ) = flow { 41 | delay(delay) 42 | for (t in period.inWholeSeconds downTo 0 step interval.inWholeSeconds) { 43 | emit(t) 44 | delay(interval) 45 | } 46 | } 47 | 48 | private suspend fun main() = coroutineScope { 49 | interval(1.seconds).take(3).collect { 50 | println("collect: $it") 51 | } 52 | 53 | countUp(1.seconds).take(3).collect { 54 | println("collect: $it") 55 | } 56 | 57 | countDown( 58 | period = 3.seconds, 59 | interval = 1.seconds 60 | ).onCompletion { println("onCompletion") } 61 | .collect { 62 | println("collect: $it") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/kotlin/util/ListUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.util.Collections 4 | 5 | object ListUtil { 6 | /** 7 | * Refer to [IterableUtil.rotateLeft] for rotating left. 8 | * 9 | * @param distance must be less than or equal to the length of the list. 10 | */ 11 | fun rotateRight(xs: List, distance: Int) = xs.takeLast(distance) + xs.dropLast(distance) 12 | 13 | fun splitAt(xs: List, index: Int) = xs.subList(0, index) to xs.subList(index, xs.size) 14 | 15 | fun splitLast(xs: List): Pair, T?> { 16 | if (xs.isEmpty()) return emptyList() to null 17 | return xs.dropLast(n = 1) to xs.last() 18 | } 19 | 20 | fun List.rotate(distance: Int) = toList().also { // toList() is a deep copy to avoid changing the original array. 21 | Collections.rotate(it, distance) 22 | } 23 | 24 | fun swap(xs: MutableList, i: Int, j: Int) { 25 | val temp = xs[i] 26 | xs[i] = xs[j] 27 | xs[j] = temp 28 | } 29 | 30 | fun createPair(xs: List) = xs[0] to xs[1] 31 | 32 | fun createPairs(xs: List) = sequence { 33 | for (i in xs.indices) { 34 | for (j in i + 1..): Pair { 42 | var min = xs[0] 43 | var max = xs[0] 44 | for (i in 1 until xs.size) { 45 | if (xs[i] < min) { 46 | min = xs[i] 47 | } else if (xs[i] > max) { 48 | max = xs[i] 49 | } 50 | } 51 | return min to max 52 | } 53 | 54 | /** 55 | * @return the number of the given element in a sorted list 56 | */ 57 | fun > count(xs: List, element: T): Int { 58 | var i = xs.binarySearch(element) 59 | if (i < 0) return 0 60 | var count = 1 61 | var j = i 62 | while (xs.getOrNull(--i) == element) count++ 63 | while (xs.getOrNull(++j) == element) count++ 64 | return count 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/kotlin/util/StringUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import kotlin.math.max 4 | 5 | object StringUtil { 6 | fun alphabetize(s: String) = s.toCharArray().sorted().joinToString("") 7 | fun equalsIgnoreCase(s1: String, s2: String) = s1.equals(other = s2, ignoreCase = true) 8 | fun replaceAt(s: String, index: Int, replacement: Char) = s.replaceRange(startIndex = index, endIndex = index + 1, replacement = replacement.toString()) 9 | 10 | fun getSubstrings(s: String) = sequence { 11 | for (i in s.indices) { 12 | for (j in i + 1..s.length) yield(s.substring(i, j)) 13 | } 14 | } 15 | 16 | fun isAllSameChars(s: String) = if (s.isEmpty()) true else s.toSet().size == 1 17 | 18 | /** https://en.wikipedia.org/wiki/Longest_common_subsequence_problem */ 19 | private fun findLongestCommonSubsequence(s1: String, s2: String): String { 20 | fun splitLast(s: String) = s.substringBeforeLast("") to s.substringAfterLast("") 21 | 22 | if (s1.isEmpty() || s2.isEmpty()) return "" 23 | val (s1a, s1last) = splitLast(s1) 24 | val (s2a, s2last) = splitLast(s2) 25 | if (s1last == s2last) return findLongestCommonSubsequence(s1a, s2a) + s1last 26 | val lcs1 = findLongestCommonSubsequence(s1, s2a) 27 | val lcs2 = findLongestCommonSubsequence(s1a, s2) 28 | return if (lcs1.length > lcs2.length) lcs1 else lcs2 29 | } 30 | 31 | /** https://en.wikipedia.org/wiki/Longest_common_subsequence_problem */ 32 | fun findLongestCommonSubsequenceLength(s1: String, s2: String) = List(s1.length + 1) { 33 | IntArray(s2.length + 1) 34 | }.also { 35 | for (i in s1.indices) { 36 | for (j in s2.indices) it[i + 1][j + 1] = if (s1[i] == s2[j]) it[i][j] + 1 else max(it[i + 1][j], it[i][j + 1]) 37 | } 38 | }[s1.length][s2.length] 39 | 40 | fun permute(s: String): List { 41 | fun permute(s: String, result: String = ""): List = if (s.isEmpty()) { 42 | listOf(result) 43 | } else { 44 | s.flatMapIndexed { i, c -> 45 | permute(s.removeRange(i, i + 1), result + c) 46 | } 47 | } 48 | return permute(s) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/kotlin/coroutines/VirtualTimeTest.kt: -------------------------------------------------------------------------------- 1 | package coroutines 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.drop 7 | import kotlinx.coroutines.flow.first 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.launch 10 | import kotlinx.coroutines.test.advanceTimeBy 11 | import kotlinx.coroutines.test.runTest 12 | import kotlinx.coroutines.withContext 13 | import kotlin.test.Test 14 | import kotlin.test.assertEquals 15 | import kotlin.time.Duration.Companion.minutes 16 | import kotlin.time.Duration.Companion.seconds 17 | import kotlin.time.measureTime 18 | 19 | @OptIn(ExperimentalCoroutinesApi::class) 20 | class VirtualTimeTest { 21 | @Test 22 | fun test1() = runTest { 23 | var x = "a" 24 | launch { 25 | x = "b" 26 | delay(1.seconds) // not skipped because this delay(...) is inside launch. 27 | x = "c" 28 | } 29 | assertEquals("a", x) 30 | advanceTimeBy(1) 31 | assertEquals("b", x) 32 | advanceTimeBy(1_000) 33 | assertEquals("c", x) 34 | } 35 | 36 | @Test 37 | fun test2() = runTest { 38 | val flow = flow { 39 | emit("a") 40 | delay(1.minutes) // skipped because this delay(...) is not inside launch. 41 | emit("b") 42 | } 43 | assertEquals("a", flow.first()) 44 | assertEquals("b", flow.drop(1).first()) 45 | } 46 | 47 | /** 48 | * [Delay-skipping](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-test.html) 49 | */ 50 | @Test 51 | fun measureTimeTest() = runTest { 52 | val elapsed = measureTime { 53 | val job = launch { 54 | delay(1.minutes) // skipped because this delay(...) is inside launch. 55 | withContext(Dispatchers.Default) { 56 | delay(5.seconds) // not skipped because this delay(...) is in a dispatcher that doesn't use TestCoroutineScheduler. 57 | } 58 | } 59 | job.join() 60 | } 61 | println(elapsed) // about five seconds 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/kotlin/json/MoshiConverter.kt: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import com.squareup.moshi.JsonAdapter 4 | import com.squareup.moshi.Moshi 5 | import com.squareup.moshi.Types 6 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory 7 | import kotlinx.serialization.Serializable 8 | import kotlinx.serialization.encodeToString 9 | import kotlinx.serialization.json.Json 10 | import util.FileUtil.readAndPrint 11 | 12 | @Serializable 13 | private data class Person(val name: String, val age: Int) 14 | 15 | object MoshiConverter { 16 | val moshi: Moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() 17 | inline fun getAdapter(): JsonAdapter = moshi.adapter(T::class.java) 18 | 19 | inline fun getListAdapter(): JsonAdapter> = moshi.adapter( 20 | Types.newParameterizedType( 21 | List::class.java, T::class.java 22 | ) 23 | ) 24 | 25 | inline fun getMapAdapter(): JsonAdapter> = moshi.adapter( 26 | Types.newParameterizedType( 27 | Map::class.java, String::class.java, T::class.java 28 | ) 29 | ) 30 | } 31 | 32 | private fun showExample1() { 33 | val person = Person("Jane", 18) 34 | println(person) 35 | 36 | val json = Json.encodeToString(person) 37 | println(json) 38 | 39 | // Note that the returned value is not nullable unlike Moshi. 40 | val person2: Person = Json.decodeFromString(json) 41 | println(person2) 42 | } 43 | 44 | private fun showExample2() { 45 | readAndPrint("data/object-of-numbers.json") { 46 | MoshiConverter.getMapAdapter().fromJson(it) 47 | } 48 | } 49 | 50 | private fun showExample3() { 51 | readAndPrint("data/array-of-numbers.json") { 52 | MoshiConverter.getListAdapter().fromJson(it) 53 | } 54 | } 55 | 56 | private fun showExample4() { 57 | readAndPrint("data/comprehensive-object.json") { 58 | MoshiConverter.getAdapter().fromJson(it) 59 | } 60 | } 61 | 62 | private fun showExample5() { 63 | readAndPrint("data/comprehensive-array.json") { 64 | MoshiConverter.getListAdapter().fromJson(it) 65 | } 66 | } 67 | 68 | private fun main() { 69 | showExample1() 70 | showExample2() 71 | showExample3() 72 | showExample4() 73 | showExample5() 74 | } 75 | -------------------------------------------------------------------------------- /markdown/misc.md: -------------------------------------------------------------------------------- 1 | # Module 2 | 3 | is a set of Kotlin files compiled together. 4 | 5 | # Function type "(A.(B) -> C)" 6 | 7 | * is called `a function literal with receiver` 8 | * https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver 9 | 10 | # Kotlin.Result 11 | 12 | * Android Studio says `'Kotlin.Result' cannot be used as a return type.`. 13 | * https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/result.md#limitations 14 | 15 | # Interoperability between Kotlin and Java 16 | 17 | Java sees functions defined in Kotlin only through bytecode. 18 | 19 | # Int.MAX_VALUE 20 | 21 | * 10^9 (1,000,000,000) < `Int.MAX_VALUE` (2,147,483,647) < 10^10 < (10,000,000,000) 22 | * This knowledge is necessary in competitive programming 23 | 24 | # Declaration-site variance 25 | 26 | * Invariant 27 | * is a type parameter without the "in" or "out" modifier. 28 | * If a type parameter is invariant, the exact same type is required. 29 | * Covariant 30 | * is a type parameter with the "out" modifier. 31 | * can only be returned (produced) and never referenced (consumed). 32 | * can return a subtype. 33 | * Producer 34 | * If a signature of a function is supposed to return a Dog, it can return a Pug but not an Animal. 35 | * Contravariant 36 | * is a type parameter with the "in" modifier. 37 | * can only be referenced (consumed) and never returned (produced). 38 | * can receive a subtype. 39 | * Consumer 40 | * If a signature of a function is supposed to receive a Dog, it can receive a Pug but not an Animal. 41 | * https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance 42 | 43 | # @Volatile 44 | 45 | is normally used in a database instance. 46 | 47 | ``` 48 | @Volatile 49 | private var INSTANCE: MyDatabase? = null 50 | ``` 51 | 52 | * Volatile fields are thread-safe. They are never cached, and all writes and reads will be done to and from the main 53 | memory. It means that changes made by one thread to the field are immediately made visible to other threads. 54 | * The increment operator "++" in JVM languages is not thread-safe even when the field is volatile because "++" is not an 55 | atomic operation because it consists of a read, an increment, and a write. 56 | * https://kotlinlang.org/docs/shared-mutable-state-and-concurrency.html#volatiles-are-of-no-help 57 | * > Volatiles are of no help. There is a common misconception that making a variable volatile solves concurrency 58 | problem. 59 | * https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-volatile/ 60 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/AsyncAwaitExample.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.CancellationException 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.cancelAndJoin 6 | import kotlinx.coroutines.coroutineScope 7 | import kotlinx.coroutines.delay 8 | import kotlinx.coroutines.ensureActive 9 | import kotlinx.coroutines.launch 10 | 11 | /** 12 | * This example demonstrates the behavior described below. 13 | * > if the coroutine in which await was called got cancelled, 14 | * https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 15 | * 16 | * Result: 17 | * try started! 18 | * async is running! 19 | * catch started! 20 | * Done! 21 | */ 22 | private suspend fun doExample1() = coroutineScope { 23 | val job = launch { 24 | val deferred = async { 25 | println("async is running!") 26 | } 27 | try { 28 | println("try started!") 29 | deferred.await() 30 | println("try is ending!") 31 | } catch (e: CancellationException) { 32 | println("catch started!") 33 | ensureActive() // throws CancellationException because the current coroutine is cancelled. 34 | println(e) // does not execute even if `ensureActive()` above is replaced by `throw e`. 35 | println("catch is ending!") // does not execute even if `ensureActive()` above is replaced by `throw e`. 36 | } 37 | } 38 | job.cancelAndJoin() 39 | println("Done!") 40 | } 41 | 42 | /** 43 | * This example demonstrates the behavior described below. 44 | * > or if the Deferred itself got completed with a CancellationException. 45 | * https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 46 | * 47 | * Result: 48 | * async started! 49 | * try started! 50 | * catch started! 51 | * kotlinx.coroutines.JobCancellationException: DeferredCoroutine was cancelled; job=DeferredCoroutine{Cancelled}@7919012c 52 | * catch is ending! 53 | * Done! 54 | */ 55 | private suspend fun doExample2() = coroutineScope { 56 | val deferred = async { 57 | println("async started!") 58 | // Delay this coroutine a little so that it does not complete before being cancelled. 59 | delay(100) 60 | println("async is ending!") 61 | } 62 | deferred.cancel() 63 | try { 64 | println("try started!") 65 | deferred.await() 66 | println("try is ending!") 67 | } catch (e: CancellationException) { 68 | println("catch started!") 69 | ensureActive() // does not throw CancellationException because the current coroutine is cancelled. 70 | println(e) // executes unless `ensureActive()` above is replaced by `throw e`. 71 | println("catch is ending!") // executes unless `ensureActive()` above is replaced by `throw e`. 72 | 73 | } 74 | println("Done!") 75 | } 76 | 77 | private suspend fun main() = coroutineScope { 78 | doExample1() 79 | } 80 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /src/main/kotlin/util/NumberUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import util.CollectionUtil.permute 4 | import kotlin.math.ceil 5 | import kotlin.math.floor 6 | import kotlin.math.pow 7 | import kotlin.math.sqrt 8 | 9 | object NumberUtil { 10 | object Bit { 11 | fun getBits(x: Int) = Integer.toBinaryString(x).padStart(Int.SIZE_BITS, '0') 12 | 13 | fun getBitsForNonNegativeInt(x: Int) { 14 | require(x >= 0) 15 | x.toString(2).padStart(Int.SIZE_BITS, '0') 16 | } 17 | 18 | fun getBitAt(x: Int, index: Int) = (x shr index) and 1 19 | } 20 | 21 | fun isEven(x: Int) = x % 2 == 0 22 | fun isOdd(x: Int) = x % 2 == 1 23 | 24 | fun isPerfectSquare(x: Int) = sqrtInt(x).let { 25 | x == it * it 26 | } 27 | 28 | /** O(sqrt(n)) */ 29 | fun isPrime(x: Int) = when { 30 | x == 2 -> true 31 | x < 2 || x % 2 == 0 -> false 32 | else -> (3..sqrt(x.toDouble()).toInt() step 2).none { x % it == 0 } 33 | } 34 | 35 | private fun sqrtInt(x: Int) = sqrt(x.toDouble()).toInt() 36 | 37 | val Int.largestDivisor: Int 38 | get() { 39 | for (i in 2..this) { 40 | if (this % i == 0) return this / i 41 | } 42 | return 1 43 | } 44 | 45 | val Int.nth: String 46 | get() { 47 | val lastDigit = this % 10 48 | return toString() + when { 49 | this in 11..13 || lastDigit !in 1..3 -> "th" 50 | lastDigit == 1 -> "st" 51 | lastDigit == 2 -> "nd" 52 | lastDigit == 3 -> "rd" 53 | else -> error("Impossible state") 54 | } 55 | } 56 | 57 | fun Int.permute() = (1..this).toList().permute() 58 | fun Int.between(fromInclusive: Int, toExclusive: Int) = this in fromInclusive until toExclusive 59 | fun Int.pow(n: Int) = toDouble().pow(n).toInt() // returns Int.MAX_VALUE when it overflowed. 60 | fun Int.pow(n: Long) = toDouble().pow(n.toInt()).toLong() 61 | fun Long.pow(n: Int) = toDouble().pow(n).toLong() 62 | fun Long.pow(n: Long) = toDouble().pow(n.toInt()).toLong() 63 | fun Long.divideAndCeil(divisor: Long) = ceil(toDouble() / divisor).toLong() 64 | fun Long.divideAndFloor(divisor: Long) = floor(toDouble() / divisor).toLong() 65 | fun divMod(a: Int, b: Int) = a / b to a % b 66 | 67 | /** 68 | * The power of n 69 | * @param n the exponent 70 | * @return 2 ^ [n] 71 | */ 72 | fun pow2(n: Int) = 1 shl n 73 | 74 | /** 75 | * Iterative method 76 | * @receiver must be greater than or equal to 0. 77 | */ 78 | fun Int.factorial() = if (this <= 1) 1 else (2..this).reduce(Int::times) 79 | 80 | /** 81 | * Tail recursive method 82 | * @receiver must be greater than or equal to 0. 83 | */ 84 | tailrec fun Int.factorialRecursive(acc: Int = 1): Int = if (this <= 1) acc else (this - 1).factorialRecursive(acc * this) 85 | 86 | fun nPr(n: Int, r: Int) = n.factorial().toDouble() / (n - r).factorial() 87 | fun nCr(n: Int, r: Int) = nPr(n, r) / r.factorial() 88 | 89 | /** @return 1, 2, ..., or 6, if sides is 6 */ 90 | fun rollDice(sides: Int = 6) = (1..sides).random() 91 | } 92 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/coRunCatching.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.ensureActive 8 | import kotlinx.coroutines.launch 9 | 10 | /** 11 | * https://detekt.dev/docs/rules/coroutines/#suspendfunswallowedcancellation 12 | */ 13 | inline fun CoroutineScope.coRunCatching(block: CoroutineScope.() -> T) = try { 14 | Result.success(value = block()) 15 | } catch (t: Throwable) { 16 | ensureActive() 17 | Result.failure(exception = t) 18 | } 19 | 20 | /** 21 | * This example demonstrates the behavior described below. 22 | * > if the coroutine in which await was called got cancelled, 23 | * https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 24 | * 25 | * Result: 26 | * try started! 27 | * async started! 28 | * onFailure: kotlinx.coroutines.JobCancellationException: DeferredCoroutine was cancelled; job=DeferredCoroutine{Cancelled}@64db3069 29 | * Done! 30 | */ 31 | private suspend fun doExample1() = coroutineScope { 32 | val job = launch { 33 | coRunCatching { 34 | println("try started!") 35 | delay(timeMillis = 100) // waits to avoid completing before being cancelled. 36 | println("try ended!") 37 | }.onSuccess { 38 | println("onSuccess: $it") 39 | }.onFailure { 40 | // onFailure is not executed because `ensureActive()` inside coRunCatching throws CancellationException. 41 | println("onFailure: $it") 42 | } 43 | } 44 | job.cancel() 45 | delay(timeMillis = 100) // waits for catch inside coRunCatching to run. 46 | job.join() 47 | println("Done!") 48 | } 49 | 50 | /** 51 | * This example demonstrates the behavior described below. 52 | * > or if the Deferred itself got completed with a CancellationException. 53 | * https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 54 | * 55 | * Result: 56 | * try started! 57 | * async started! 58 | * onFailure: kotlinx.coroutines.JobCancellationException: DeferredCoroutine was cancelled; job=DeferredCoroutine{Cancelled}@1c5f44ad 59 | * Done! 60 | */ 61 | private suspend fun doExample2() = coroutineScope { 62 | coRunCatching { 63 | println("try started!") 64 | val deferred = async { 65 | println("async started!") 66 | delay(timeMillis = 100) // waits to avoid completing before being cancelled. 67 | println("async ended!") 68 | } 69 | deferred.cancel() 70 | deferred.await() // throws CancellationException. 71 | println("try ended!") 72 | }.onSuccess { 73 | println("onSuccess: $it") 74 | }.onFailure { 75 | // onFailure is executed because `ensureActive()` inside coRunCatching does not throw CancellationException. 76 | // onFailure is executed unless you replace `ensureActive()` inside coRunCatching with `throw t`. 77 | println("onFailure: $it") 78 | } 79 | println("Done!") 80 | } 81 | 82 | private suspend fun main() = coroutineScope { 83 | doExample1() 84 | } 85 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/SafeLaunch.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.ensureActive 8 | import kotlinx.coroutines.launch 9 | 10 | inline fun CoroutineScope.safeLaunch( 11 | crossinline tryBlock: suspend CoroutineScope.() -> Unit, 12 | crossinline catchBlock: suspend CoroutineScope.(Throwable) -> Unit = {}, 13 | ) = launch { 14 | try { 15 | tryBlock() 16 | } catch (t: Throwable) { 17 | ensureActive() 18 | catchBlock(t) 19 | } 20 | } 21 | 22 | /** 23 | * This example demonstrates the behavior described below. 24 | * > if the coroutine in which await was called got cancelled, 25 | * https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 26 | * 27 | * Result: 28 | * try started! 29 | * Done! 30 | */ 31 | private suspend fun doExample1() = coroutineScope { 32 | val job = safeLaunch( 33 | tryBlock = { 34 | println("try started!") 35 | delay(timeMillis = 100) // waits to avoid completing before being cancelled. 36 | println("try ended!") 37 | }, 38 | catchBlock = { 39 | println("catch started!") 40 | // The following is not executed because `ensureActive()` inside safeLaunch throws CancellationException. 41 | println(it) 42 | println("catch ended!") 43 | }, 44 | ) 45 | job.cancel() 46 | delay(timeMillis = 100) // waits for catchBlock to run. 47 | job.join() 48 | println("Done!") 49 | } 50 | 51 | /** 52 | * This example demonstrates the behavior described below. 53 | * > or if the Deferred itself got completed with a CancellationException. 54 | * https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 55 | * 56 | * Result: 57 | * try started! 58 | * async started! 59 | * catch started! 60 | * kotlinx.coroutines.JobCancellationException: DeferredCoroutine was cancelled; job=DeferredCoroutine{Cancelled}@6575b4c7 61 | * catch ended! 62 | * Done! 63 | */ 64 | private suspend fun doExample2() = coroutineScope { 65 | val job = safeLaunch( 66 | tryBlock = { 67 | println("try started!") 68 | val deferred = async { 69 | println("async started!") 70 | delay(timeMillis = 100) // waits to avoid completing before being cancelled. 71 | println("async ended!") 72 | } 73 | deferred.cancel() 74 | deferred.await() 75 | println("try ended!") 76 | }, 77 | catchBlock = { 78 | println("catch started!") 79 | // The following is not executed because `ensureActive()` inside safeLaunch does not throw CancellationException. 80 | // The following is executed unless you replace `ensureActive()` inside safeLaunch with `throw t`. 81 | println(it) 82 | println("catch ended!") 83 | }, 84 | ) 85 | delay(timeMillis = 100) // waits for catchBlock to run. 86 | job.join() 87 | println("Done!") 88 | } 89 | 90 | private suspend fun main() = coroutineScope { 91 | doExample1() 92 | } 93 | -------------------------------------------------------------------------------- /markdown/best-practices.md: -------------------------------------------------------------------------------- 1 | # Use A rather than B for simplicity or clarity 2 | 3 | A|B|Note 4 | --|--|-- 5 | `with(...)` | `run(...)` | if the receiver is not nullable. 6 | `emptyList`
`emptySet`
`emptyMap`
`emptyFlow`|`listOf`
`setOf`
`mapOf`
`flowOf` 7 | `buildList`
`buildSet`
`buildMap`|`mutableListOf`
`mutableSetOf`
`mutableMapOf` 8 | `repeat(n) { println(it) }` | `for (i in 0.. 9 | `Iterable.find(...)` | `Iterable.firstOrNull(...)` | `find` is an alias of `firstOrNull`. 10 | `Collection.size`
`String.length`|`Collection.count()`
`String.count()` 11 | `String.substring(...)`|`String.take(...)` 12 | `Set`|`List`|if elements are unique and unordered. Don't overuse `List`. 13 | `SortedSet`|`Set`|if elements must remain sorted. 14 | `SortedMap`|`Map`|if keys must remain sorted. 15 | `Iterable.forEachIndexed { i, x -> ... }` | `Iterable.forEach { ... }` | if you need to access both indices and elements. 16 | `nullableString.orEmpty()`
`nullableList.orEmpty()` | `nullableString ?: ""`
`nullableList ?: emptyList()` 17 | `CharSequence.lastIndex`
`List.lastIndex` | `String.length - 1`
`List.size - 1` 18 | `List.sumBy {...}` or `List.sumByDouble {...}` | `List.map {...}.sum()` 19 | `(this.)javaClass.simpleName` | `this::class.java.simpleName` 20 | `Flow.filterNotNull()` | `Flow.flatMapConcat { if (it != null) flowOf(it) else emptyFlow() }` 21 | 22 | ## Prefer infix notation 23 | Recommended|Not recommended 24 | --|-- 25 | `in`
`!in`|`contains` 26 | `A to B`|`Pair(A, B)` 27 | 28 | # Date and time 29 | Recommended|Not recommended 30 | --|-- 31 | `kotlin.time.Duration`|`java.time.Duration` 32 | `DateTimeFormatter.ISO_LOCAL_DATE`|`DateTimeFormatter.ofPattern("yyyy-MM-dd")` 33 | `DateTimeFormatter.ISO_LOCAL_TIME`|`DateTimeFormatter.ofPattern("HH:mm:ss")` 34 | 35 | # Use [onEach](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/on-each.html) or [onEachIndexed](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/on-each-indexed.html) when you want to perform a side effect for clarity 36 | 37 | For example, the following is better ... 38 | 39 | ```kotlin 40 | val capitals = listOf('a', 'b', 'c') 41 | .onEach { println(it) } // a b c 42 | .map { it.uppercaseChar() } 43 | ``` 44 | 45 | ... than the following. 46 | 47 | ```kotlin 48 | val capitals = listOf('a', 'b', 'c') 49 | .map { 50 | println(it) // a b c 51 | it.uppercaseChar() 52 | } 53 | ``` 54 | 55 | # Misc 56 | 57 | - Decompose a pair if possible. 58 | - Recommended: `listOf("apple" to "🍎", "orange" to "🍊").map { it.first + it.second })` 59 | - Not recommended: `listOf("apple" to "🍎", "orange" to "🍊").map { (fruit, emoji) -> fruit + emoji }` 60 | - When defining a function, make the parameter types as abstract as possible. 61 | - e.g. Use a type as left as possible. 62 | - Iterable > Collection > (Set) > List. 63 | - Mark a function with `suspend` if you need to call another suspend function in it, but cannot access `CoroutineScope`. 64 | -------------------------------------------------------------------------------- /src/main/kotlin/util/Array2dUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | object Array2dUtil { 4 | fun create2dIntArray(rowCount: Int, columnCount: Int) = Array(rowCount) { IntArray(columnCount) } 5 | fun create2dDoubleArray(rowCount: Int, columnCount: Int) = Array(rowCount) { DoubleArray(columnCount) } 6 | fun create2dArray(vararg xs: IntArray) = arrayOf(*xs) 7 | fun create2dArray(vararg xs: DoubleArray) = arrayOf(*xs) 8 | inline fun create2dArray(vararg xs: Array) = arrayOf(*xs) 9 | 10 | fun Array.rowCount() = size 11 | fun Array.columnCount() = first().size 12 | 13 | fun Array.transpose(): Array { 14 | val transposed = create2dDoubleArray(columnCount(), rowCount()) 15 | for (i in indices) { 16 | for (j in first().indices) { 17 | transposed[j][i] = this[i][j] 18 | } 19 | } 20 | return transposed 21 | } 22 | 23 | fun Array.subMatrix(rowToExclude: Int, columnToExclude: Int): Array { 24 | val subMatrix = create2dDoubleArray(rowCount() - 1, columnCount() - 1) 25 | var subMatrixRow = 0 26 | for (matrixRow in indices) { 27 | if (matrixRow == rowToExclude) continue 28 | var subMatrixColumn = 0 29 | for (matrixColumn in first().indices) { 30 | if (matrixColumn == columnToExclude) continue 31 | subMatrix[subMatrixRow][subMatrixColumn] = this[matrixRow][matrixColumn] 32 | subMatrixColumn++ 33 | } 34 | subMatrixRow++ 35 | } 36 | return subMatrix 37 | } 38 | 39 | fun Array.multiply(x: Double): Array { 40 | for (row in indices) { 41 | for (column in first().indices) { 42 | this[row][column] = x * this[row][column] 43 | } 44 | } 45 | return this 46 | } 47 | 48 | fun Array.multiply(matrix: Array): Array { 49 | require(columnCount() == matrix.rowCount()) 50 | val result = create2dDoubleArray(size, matrix.columnCount()) 51 | for (i in indices) { 52 | for (j in matrix.first().indices) { 53 | for (k in first().indices) { 54 | result[i][j] += this[i][k] * matrix[k][j] 55 | } 56 | } 57 | } 58 | return result 59 | } 60 | 61 | fun Array.inverse(): Array { 62 | fun Array.determinant(): Double { 63 | fun Array.isSquareMatrix() = rowCount() == columnCount() 64 | require(isSquareMatrix()) 65 | return when (val n = rowCount()) { 66 | 1 -> this[0][0] 67 | 2 -> this[0][0] * this[1][1] - this[0][1] * this[1][0] 68 | else -> { 69 | var sum = 0.0 70 | for (column in 0 until n) { 71 | sum += (if (column % 2 == 0) 1 else -1) * this[0][column] * subMatrix(0, column).determinant() 72 | } 73 | sum 74 | } 75 | } 76 | } 77 | 78 | fun Array.cofactor(): Array { 79 | val cofactorMatrix = create2dDoubleArray(rowCount(), columnCount()) 80 | for (row in indices) { 81 | for (column in first().indices) { 82 | cofactorMatrix[row][column] = (if ((row + column) % 2 == 0) 1 else -1) * subMatrix(row, column).determinant() 83 | } 84 | } 85 | return cofactorMatrix 86 | } 87 | return cofactor().transpose().multiply(1 / determinant()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/kotlin/util/ArrayUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import java.util.Collections 4 | 5 | object ArrayUtil { 6 | object RotationUtil { 7 | /** @param distance must be less than or equal to the length of the array. */ 8 | fun Array.rotateLeft(distance: Int) = drop(distance) + take(distance) 9 | 10 | /** @param distance must be less than or equal to the length of the array. */ 11 | fun Array.rotateRight(distance: Int) = takeLast(distance) + dropLast(distance) 12 | 13 | fun Array.rotate(distance: Int) = 14 | toList().also { // toList() is a deep copy to avoid changing the original array. 15 | Collections.rotate(it, distance) 16 | } 17 | } 18 | 19 | val pow2 = intArrayOf( 20 | 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 21 | 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 22 | 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824 23 | ) 24 | 25 | fun IntArray.swap(i: Int, j: Int) { 26 | val temp = this[i] 27 | this[i] = this[j] 28 | this[j] = temp 29 | } 30 | 31 | /** 32 | * @receiver must not be empty. 33 | */ 34 | fun IntArray.minMax(): Pair { 35 | var min = this[0] 36 | var max = this[0] 37 | for (i in 1 until size) { 38 | if (this[i] < min) { 39 | min = this[i] 40 | } else if (this[i] > max) { 41 | max = this[i] 42 | } 43 | } 44 | return min to max 45 | } 46 | 47 | val IntArray.indexOfMin: Int 48 | get() { 49 | var indexOfMin = 0 50 | for (i in 1 until size) { 51 | if (this[i] < this[indexOfMin]) indexOfMin = i 52 | } 53 | return indexOfMin 54 | } 55 | 56 | val IntArray.indexOfMax: Int 57 | get() { 58 | var indexOfMax = 0 59 | for (i in 1 until size) { 60 | if (this[i] > this[indexOfMax]) indexOfMax = i 61 | } 62 | return indexOfMax 63 | } 64 | 65 | fun IntArray.median(): Double { 66 | val i = size / 2 67 | return if (size % 2 == 0) (this[i - 1] + this[i]) / 2.0 else this[i].toDouble() 68 | } 69 | 70 | fun IntArray.mode() = toList().groupingBy { it }.eachCount().maxByOrNull { it.value }?.key 71 | 72 | fun IntArray.medianByCountingSort(): Double { 73 | fun median(onMiddleIndex: (IntArray, Int) -> Double): Double { 74 | val counts = IntArray(maxOrNull()!! + 1) 75 | for (x in this) { 76 | counts[x]++ 77 | } 78 | val indexOfMedian = size / 2 79 | var i = 0 80 | counts.forEachIndexed { j, x -> 81 | repeat(x) { 82 | this[i] = j 83 | if (i == indexOfMedian) return onMiddleIndex(this, i) 84 | i++ 85 | } 86 | } 87 | error("Unexpected to reach here.") 88 | } 89 | return if (size % 2 == 0) { 90 | median { xs, i -> (xs[i - 1] + xs[i]) / 2.0 } 91 | } else { 92 | median { xs, j -> xs[j].toDouble() } 93 | } 94 | } 95 | 96 | fun IntArray.permute(left: List = emptyList(), right: List = toList()): List> = 97 | if (right.isEmpty()) listOf(left) else right.flatMap { permute(left + it, right - it) } 98 | 99 | fun IntArray.permuteWithoutDuplicates() = permute().distinct() 100 | 101 | /** @return the number of the given element in a sorted array */ 102 | fun LongArray.count(element: Long): Int { 103 | var i = binarySearch(element) 104 | if (i < 0) return 0 105 | var count = 1 106 | var j = i 107 | while (getOrNull(--i) == element) count++ 108 | while (getOrNull(++j) == element) count++ 109 | return count 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/kotlin/data/TreeUtil.kt: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | object TreeUtil { 4 | data class Vertex(val ancestors: List, val children: List, val subtreeSum: Long) { 5 | val isRoot get() = ancestors.isEmpty() 6 | val depth get() = ancestors.size 7 | val isLeaf get() = children.isEmpty() 8 | } 9 | 10 | fun convertGraphToTree(graph: List>, data: List, root: Int): List { 11 | val ancestors = Array>(graph.size) { emptyList() } 12 | val vertices = Array(graph.size) { null } 13 | 14 | fun visitVertex(id: Int, parent: Int) { 15 | ancestors[id] = if (parent == -1) emptyList() else listOf(parent) + ancestors[parent] 16 | val children = graph[id].filter { parent != it } 17 | for (child in children) visitVertex(child, id) 18 | vertices[id] = Vertex( 19 | ancestors[id], 20 | children, 21 | data[id] + children.map { vertices[it]!!.subtreeSum }.sum() 22 | ) 23 | } 24 | 25 | visitVertex(root, -1) 26 | return vertices.filterNotNull() 27 | } 28 | 29 | /** Faster but has more code */ 30 | fun fastConvertGraphToTree(graph: List>, data: List, root: Int): List { 31 | val ancestors = Array>(graph.size) { emptyList() } 32 | val vertices = Array(graph.size) { null } 33 | 34 | fun visitVertex(id: Int, parent: Int) { 35 | ancestors[id] = listOf(parent) + ancestors[parent] 36 | val children = graph[id].filter { parent != it } 37 | for (child in children) visitVertex(child, id) 38 | vertices[id] = Vertex( 39 | ancestors[id], 40 | children, 41 | data[id] + children.map { vertices[it]!!.subtreeSum }.sum() 42 | ) 43 | } 44 | 45 | for (child in graph[root]) visitVertex(child, root) 46 | vertices[root] = Vertex( 47 | ancestors[root], 48 | graph[root], 49 | data[root] + graph[root].map { vertices[it]!!.subtreeSum }.sum() 50 | ) 51 | 52 | return vertices.filterNotNull() 53 | } 54 | 55 | /** finds the lowest common ancestor (LCA). */ 56 | fun findLCA(ancestors: List>, vertex1: Int, vertex2: Int): Int { 57 | var v1 = vertex1 58 | var v2 = vertex2 59 | while (ancestors[v1].size != ancestors[v2].size) { 60 | if (ancestors[v1].size < ancestors[v2].size) { 61 | v2 = ancestors[v2].first() 62 | } else { 63 | v1 = ancestors[v1].first() 64 | } 65 | } 66 | while (v1 != v2) { 67 | v1 = ancestors[v1].first() 68 | v2 = ancestors[v2].first() 69 | } 70 | return v1 71 | } 72 | 73 | fun isParentChildRelationship(ancestors: List>, vertex1: Int, vertex2: Int) = 74 | findLCA(ancestors, vertex1, vertex2).let { 75 | it == vertex1 || it == vertex2 76 | } 77 | 78 | /** 79 | * returns one or two roots of minimum height tree(s). 80 | * https://www.geeksforgeeks.org/roots-tree-gives-minimum-height/ 81 | */ 82 | fun findRootOfMinHeightTree(graph: List>): Set { 83 | // Early return if the graph has only one vertex because the rest of findRootOfMinHeightTree() mistakenly returns an empty set in the scenario. 84 | if (graph.size == 1) return setOf(0) 85 | var remainingVertices = graph.size 86 | val degrees = graph.map { it.size }.toMutableList() 87 | val leaves = graph.indices.filter { graph[it].size == 1 }.toMutableList() 88 | while (remainingVertices > 2) { 89 | val leafCount = leaves.size 90 | remainingVertices -= leaves.size 91 | repeat(leafCount) { 92 | val leaf = leaves.removeFirst() 93 | for (semiLeaf in graph[leaf]) { 94 | degrees[semiLeaf]-- 95 | if (degrees[semiLeaf] == 1) leaves.add(semiLeaf) 96 | } 97 | } 98 | } 99 | return leaves.toSet() 100 | } 101 | 102 | fun List.getDescendants(i: Int): List { 103 | fun getDescendants(vertex: Vertex): List { 104 | if (vertex.children.isEmpty()) return emptyList() 105 | return vertex.children + vertex.children.map { getDescendants(it) }.flatten() 106 | } 107 | return getDescendants(this[i]) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/kotlin/util/RxUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import io.reactivex.rxjava3.core.Completable 4 | import io.reactivex.rxjava3.core.Maybe 5 | import io.reactivex.rxjava3.core.Observable 6 | import io.reactivex.rxjava3.core.ObservableTransformer 7 | import io.reactivex.rxjava3.core.Observer 8 | import io.reactivex.rxjava3.core.Single 9 | import io.reactivex.rxjava3.disposables.Disposable 10 | import util.RxUtil.DoOn.doOnMisc 11 | 12 | object RxUtil { 13 | object DoOn { 14 | fun Completable.doOnMisc(): Completable = doOnSubscribe { 15 | println("👀doOnSubscribe") 16 | }.doOnComplete { 17 | println("👀doOnComplete") 18 | }.doOnError { 19 | println("👀doOnError: $it") 20 | }.doOnTerminate { 21 | println("👀doOnTerminate") 22 | }.doOnDispose { 23 | println("👀doOnDispose") 24 | } 25 | 26 | fun Single.doOnMisc(): Single = doOnSubscribe { 27 | println("👀doOnSubscribe") 28 | }.doOnSuccess { 29 | println("👀doOnSuccess: $it") 30 | }.doOnError { 31 | println("👀doOnError: $it") 32 | }.doOnTerminate { 33 | println("👀doOnTerminate") 34 | }.doOnDispose { 35 | println("👀doOnDispose") 36 | } 37 | 38 | fun Maybe.doOnMisc(): Maybe = doOnSubscribe { 39 | println("👀doOnSubscribe") 40 | }.doOnComplete { 41 | println("👀doOnComplete") 42 | }.doOnError { 43 | println("👀doOnError: $it") 44 | }.doOnTerminate { 45 | println("👀doOnTerminate") 46 | }.doOnDispose { 47 | println("👀doOnDispose") 48 | } 49 | 50 | fun Observable.doOnMisc(): Observable = doOnSubscribe { 51 | println("👀doOnSubscribe") 52 | }.doOnNext { 53 | println("👀doOnNext: $it") 54 | }.doOnComplete { 55 | println("👀doOnComplete") 56 | }.doOnError { 57 | println("👀doOnError: $it") 58 | }.doOnTerminate { 59 | println("👀doOnTerminate") 60 | }.doOnDispose { 61 | println("👀doOnDispose") 62 | } 63 | 64 | fun Disposable.print() { 65 | println("👀Disposable#isDisposed: $isDisposed") 66 | } 67 | } 68 | 69 | object SubscribeUtil { 70 | fun Completable.mySubscribe(): Disposable = 71 | subscribe({ println("CompletableObserver#onComplete") }) { println("CompletableObserver#onError: $it") } 72 | 73 | fun Single.mySubscribe(): Disposable = 74 | subscribe({ println("Observer#onSuccess: $it") }, { println("Observer#onError: $it") }) 75 | 76 | fun Maybe.mySubscribe(): Disposable = subscribe({ println("MaybeObserver#onNext: $it") }, 77 | { println("MaybeObserver#onError: $it") }, 78 | { println("MaybeObserver#onComplete") }) 79 | 80 | fun Observable.mySubscribe(): Disposable = subscribe({ println("Observer#onNext: $it") }, 81 | { println("Observer#onError: $it") }, 82 | { println("Observer#onComplete") }) 83 | 84 | fun Observable.mySubscribeWithId(id: Int) { 85 | fun createObserver(id: Int) = object : Observer { 86 | override fun onSubscribe(d: Disposable) { 87 | println("Observer[$id]#onSubscribe") 88 | } 89 | 90 | override fun onNext(item: T) { 91 | println("Observer[$id]#onNext: $item") 92 | } 93 | 94 | override fun onError(e: Throwable) { 95 | println("Observer[$id]#onError: $e") 96 | e.printStackTrace() 97 | } 98 | 99 | override fun onComplete() { 100 | println("Observer[$id]#onComplete") 101 | } 102 | } 103 | 104 | return subscribe(createObserver(id)) 105 | } 106 | } 107 | 108 | fun Observable.errorWhen(elementToBeError: T): Observable = flatMap { 109 | if (it == elementToBeError) { 110 | Observable.error(Throwable("WTF")) 111 | } else { 112 | Observable.just(it) 113 | } 114 | } 115 | 116 | /** 117 | * Unlike [Observable.error], throwing [Throwable] prevents calling [Observer.onNext] even on the previous items. 118 | */ 119 | fun Observable.errorWhen2(elementToBeError: T): Observable = map { 120 | if (it != elementToBeError) { 121 | throw Throwable("WTF") 122 | } 123 | it 124 | } 125 | 126 | private fun createObservableTransformer() = ObservableTransformer { 127 | it.doOnMisc() 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/example/ObservableExample.kt: -------------------------------------------------------------------------------- 1 | package rx.example 2 | 3 | import io.reactivex.rxjava3.core.Observable 4 | import io.reactivex.rxjava3.core.ObservableEmitter 5 | import io.reactivex.rxjava3.disposables.CompositeDisposable 6 | import io.reactivex.rxjava3.observables.ConnectableObservable 7 | import util.RxUtil.DoOn.doOnMisc 8 | import util.RxUtil.SubscribeUtil.mySubscribe 9 | import util.RxUtil.SubscribeUtil.mySubscribeWithId 10 | import util.RxUtil.errorWhen 11 | import util.RxUtil.errorWhen2 12 | import kotlin.random.Random 13 | 14 | object ObservableExample { 15 | private val compositeDisposable = CompositeDisposable() 16 | private var observerCount = 0 17 | 18 | fun example1() { 19 | println("-- " + object {}::class.java.enclosingMethod?.name + " --") 20 | Observable.just("a", "b", "c").mySubscribe() 21 | compositeDisposable.clear() 22 | } 23 | 24 | fun example2() { 25 | println("-- " + object {}::class.java.enclosingMethod?.name + " --") 26 | var emitter: ObservableEmitter? = null 27 | val observable = Observable.create { 28 | emitter = it 29 | } 30 | emitter?.onNext("a") // emitter is null because observable is not subscribed to yet. 31 | observable.mySubscribe() 32 | emitter?.onNext("b") 33 | emitter?.onComplete() 34 | } 35 | 36 | /** 37 | * Observable unicasts by default. Observable can take only one Subscriber. Registering the second Subscriber unregisters the first Subscriber. 38 | * Observable multicasts after calling share(). 39 | * 40 | * [Observable.share] is an alias for publish().refCount(). 41 | * [Observable.publish] returns a ConnectableObservable, which starts emitting items, not when subscribed, but when connect() is called. By which, a ConnectableObservable can emit items only after all the subscribers subscribe. 42 | * [ConnectableObservable.refCount] returns an Observable that will be disposed when all the subscribers are unsubscribed. 43 | * 44 | * Output: 45 | * Observer[0]#onNext: a 46 | * Observer[0]#onNext: b <- not shown because Observable can only register the last subscriber. Registering the second subscriber unregisters the first subscriber. 47 | * Observer[1]#onNext: b 48 | * Observer[2]#onNext: c 49 | * Observer[2]#onNext: d <- shown because share() is called. 50 | * Observer[3]#onNext: d 51 | */ 52 | fun example3() { 53 | println("-- " + object {}::class.java.enclosingMethod?.name + " --") 54 | var emitter: ObservableEmitter? = null 55 | val observable = Observable.create { 56 | emitter = it 57 | } 58 | 59 | observable.mySubscribeWithId(observerCount++) 60 | emitter?.onNext("a") 61 | observable.mySubscribeWithId(observerCount++) 62 | emitter?.onNext("b") 63 | 64 | // Note share(). 65 | val observable2 = Observable.create { 66 | emitter = it 67 | }.share() 68 | observable2.mySubscribeWithId(observerCount++) 69 | emitter?.onNext("c") 70 | observable2.mySubscribeWithId(observerCount++) 71 | emitter?.onNext("d") 72 | } 73 | 74 | /** 75 | * Example of Observable.empty() 76 | */ 77 | fun observableEmptyExample() { 78 | val disposable = Observable 79 | .just("a", "b", "c") 80 | .flatMap { 81 | if (it == "b") Observable.empty() else Observable.just(it) 82 | } 83 | .doOnMisc() 84 | .mySubscribe() 85 | println(disposable.isDisposed) 86 | } 87 | 88 | fun errorExample1() { 89 | println("-- " + object {}::class.java.enclosingMethod?.name + " --") 90 | Observable 91 | .just("a", "b", "c") 92 | .errorWhen("b") 93 | .mySubscribe() 94 | compositeDisposable.clear() 95 | } 96 | 97 | fun errorExample2() { 98 | println("-- " + object {}::class.java.enclosingMethod?.name + " --") 99 | Observable.just("a", "b", "c") 100 | .errorWhen2("b") 101 | .mySubscribe() 102 | compositeDisposable.clear() 103 | } 104 | 105 | fun errorExample3() { 106 | val observable = 107 | Observable.just("a", "b", "c") 108 | .errorWhen("b") 109 | .onErrorResumeNext { 110 | println("Observable#onErrorResumeNext: $it") 111 | Observable.just("z") 112 | } 113 | observable.mySubscribe() 114 | observable.mySubscribe() 115 | compositeDisposable.clear() 116 | } 117 | 118 | fun retrySample() { 119 | Observable 120 | .just(Unit) 121 | .flatMap { 122 | if (Random.nextBoolean()) { 123 | Observable.just(Unit) 124 | } else { 125 | Observable.error(Throwable()) 126 | } 127 | }.retryWhen { 128 | it.flatMap { 129 | println("retryWhen") 130 | Observable.just(Unit) 131 | } 132 | } 133 | .subscribe { 134 | println("onNext") 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/kotlin/util/MathUtil.kt: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import util.NumberUtil.factorial 4 | import util.NumberUtil.nCr 5 | import kotlin.math.E 6 | import kotlin.math.pow 7 | import kotlin.math.sqrt 8 | 9 | object MathUtil { 10 | val fibonacci = generateSequence(0 to 1) { it.second to it.first + it.second }.map { it.first } 11 | fun mode(xs: Collection) = xs.groupingBy { it }.eachCount().maxByOrNull { it.value }?.key 12 | fun weightedMean(xs: Collection, weights: Collection) = xs.zip(weights) { x, weight -> x * weight }.sum().toDouble() / weights.sum() 13 | 14 | /** 15 | * @param xs must be sorted. 16 | */ 17 | fun getMedian(xs: List): Double { 18 | val i = xs.size / 2 19 | return if (xs.size % 2 == 0) (xs[i - 1] + xs[i]) / 2.0 else xs[i].toDouble() 20 | } 21 | 22 | fun getStandardDeviation(xs: Collection): Double { 23 | val mean = xs.average() 24 | return sqrt(xs.sumOf { (it - mean).pow(2) } / xs.size) 25 | } 26 | 27 | @JvmName("standardDeviationDouble") 28 | fun getStandardDeviation(xs: Collection): Double { 29 | val mean = xs.average() 30 | return sqrt(xs.sumOf { (it - mean).pow(2) } / xs.size) 31 | } 32 | 33 | fun getCovariance(xs: Collection, ys: Collection): Double { 34 | val meanX = xs.average() 35 | val meanY = ys.average() 36 | return xs.zip(ys) { x, y -> (x - meanX) * (y - meanY) }.sum() / xs.size 37 | } 38 | 39 | @JvmName("covarianceDouble") 40 | fun getCovariance(xs: Collection, ys: Collection): Double { 41 | val meanX = xs.average() 42 | val meanY = ys.average() 43 | return xs.zip(ys) { x, y -> (x - meanX) * (y - meanY) }.sum() / xs.size 44 | } 45 | 46 | fun correlationCoefficient(xs: Collection, ys: Collection) = getCovariance(xs, ys) / (getStandardDeviation(xs) * getStandardDeviation(ys)) 47 | 48 | @JvmName("correlationCoefficientDouble") 49 | fun correlationCoefficient(xs: Collection, ys: Collection) = getCovariance(xs, ys) / (getStandardDeviation(xs) * getStandardDeviation(ys)) 50 | 51 | /** 52 | * @param p probability of success 53 | * @param n number of trials 54 | * @param k number of successes 55 | * @return probability of [k] successes on [n] trials 56 | */ 57 | fun binomialProbability(p: Double, n: Int, k: Int) = nCr(n, k) * p.pow(k) * (1 - p).pow(n - k) 58 | 59 | /** 60 | * @param p probability of success 61 | * @param n number of trials 62 | * @param ks number of successes 63 | * @return probability of at least [ks] successes on [n] trials 64 | * e.g. If [n] is 6 and [ks] is [4, 5, 6], the returned value is the probability of 4 successes + probability of 5 successes + probability of 6 successes. 65 | * i.e. probability of at least 6 successes 66 | */ 67 | fun cumulativeBinomialProbability(p: Double, n: Int, ks: IntRange) = ks.sumOf { binomialProbability(p, n, it) } 68 | 69 | /** 70 | * Redundant wrapper for geometric probability 71 | * @param p probability of success 72 | * @param k number of trials 73 | * @return probability of the first success on the [k]th trial. 74 | */ 75 | fun firstSuccessOnKthTrialProbability(p: Double, k: Int) = (1 - p).pow(k - 1) * p 76 | 77 | /** 78 | * Redundant wrapper for geometric probability 79 | * @param p probability of success 80 | * @param k number of trials 81 | * @return probability that all the trials fail 82 | */ 83 | fun allTrialsFailProbability(p: Double, k: Int) = (1 - p).pow(k) 84 | 85 | /** 86 | * @param mean average number of successes 87 | * @param k actual number of successes 88 | */ 89 | fun poissonProbability(mean: Double, k: Int) = mean.pow(k) * E.pow(-mean) / k.factorial() 90 | 91 | /** 92 | * E\[X] in the Poisson distribution = [mean] (average number of successes) 93 | * Var(X) in the Poisson distribution = [mean] (actual number of successes) 94 | * 95 | * Var(X) = E[X^2] - E\[X]^2 96 | * -> E[X^2] = Var(X) + E\[X]^2 = [mean] + [mean]^2 97 | */ 98 | fun squaredPoissonProbability(mean: Double) = mean + mean.pow(2) 99 | 100 | /** 101 | * If the number of elements is odd, the median element is excluded. 102 | * @receiver must be sorted. 103 | */ 104 | fun firstQuartile(xs: Collection) = getMedian(xs.take(xs.size / 2)) 105 | 106 | /** 107 | * If the number of elements is odd, the median element is excluded. 108 | * @receiver must be sorted. 109 | */ 110 | fun thirdQuartile(xs: List) = getMedian(xs.takeLast(xs.size / 2)) 111 | 112 | /** 113 | * @receiver must be sorted. 114 | */ 115 | fun interquartileRange(xs: List) = thirdQuartile(xs) - firstQuartile(xs) 116 | 117 | /** 118 | * Spearman's rank correlation coefficient 119 | */ 120 | fun spearman(xs: List, ys: List): Double { 121 | fun List.rank(): List { 122 | val sorted = sorted() 123 | return map { sorted.indexOf(it) + 1 } 124 | } 125 | 126 | val n = xs.size 127 | return 1 - (6 * xs.rank().zip(ys.rank()) { rankX, rankY -> (rankX - rankY).toDouble().pow(2) }.sum() / (n * (n * n - 1))) 128 | } 129 | 130 | fun cartesianProduct(vararg sets: Set): Set> { 131 | require(sets.size >= 2) { "Cartesian product requires at least two sets." } 132 | return sets.fold(setOf(emptyList())) { acc, value -> 133 | acc.flatMap { set -> value.map { set + it } }.toSet() 134 | } 135 | } 136 | } 137 | 138 | private fun main() { 139 | val set1 = setOf(1, 2) 140 | val set2 = setOf("a", "b") 141 | val set3 = setOf(true, false) 142 | val result = MathUtil.cartesianProduct(set1, set2, set3) 143 | println(result) // [[1, a, true], [1, a, false], [1, b, true], [1, b, false], [2, a, true], [2, a, false], [2, b, true], [2, b, false]] 144 | } 145 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/example/SubjectExample.kt: -------------------------------------------------------------------------------- 1 | package rx.example 2 | 3 | import io.reactivex.rxjava3.core.Completable 4 | import io.reactivex.rxjava3.core.Maybe 5 | import io.reactivex.rxjava3.core.Observable 6 | import io.reactivex.rxjava3.core.ObservableEmitter 7 | import io.reactivex.rxjava3.core.Single 8 | import io.reactivex.rxjava3.disposables.Disposable 9 | import io.reactivex.rxjava3.subjects.AsyncSubject 10 | import io.reactivex.rxjava3.subjects.BehaviorSubject 11 | import io.reactivex.rxjava3.subjects.PublishSubject 12 | import io.reactivex.rxjava3.subjects.ReplaySubject 13 | import io.reactivex.rxjava3.subjects.Subject 14 | import util.RxUtil.DoOn.doOnMisc 15 | import util.RxUtil.DoOn.print 16 | import util.RxUtil.SubscribeUtil.mySubscribe 17 | 18 | object SubjectExample { 19 | /** 20 | * It shows the difference among several types of subjects. 21 | * AsyncSubject emits the last value just after onComplete(). 22 | * PublishSubject ignores past emissions before the subscription. 23 | * BehaviorSubject takes the last emission before the subscription. 24 | * ReplySubject ignores all the past emissions before the subscription. 25 | */ 26 | fun example1() { 27 | fun demo(subject: Subject) { 28 | val observable = subject.hide() 29 | subject.onNext("a") 30 | subject.onNext("b") 31 | observable.mySubscribe() 32 | subject.onNext("c") 33 | subject.onNext("d") 34 | subject.onComplete() // Without onComplete(), AsyncSubject will never emit a value. 35 | } 36 | println("-- AsyncSubject --") 37 | demo(AsyncSubject.create()) 38 | println("-- PublishSubject --") 39 | demo(PublishSubject.create()) 40 | println("-- BehaviorSubject --") 41 | demo(BehaviorSubject.create()) 42 | println("-- ReplySubject --") 43 | demo(ReplaySubject.create()) 44 | } 45 | 46 | /** 47 | * Example of calling Observer.onError() 48 | */ 49 | fun example2() { 50 | val subject = PublishSubject.create() 51 | subject 52 | .hide() 53 | .flatMap { 54 | Observable.error(Throwable("WTF")) 55 | } 56 | .mySubscribe() 57 | subject.onNext("a") 58 | subject.onNext("b") 59 | } 60 | 61 | /** 62 | * Example of Observable.onErrorResumeNext(), which calls onComplete() instead of onError(). 63 | */ 64 | fun example3() { 65 | val subject = PublishSubject.create() 66 | subject 67 | .hide() 68 | .flatMap { 69 | Observable.error(Throwable("WTF")) 70 | } 71 | .onErrorResumeNext { 72 | println("onErrorResumeNext (Immediately afterwards, Observer.onNext() and Observer.onComplete() will be called instead of Observer.onError(): $it") 73 | Observable.just("phoenix") 74 | } 75 | .mySubscribe() 76 | subject.onNext("a") 77 | subject.onNext("b") 78 | } 79 | 80 | /** 81 | * This example shows that when [Observable.onErrorResumeNext] is inside [Observable.flatMap], the outer [Observable] continues as if no error occurred. 82 | */ 83 | fun example4() { 84 | val subject = PublishSubject.create() 85 | subject 86 | .hide() 87 | .flatMap { 88 | Observable 89 | .error(Throwable("WTF")) 90 | .onErrorResumeNext { 91 | println("onErrorResumeNext (Because this onErrorResumeNext is inside flatMap, the outer Observable will continue as if no error occurred.)") 92 | Observable.just("phoenix") 93 | } 94 | } 95 | .mySubscribe() 96 | subject.onNext("a") 97 | subject.onNext("b") 98 | } 99 | 100 | /** 101 | * This example shows that even after a subscription's [Disposable] is disposed (e.g. by onError()), 102 | * you can subscribe to the same [Observable] and it works as if no error occurred before. 103 | */ 104 | fun example5() { 105 | val subject = PublishSubject.create() 106 | val observable = subject.hide() 107 | val disposable = observable 108 | .flatMap { 109 | Observable.error(Throwable("WTF")) 110 | } 111 | .onErrorResumeNext { 112 | println("onErrorResumeNext (Immediately afterwards, Observer.onNext() and Observer.onComplete() will be called instead of Observer.onError(): $it") 113 | Observable.just("phoenix") 114 | } 115 | .mySubscribe() 116 | subject.onNext("a") 117 | subject.onNext("b") 118 | disposable.print() // true 119 | val disposable2 = observable.mySubscribe() 120 | subject.onNext("c") 121 | subject.onNext("d") 122 | disposable2.print() // false 123 | subject.onComplete() 124 | disposable2.print() // true 125 | } 126 | 127 | /** Example of converting Observable to Completable */ 128 | fun example6() { 129 | val subject = PublishSubject.create() 130 | val completable = Completable.fromObservable(subject.hide()) 131 | val disposable = completable.mySubscribe() 132 | subject.onComplete() 133 | disposable.print() // true 134 | } 135 | 136 | /** Example of converting Observable to Maybe */ 137 | fun example7() { 138 | val subject = PublishSubject.create() 139 | val maybe = Maybe.fromObservable(subject.hide()) 140 | val disposable = maybe.mySubscribe() 141 | subject.onNext("a") 142 | subject.onComplete() 143 | disposable.print() // true 144 | } 145 | 146 | /** Example of converting Observable to Single */ 147 | fun example8() { 148 | val subject = PublishSubject.create() 149 | val single = 150 | Single.fromObservable(subject.hide()) // Unlike Observable.single(), you don't have to set the default value. 151 | val disposable = single.mySubscribe() 152 | subject.onNext("a") 153 | subject.onComplete() 154 | disposable.print() // true 155 | } 156 | 157 | /** Example of Observable.retry() */ 158 | fun example9() { 159 | var firstRun = true 160 | val subject = PublishSubject.create() 161 | val observable = subject 162 | .hide() 163 | .doOnMisc() 164 | .flatMap { 165 | if (firstRun) { 166 | firstRun = false 167 | Observable.error(Throwable("WTF")) 168 | } else { 169 | Observable.just(it) 170 | } 171 | } 172 | .retry() 173 | val disposable = observable.mySubscribe() 174 | subject.onNext("a") 175 | subject.onNext("b") 176 | subject.onComplete() 177 | disposable.print() // true 178 | } 179 | 180 | /** demonstrates that Subject multicasts. */ 181 | fun example10() { 182 | val subject = PublishSubject.create() 183 | val observable = subject.hide() 184 | 185 | observable.mySubscribe() 186 | observable.mySubscribe() 187 | 188 | subject.onNext("a") 189 | subject.onNext("b") 190 | } 191 | 192 | /** demonstrates that ObservableEmitter uni-casts. */ 193 | fun example11() { 194 | lateinit var emitter: ObservableEmitter 195 | val observable = Observable.create { 196 | emitter = it 197 | } 198 | 199 | observable.mySubscribe() 200 | observable.mySubscribe() 201 | 202 | emitter.onNext("a") 203 | emitter.onNext("b") 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /markdown/coroutines-flow.md: -------------------------------------------------------------------------------- 1 | # [CancellationException](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/) 2 | 3 | If you want to catch a `CancellationException`, rethrow it. 4 | 5 | ### References 6 | 7 | > The same problem can be observed by catching a CancellationException and not rethrowing it: 8 | 9 | > While catching Exception is an anti-pattern, this issue may surface in more subtle ways, like when using the runCatching function, which does not rethrow CancellationException. 10 | 11 | https://kotlinlang.org/docs/cancellation-and-timeouts.html#cancellation-is-cooperative 12 | 13 | > Using `runCatching` increases this risk of mis-handling cancellation. If you catch and don't rethrow all the `CancellationException`, your coroutines are not cancelled even if you cancel their `CoroutineScope`. 14 | > 15 | > This can very easily lead to: 16 | > - unexpected crashes 17 | > - extremely hard to diagnose bugs 18 | > - memory leaks 19 | > - performance issues 20 | > - battery drain 21 | 22 | https://detekt.dev/docs/rules/coroutines/#suspendfunswallowedcancellation 23 | 24 | https://github.com/Kotlin/kotlinx.coroutines/issues/1814 25 | 26 | https://stackoverflow.com/a/78683217/10867055 27 | 28 | ## How should you rethrow [CancellationException](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/)? 29 | 30 | There are two options. 31 | 32 | ### Option 1: Use `throw` 33 | ### Option 2: Use [ensureActive](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/ensure-active.html) 34 | - Pros 35 | - https://betterprogramming.pub/the-silent-killer-thats-crashing-your-coroutines-9171d1e8f79b 36 | - https://github.com/Kotlin/kotlinx.coroutines/issues/3658#issuecomment-1465747377 37 | - https://github.com/Kotlin/kotlinx.coroutines/issues/1814#issuecomment-1943224856 38 | - https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html 39 | - Cons 40 | - https://github.com/Kotlin/kotlinx.coroutines/issues/3658#issuecomment-1527096439 41 | 42 | # Flow's `count()`, `toList()`, `drop()`, `take()`, `first()`, and `single()` 43 | 44 | ```kotlin 45 | val flow = flowOf("a", "b", "c") 46 | 47 | println(flow.count()) // 3 48 | println(flow.count { it == "b" }) // 1 49 | println(flow.toList()) // [a, b, c] 50 | println(flow.drop(1).take(1).toList()) // [b] 51 | println(flow.first()) // a 52 | println(flow.single()) // IllegalArgumentException: Flow has more than one element 53 | ``` 54 | 55 | # Four ways to update the value of `MutableStateFlow` 56 | 57 | ```kotlin 58 | val _stateFlow = MutableStateFlow("") 59 | val stateFlow = _stateFlow.asStateFlow() 60 | 61 | _stateFlow.tryEmit("a") 62 | println(stateFlow.value) // a 63 | 64 | coroutineScope { _stateFlow.emit("b") } 65 | println(stateFlow.value) // b 66 | 67 | _stateFlow.update { "c" } 68 | println(stateFlow.value) // c 69 | 70 | _stateFlow.value = "d" 71 | println(stateFlow.value) // d 72 | ``` 73 | 74 | # `StateFlow` is preferred over `SharedFlow` 75 | 76 | > The recommended way to expose a Flow from a ViewModel is with a StateFlow 77 | 78 | https://developer.android.com/codelabs/basic-android-kotlin-compose-update-data-room#2 79 | 80 | > There are more types of flows, but this is what we recommend because we can optimize StateFlow very precisely. 81 | 82 | https://youtu.be/fSB6_KE95bU?t=1001 83 | 84 | # `repeatOnLifecycle()` is preferred over `asLiveData()` or `flowWithLifecycle()` 85 | 86 | https://youtu.be/fSB6_KE95bU?t=610 87 | 88 | # Even with the `catch` operator, the backing Flow still terminates. 89 | 90 | > Note: The catch operator only prevents the exception from crashing the app, the backing Flow still terminates. To 91 | > resume collecting from the flow after the exception, consider the retry method. 92 | 93 | # Actual time versus virtual time with `delay` in `runTest` 94 | 95 | runTest | Actual time to advance | Virtual time to advance 96 | -----------------------------------------------------------------------------------------------------|------------------------|------------------------- 97 | `delay(1_000)` not in `launch` | 0s | 0s 98 | `delay(1_000)` in `launch` | 0s | 1s 99 | `delay(1_000)` in a dispatcher that doesn't use `TestCoroutineScheduler` (e.g. using `withContext`) | 1s | 1s 100 | 101 | # Difference among PublishSubject, BehaviorSubject, MutableSharedFlow, and MutableStateFlow 102 | 103 |   | PublishSubject
.create | BehaviorSubject
.create | BehaviorSubject
.createDefault | MutableSharedFlow | MutableSharedFlow
(replay=1) | MutableStateFlow 104 | ------------------------------------------------------|---------------------------|----------------------------|-----------------------------------|-------------------|---------------------------------|------------------ 105 | Requires a initial value | FALSE | FALSE | TRUE | FALSE | FALSE | TRUE 106 | Can collect the latest value when a collector starts | FALSE | TRUE | TRUE | FALSE | TRUE | TRUE 107 | Can emit the same value as the previous one | TRUE | TRUE | TRUE | TRUE | TRUE | FALSE 108 | 109 | > A SharedFlow is a highly-configurable generalization of StateFlow. 110 | 111 | https://developer.android.com/kotlin/flow/stateflow-and-sharedflow#sharedflow 112 | 113 | # Cold stream versus hot stream (Cold flow versus hot flow) 114 | 115 | Cold stream | Hot stream 116 | ------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------- 117 | is like an automatic faucet. It emits values only when a new consumer starts collecting. | is like a normal faucet. It emits values without a new consumer starts collecting.
is like a hot spring. It gushes out whether or not any user exists. 118 | e.g. (regular) flow | e.g. Channel, StateFlow, LiveData 119 | 120 | The analogy for a hot stream is a hot spring. It gushes out whether a user exists. 121 | 122 | # SupervisorJob 123 | 124 | - If a child job throws an exception other than CancellationException, its parent and sibling jobs are NOT canceled. 125 | - i.e. Failure propagates downward only. 126 | 127 | ``` 128 | SupervisorJobA 129 | | | 130 | JobB JobC 131 | | 132 | JobD 133 | ``` 134 | 135 |   | If JobB is cancelled (i.e. throws CancellationException) ... | If JobB throws an exception other than CancellationException... 136 | --------|---------------------------------------------------------|----------------------------------------------------------------- 137 | JobA | will NOT cancel. | will cancel. 138 | JobC | will cancel. | will cancel. 139 | JobD | will cancel. | will cancel. 140 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | 118 | 119 | # Determine the Java command to use to start the JVM. 120 | if [ -n "$JAVA_HOME" ] ; then 121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 122 | # IBM's JDK on AIX uses strange locations for the executables 123 | JAVACMD=$JAVA_HOME/jre/sh/java 124 | else 125 | JAVACMD=$JAVA_HOME/bin/java 126 | fi 127 | if [ ! -x "$JAVACMD" ] ; then 128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 129 | 130 | Please set the JAVA_HOME variable in your environment to match the 131 | location of your Java installation." 132 | fi 133 | else 134 | JAVACMD=java 135 | if ! command -v java >/dev/null 2>&1 136 | then 137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 138 | 139 | Please set the JAVA_HOME variable in your environment to match the 140 | location of your Java installation." 141 | fi 142 | fi 143 | 144 | # Increase the maximum file descriptors if we can. 145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 146 | case $MAX_FD in #( 147 | max*) 148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 149 | # shellcheck disable=SC2039,SC3045 150 | MAX_FD=$( ulimit -H -n ) || 151 | warn "Could not query maximum file descriptor limit" 152 | esac 153 | case $MAX_FD in #( 154 | '' | soft) :;; #( 155 | *) 156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 157 | # shellcheck disable=SC2039,SC3045 158 | ulimit -n "$MAX_FD" || 159 | warn "Could not set maximum file descriptor limit to $MAX_FD" 160 | esac 161 | fi 162 | 163 | # Collect all arguments for the java command, stacking in reverse order: 164 | # * args from the command line 165 | # * the main class name 166 | # * -classpath 167 | # * -D...appname settings 168 | # * --module-path (only if needed) 169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 170 | 171 | # For Cygwin or MSYS, switch paths to Windows format before running java 172 | if "$cygwin" || "$msys" ; then 173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Official style guides 2 | 3 | * https://developer.android.com/kotlin/style-guide 4 | * https://kotlinlang.org/docs/reference/coding-conventions.html 5 | 6 | # Iterable 7 | 8 | ## How to iterate through an Iterable with index 9 | 10 | ```kotlin 11 | val xs: List = listOf('a', 'b', 'c') 12 | for ((i, x) in xs.withIndex()) println("$i $x") 13 | 14 | // Alternatively 15 | xs.forEachIndexed { i, x -> println("$i $x") } 16 | 17 | // Alternatively 18 | xs.mapIndexed { i, x -> "$i $x" }.forEach(::println) 19 | ``` 20 | 21 | ## How to filter an Iterable by index 22 | 23 | ```kotlin 24 | val xs: List = listOf('a', 'b', 'c').filterIndexed { i, _ -> i != 1 } // [a, c] 25 | ``` 26 | 27 | ## How to iterate two iterables in parallel 28 | 29 | ```kotlin 30 | // > The returned list has length of the shortest collection. 31 | // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/zip.html 32 | fun showZipExample(xs: List, ys: List) { 33 | xs.zip(ys).forEach { (x, y) -> 34 | println("$x$y") 35 | } 36 | } 37 | 38 | fun main() { 39 | showZipExample(listOf("A", "B"), listOf("a", "b")) // Aa Bb 40 | showZipExample(listOf("A", "B", "C"), listOf("a", "b")) // Aa Bb 41 | showZipExample(listOf("A", "B"), listOf("a", "b", "c")) // Aa Bb 42 | } 43 | ``` 44 | 45 | # Array 46 | 47 | ## Type mapping between Kotlin and Java 48 | 49 | Kotlin | Java 50 | -------------|----------- 51 | IntArray | int[] 52 | Array\ | Integer[] 53 | 54 | ## How to create an IntArray 55 | 56 | ```kotlin 57 | val xs: IntArray = intArrayOf(1, 2, 3) // [1, 2, 3] 58 | val ys: IntArray = IntArray(3) // [0, 0, 0] 59 | val zs: IntArray = IntArray(3) { it * 2 } // [0, 2, 4] 60 | ``` 61 | 62 | ## How to create an Array\ 63 | 64 | ```kotlin 65 | val xs: Array = arrayOf(1, 2, 3) // [1, 2, 3] 66 | 67 | // The lambda is not optional. 68 | val ys: Array = Array(3) { it } // [0, 1, 2] 69 | val zs: Array = Array(3) { "item$it" } // ["item0", "item1", "item2"] 70 | ``` 71 | 72 | ## How to convert an IntArray an to Array\ 73 | 74 | ```kotlin 75 | val xs: IntArray = intArrayOf(1, 2, 3) 76 | val ys: Array = xs.toTypedArray() 77 | ``` 78 | 79 | ## How to convert an Array\ to an IntArray 80 | 81 | ```kotlin 82 | val xs: Array = arrayOf(1, 2, 3) 83 | val ys: IntArray = xs.toIntArray() 84 | ``` 85 | 86 | ## How to iterate through an IntArray in reverse order 87 | 88 | ```kotlin 89 | val xs: IntArray = intArrayOf(1, 2, 3) 90 | for (i in xs.lastIndex downTo 0) println(xs[i]) // 3 2 1 91 | ``` 92 | 93 | ## How to iterate through an Array\ in reverse order 94 | 95 | ```kotlin 96 | val xs: Array = arrayOf("a", "b", "c") 97 | for (i in xs.lastIndex downTo 0) println(xs[i]) // c b a 98 | ``` 99 | 100 | ## How to deep-copy an IntArray 101 | 102 | ```kotlin 103 | val xs: IntArray = intArrayOf(1, 2, 3) 104 | val ys: IntArray = xs.clone() 105 | ``` 106 | 107 | ## How to deep-copy an Array\ 108 | 109 | ```kotlin 110 | val xs: Array = arrayOf("a", "b", "c") 111 | val ys: Array = xs.clone() 112 | ``` 113 | 114 | # List 115 | 116 | ## How to create a List 117 | 118 | ```kotlin 119 | val a: List = emptyList() // equivalent of listOf(). []. 120 | val b: List = listOf(1, 2, 3) // [1, 2, 3] 121 | val c: List = listOfNotNull(1, null, 2) // [1, 2] 122 | val d: List = List(3) { it } // The lambda is not optional. [0, 1, 2] 123 | val e: List = List(3) { "item$it" } // ["item0", "item1", "item2"] 124 | ``` 125 | 126 | ## How to get the size of a List 127 | 128 | ```kotlin 129 | val xs: List = listOf(1, 2, 3) 130 | val size: Int = xs.size // 3. simpler than xs.count(). 131 | ``` 132 | 133 | ## How to iterate through a List in reverse order 134 | 135 | ```kotlin 136 | val xs = listOf("a", "b", "c") 137 | for (i in xs.lastIndex downTo 0) println(i) // 2 1 0 138 | ``` 139 | 140 | ## How to add/remove an element to/from a List 141 | 142 | ```kotlin 143 | val xs: List = listOf(1, 2, 3) 144 | val result1: List = xs.plus(4) // [1, 2, 3, 4] 145 | val result2: List = xs.minus(2) // [1, 3] 146 | ``` 147 | 148 | ## How to use `onEach` and `forEach` 149 | 150 | ```kotlin 151 | listOf('a', 'b', 'c') 152 | .onEach { println(it) } // a b c 153 | .map(Char::uppercaseChar) 154 | .forEach { println(it) } // A B C 155 | ``` 156 | 157 | ## How to filter a List 158 | 159 | ```kotlin 160 | val xs: List = listOf(1, 2, 3) 161 | val result1: List = xs.filter { it == 2 } // [2] 162 | val result2: List = xs.filterNot { it == 2 } // [1, 3] 163 | val ys: List = listOf(1, null, 2) 164 | val result3: List = ys.filterNotNull() // [1, 2] 165 | val result4: List = ys.mapNotNull { it.takeIf { it == 2 } } // [2] 166 | ``` 167 | 168 | ## ifEmpty() / takeIf() / takeUnless() 169 | 170 | ```kotlin 171 | val x: List = emptyList().ifEmpty { listOf(42) } // 42 172 | val y: List? = listOf(1, 2, 3).takeIf { 0 in it } // null 173 | val z: List? = listOf(1, 2, 3).takeUnless { 1 in it } // null 174 | ``` 175 | 176 | ## How to work with two List(s) 177 | 178 | ```kotlin 179 | val xs: List = listOf(1, 2, 3) 180 | val ys: List = listOf(2, 3, 4) 181 | val result1: List = xs + ys // [1, 2, 3, 2, 3, 4] 182 | val result2: Set = xs union ys // [1, 2, 3, 4] 183 | val result3: Set = xs intersect ys // [2, 3] 184 | val result4: Set = xs subtract ys // [1] 185 | ``` 186 | 187 | ## How to sort a List 188 | 189 | ```kotlin 190 | data class Sample(val name: String) 191 | ``` 192 | 193 | ```kotlin 194 | val xs: List = listOf(1, 3, 2).sorted() // [1, 2, 3] 195 | val ys: List = listOf(1, 3, 2).sortedDescending() // [3, 2, 1] 196 | val zs: List = listOf( 197 | Sample("c"), 198 | Sample("b"), 199 | Sample("a") 200 | ).sortedBy { it.name } // [Sample(name=a), Sample(name=b), Sample(name=c)] 201 | ``` 202 | 203 | ## How to count the appearance of elements 204 | 205 | ```kotlin 206 | val occurrences: Map = 207 | listOf("b", "c", "b", "c", "a", "c").groupingBy { it }.eachCount() // {a=1, b=2, c=3} 208 | ``` 209 | 210 | ## How to convert a List? to a List 211 | 212 | ```kotlin 213 | val xs: List? = null 214 | val ys: List = x.orEmpty() 215 | ``` 216 | 217 | ## How to convert a List to a Map 218 | 219 | ```kotlin 220 | // data class Fruit(val name: String, val price: Int) 221 | val fruits: List = listOf(Fruit(name = "apple", price = 100), Fruit(name = "orange", price = 200)) 222 | val result: Map = fruits.associate { it.name to it.price } // {apple=100, orange=200} 223 | ``` 224 | 225 | ```kotlin 226 | val xs: List = listOf('a', 'b', 'c') 227 | val result: Map = xs.withIndex().associate { it.value to it.index } // {a=0, b=1, c=2} 228 | ``` 229 | 230 | ```kotlin 231 | val xs: List = listOf('a', 'b', 'c') 232 | val result: Map = xs.associateWith { it.uppercaseChar() } // {a=A, b=B, c=C} 233 | ``` 234 | 235 | ```kotlin 236 | val xs: List = listOf('a', 'b', 'c') 237 | val result: Map = xs.associateBy { it.uppercaseChar() } // {A=a, B=b, C=c} 238 | ``` 239 | 240 | ```kotlin 241 | val xs: List = listOf("fruit" to "apple", "fruit" to "orange", "vegetable" to "carrot", "vegetable" to "potato") 242 | val result: Map> = 243 | xs.groupBy({ it.first }, { it.second }) // {fruit=[apple, orange], vegetable=[carrot, potato]} 244 | ``` 245 | 246 | ```kotlin 247 | val xs: List = listOf("key1", "key2", "key1", "key2") 248 | val result: Map> = 249 | xs.mapIndexed { i, x -> x to i }.groupBy({ it.first }, { it.second }) // {key1=[0, 2], key2=[1, 3]} 250 | ``` 251 | 252 | ## How to convert two List(s) to a List 253 | 254 | ```kotlin 255 | val xs: List = listOf(1, 2, 3) 256 | val ys: List = listOf("a", "b", "c") 257 | val zs: List = xs.zip(ys).map { (x, y) -> 258 | x.toString() + y 259 | } // [1a, 2b, 3c] 260 | ``` 261 | 262 | ## How to convert two List(s) to a Map 263 | 264 | ```kotlin 265 | val xs: List = listOf(1, 2, 3) 266 | val ys: List = listOf("a", "b", "c") 267 | val zs: Map = xs.zip(ys).toMap() // {1=a, 2=b, 3=c} 268 | ``` 269 | 270 | ## How to convert a List to a Pair 271 | 272 | ```kotlin 273 | val pair: Pair = listOf("a", "b", "c").zipWithNext().first() // (a, b) 274 | ``` 275 | 276 | ## How to deep-copy a MutableList 277 | 278 | ```kotlin 279 | val xs: MutableList = mutableListOf("a", "b") 280 | val ys: MutableList = xs.toMutableList() 281 | ys[1] = "c" 282 | println(xs) // [a, b] 283 | println(ys) // [a, c] 284 | ``` 285 | 286 | ## How to destruct a List to values 287 | 288 | ```kotlin 289 | val (a, b) = listOf("a", "b", "c") 290 | ``` 291 | 292 | # Set 293 | 294 | ## How to create a Set 295 | 296 | ```kotlin 297 | val set1: Set = setOf("a", "b", "b") // [a, b] 298 | val set2: Set = setOfNotNull(null, "a", "b", "b", null) // [a, b] 299 | ``` 300 | 301 | ## How to convert a List to a SortedSet 302 | 303 | ```kotlin 304 | enum class Fruit { 305 | APPLE, ORANGE 306 | } 307 | 308 | fun main() { 309 | val xs: SortedSet = listOf("c", "b", "a").toSortedSet() 310 | println(xs) // [a, b, c] 311 | 312 | val ys: SortedSet = listOf(Fruit.ORANGE, Fruit.APPLE).toSortedSet(compareBy { it.ordinal }) 313 | println(ys) // [APPLE, ORANGE] 314 | } 315 | ``` 316 | 317 | ## SortedSet 318 | ### How to sort a Set 319 | ```kotlin 320 | println(sortedSetOf("10", "1", "2")) // [1, 10, 2] 321 | println(sortedSetOf(comparator = reverseOrder(), "10", "1", "2")) // [2, 10, 1] 322 | println(sortedSetOf(comparator = compareBy { it.toInt() }, "10", "1", "2")) // [1, 2, 10] 323 | ``` 324 | 325 | ### How to convert a Set to a SortedSet 326 | ```kotlin 327 | val set = setOf("10", "1", "2") 328 | println(set.toSortedSet()) // [1, 10, 2] 329 | println(set.toSortedSet(comparator = reverseOrder())) // [2, 10, 1] 330 | println(set.toSortedSet(comparator = compareBy { it.toInt() })) // [1, 2, 10] 331 | ``` 332 | 333 | # Map 334 | 335 | ## How to create a Map 336 | 337 | ```kotlin 338 | val map: Map = mapOf("a" to "x", "b" to "y", "c" to "z").withDefault { "default" } 339 | val result1: Set> = map.entries // [a=x, b=y, c=z] 340 | val result2: Set = map.keys // [a, b, c] 341 | val result3: Collection = map.values // [x, y, z] 342 | val result4: String? = map["d"] // null 343 | val result5: String = map.getValue("d") // "default" 344 | val result6: String = map.getOrDefault("d", "not found") // not found 345 | val result7: String = map.getOrElse("d") { "not found" } // not found 346 | ``` 347 | 348 | ## How to map a Map 349 | 350 | ```kotlin 351 | val map: Map = mapOf('a' to 10, 'b' to 20) 352 | val result1: Map = map.mapKeys { it.key.uppercaseChar() } // {A=10, B=20} 353 | val result2: Map = map.mapValues { it.value * 10 } // {a=100, b=200} 354 | val result3: Map = map.map { it.key.uppercaseChar() to it.value * 10 }.toMap() // {A=100, B=200} 355 | ``` 356 | 357 | ## How to filter a Map 358 | 359 | ```kotlin 360 | val map: Map = mapOf('A' to 'a', 'B' to 'b') 361 | val result1: Map = map.filterKeys { it == 'A' } // {A=a} 362 | val result2: Map = map.filterValues { it == 'b' } // {B=b} 363 | val result3: Map = map.filter { it.key == 'B' && it.value == 'b' } // {B=b} 364 | ``` 365 | 366 | ## How to flatten a list of Map(s) 367 | 368 | ```kotlin 369 | val map1: Map = mapOf('A' to 'a') 370 | val map2: Map = mapOf('B' to 'b') 371 | val maps: List> = listOf(map1, map2) 372 | val map: Map = maps.reduce { acc, x -> acc + x } // {A=a, B=b} 373 | ``` 374 | 375 | ## SortedMap 376 | ### How to sort a Map by key 377 | ```kotlin 378 | println(sortedMapOf("10" to "ten", "1" to "one", "2" to "two")) // {1=one, 10=ten, 2=two} 379 | println(sortedMapOf(comparator = reverseOrder(), "10" to "ten", "1" to "one", "2" to "two")) // {2=two, 10=ten, 1=one} 380 | println(sortedMapOf(comparator = compareBy { it.toInt() }, "10" to "ten", "1" to "one", "2" to "two")) // {1=one, 2=two, 10=ten} 381 | ``` 382 | 383 | ### How to sort a Map by value 384 | ```kotlin 385 | val map = mapOf("ten" to "10", "one" to "1", "two" to "2") 386 | println(map.toList().sortedBy { it.second }.toMap()) // {one=1, ten=10, two=2} 387 | println(map.toList().sortedByDescending { it.second }.toMap()) // {two=2, ten=10, one=1} 388 | println(map.toList().sortedBy { it.second.toInt() }.toMap()) // {one=1, two=2, ten=10} 389 | ``` 390 | 391 | ### How to convert a Map to a SortedMap 392 | ```kotlin 393 | val map = mapOf("10" to "ten", "1" to "one", "2" to "two") 394 | println(map.toSortedMap()) // {1=one, 10=ten, 2=two} 395 | println(map.toSortedMap(comparator = reverseOrder())) // {2=two, 10=ten, 1=one} 396 | println(map.toSortedMap(comparator = compareBy { it.toInt() })) // {1=one, 2=two, 10=ten} 397 | ``` 398 | 399 | # String 400 | 401 | ## How to convert a String? to a String 402 | 403 | ```kotlin 404 | val x: String? = null 405 | val y: String = x.orEmpty() 406 | ``` 407 | 408 | ## isBlank() / isEmpty() / ifBlank() / ifEmpty() 409 | 410 | ```kotlin 411 | val a: Boolean = " ".isBlank() // true 412 | val b: Boolean = "".isEmpty() // true 413 | val c: String = " ".ifBlank { "default" } // "default" 414 | val d: String = "".ifEmpty { "default" } // "default" 415 | ``` 416 | 417 | ## How to get a substring 418 | 419 | ```kotlin 420 | val s1: String = "a/b/c".substringBefore("/") // a 421 | val s2: String = "a/b/c".substringAfter("/") // b/c 422 | val s3: String = "a/b/c".substringBeforeLast("/") // a/b 423 | val s4: String = "a/b/c".substringAfterLast("/") // c 424 | ``` 425 | 426 | ## How to remove a substring 427 | 428 | ```kotlin 429 | val s1: String = "abc".removePrefix("a") // bc 430 | val s2: String = "abc".removeSuffix("c") // ab 431 | val s3: String = "aaa".removeSurrounding("a") // a 432 | val s4: String = "abc".removeSurrounding("a", "c") // b 433 | ``` 434 | 435 | ## How to use joinToString(...) 436 | 437 | ```kotlin 438 | val xs: String = listOf("a", "b", "c").joinToString( 439 | separator = ";", 440 | prefix = "{", 441 | postfix = "}", 442 | limit = 2, 443 | truncated = "..." 444 | ) { 445 | it.uppercaseChar() 446 | } // "{A;B;...} 447 | ``` 448 | 449 | ## How to append strings 450 | 451 | ```kotlin 452 | val s: String = buildString { 453 | appendLine("aaa") 454 | appendLine("bbb") 455 | } 456 | ``` 457 | 458 | # Enum 459 | 460 | ```kotlin 461 | enum class MyColor(val value: Int) { 462 | BLACK(0x000000), WHITE(0xffffff), UNKNOWN(Int.MIN_VALUE); 463 | 464 | companion object { 465 | // Verbose for illustrative purposes 466 | fun fromOrdinal(ordinal: Int): MyColor = entries[ordinal] 467 | fun fromOrdinalOrNull(ordinal: Int): MyColor? = entries.getOrNull(ordinal) 468 | fun fromOrdinalOrDefault(ordinal: Int): MyColor = entries.getOrElse(ordinal) { UNKNOWN } 469 | fun fromName(name: String): MyColor = valueOf(name) 470 | fun fromNameIgnoreCase(name: String): MyColor = valueOf(name.uppercase()) 471 | fun fromNameOrNull(name: String): MyColor? = entries.find { it.name == name } 472 | fun fromNameIgnoreCaseOrNull(name: String): MyColor? = entries.find { it.name == name.uppercase() } 473 | fun fromValue(value: Int): MyColor = entries.first { it.value == value } 474 | fun fromValueOrNull(value: Int): MyColor? = entries.find { it.value == value } 475 | } 476 | } 477 | ``` 478 | 479 | ## Enum with properties and functions 480 | 481 | ```kotlin 482 | enum class Fruit { 483 | APPLE { 484 | override fun toString() = "🍎" 485 | override val producer: String = "👨‍🌾" 486 | override fun printSimilarFruit() { 487 | println("🍏") 488 | } 489 | }, 490 | ORANGE { 491 | override fun toString() = "🍊" 492 | override val producer: String = "👩‍🌾" 493 | override fun printSimilarFruit() { 494 | println("🍋") 495 | } 496 | }; 497 | 498 | abstract val producer: String 499 | abstract fun printSimilarFruit() 500 | } 501 | 502 | println(apple) // 🍎 503 | println(apple.producer) // 👨‍🌾 504 | apple.printSimilarFruit() // 🍏 505 | ``` 506 | 507 | ## How to extend an enum class 508 | 509 | ``` 510 | enum class Fruit(val emoji: String) { 511 | APPLE("🍎"), 512 | ORANGE("🍊"); 513 | } 514 | 515 | enum class ExtendedFruit(emoji: String) { 516 | APPLE(Fruit.APPLE.emoji), 517 | ORANGE(Fruit.ORANGE.emoji), 518 | BANANA("🍌"); 519 | } 520 | ``` 521 | 522 | # Function references / Constructor references 523 | 524 | ```kotlin 525 | fun main() { 526 | val xs: List = listOf(1, 2) 527 | 528 | // Function reference 529 | val result1: List = xs.filter(::isOdd) // [true, false] 530 | 531 | // Qualified function reference 532 | val result2: List = xs.map(Int::toString) // ["1", "2"] 533 | 534 | // Companion object function reference 535 | val result3: List = xs.map((Sample)::double) // [2, 4] 536 | val result4: List = xs.map(Sample.Companion::double) // [2, 4] 537 | 538 | // Constructor reference 539 | val result5: List = xs.map(::Sample) // [Sample(1), Sample(2)] 540 | } 541 | 542 | fun isOdd(x: Int): Boolean = x % 2 != 0 543 | 544 | data class Sample(val x: Int) { 545 | companion object { 546 | fun double(x: Int): Int { 547 | return x * 2 548 | } 549 | } 550 | } 551 | ``` 552 | 553 | # How do I know when to use a sealed class or interface? 554 | 555 | Prefer sealed interfaces to sealed classes, except in these two cases. 556 | 557 | ## Case 1: You want to use a constructor. (i.e. you want to hardcode a common property) 558 | 559 | Using the constructor of a sealed class is more concise than ... 560 | 561 | ```kotlin 562 | sealed class Fruit(val emoji: String) 563 | data class Apple(val foo: String) : Fruit(emoji = "🍎") 564 | data class Orange(val bar: String) : Fruit(emoji = "🍊") 565 | ``` 566 | 567 | ... using a sealed interface. 568 | 569 | ```kotlin 570 | sealed interface Fruit { 571 | val emoji: String 572 | } 573 | 574 | data class Apple(val foo: String, override val emoji: String = "🍎") : Fruit 575 | data class Orange(val bar: String, override val emoji: String = "🍊") : Fruit 576 | ``` 577 | 578 | ## Case 2: You want to use generics, which interfaces don't support. 579 | 580 | You can use generics with a sealed class, as shown below. 581 | 582 | ```kotlin 583 | sealed class UiState { 584 | data object Loading : UiState() 585 | data class Success(val data: T) : UiState() 586 | data class Failure(val throwable: Throwable) : UiState() 587 | } 588 | ``` 589 | 590 | On the other hand, you cannot use generics with a sealed interface as shown below. 591 | 592 | ```kotlin 593 | private sealed interface NonGenericUiState { 594 | data object Loading : NonGenericUiState 595 | data class Success(val data: String) : NonGenericUiState 596 | data class Failure(val throwable: Throwable) : NonGenericUiState 597 | } 598 | ``` 599 | 600 | # `` (Reified type parameters) 601 | 602 | enables you to use `is` and `as` on `T`. 603 | 604 | ```kotlin 605 | inline fun f(a: Any) { 606 | if (a is T) { 607 | // Do something 608 | } 609 | val t = a as T 610 | } 611 | ``` 612 | 613 | https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters 614 | 615 | # Delegate 616 | 617 | ## How to use Delegates.observable(...) 618 | 619 | ```kotlin 620 | // The type can be omitted. 621 | var observed: String by Delegates.observable("a") { _, old, new -> 622 | if (old == new) return@observable // In case you only want to react to the value change. 623 | println("$old -> $new") 624 | } 625 | println(observed) // "a" 626 | observed = "b" // "a -> b" 627 | observed = "b" // (none) 628 | observed = "c" // "b -> c" 629 | ``` 630 | 631 | ## How to use Delegates.vetoable(...) 632 | 633 | ```kotlin 634 | // The type can be omitted. 635 | var evenNumber: Int by Delegates.vetoable(1) { _, old, new -> 636 | new % 2 == 0 637 | } 638 | 639 | println(evenNumber) // 1. Though 1 is not an even number, the initial value is not validated. 640 | evenNumber = 3 // vetoed because 3 is not a even number. 641 | println(evenNumber) // 1 642 | evenNumber = 2 643 | println(evenNumber) // 2 644 | ``` 645 | 646 | ## Comparison among `lazy`, `Delegates.notNull()`, and `lateinit` 647 | 648 |   | lazy | Delegates.notNull() | lateinit 649 | --------------|-------------|---------------------|------------------------------- 650 | val | supported | not supported | not supported 651 | non-nullable | supported | supported | not supported primitive types |supported|supported|not supported performance|ok(※1)|ok( 652 | ※1) | good Dagger | supported | supported(※2) |supported 653 | 654 | * (※1) A delegate object is created. 655 | * (※2) Dagger's field injection is not supported but Dagger's method (setter) injection is. 656 | 657 | # Regex 658 | 659 | > For pattern syntax reference see [Pattern](https://developer.android.com/reference/java/util/regex/Pattern). 660 | 661 | https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-regex/ 662 | 663 | # How to print the name of the current function 664 | 665 | ```kotlin 666 | fun sample() { 667 | println(object {}.javaClass.enclosingMethod?.name) // sample 668 | } 669 | ``` 670 | 671 | # How to print when a property is accessed 672 | 673 | ```kotlin 674 | var x = "" 675 | get() { 676 | println("${object {}.javaClass.enclosingMethod?.name}: $field") 677 | return field 678 | } 679 | set(value) { 680 | println("${object {}.javaClass.enclosingMethod?.name}: $value") 681 | field = value 682 | } 683 | ``` 684 | 685 | # How to get the name of the current thread 686 | 687 | ```kotlin 688 | println(Thread.currentThread().name) 689 | 690 | // alternatively 691 | println(currentCoroutineContext()) 692 | ``` 693 | 694 | * The preceding code also shows the name of the current coroutine after the following preparation: 695 | * IntelliJ IDEA > Menu bar > Run > Edit Configurations > Configurations > VM options > 696 | add `-Dkotlinx.coroutines.debug`. 697 | 698 | # Syntactic sugar for throwing exceptions 699 | 700 | Function | Throws 701 | --------------------|-------------------------- 702 | check(Boolean) | IllegalStateException 703 | checkNotNull(T?) | IllegalStateException 704 | error(Any) | IllegalStateException 705 | require(Boolean) | IllegalArgumentException 706 | requireNotNull(T?) | IllegalArgumentException 707 | 708 | # `fold` versus `reduce` 709 | 710 |   | Type of initial value | Type of element of input array | Return type 711 | --------|-----------------------|--------------------------------|--------------------------- 712 | fold | R | T | R 713 | reduce | n/a | T | T or one of T's ancestors 714 | 715 | * https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fold.html 716 | * https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/reduce.html 717 | 718 | # KDoc 719 | 720 | ## How to add an external link 721 | 722 | ```kotlin 723 | /** 724 | * @see Example 725 | */ 726 | ``` 727 | 728 | Markdown also works. However, Intellij IDEA issues a warning that "Example" in `[Example]` is invalid. 729 | 730 | ```kotlin 731 | /** 732 | * @see [Example](https://example.com) 733 | */ 734 | ``` 735 | 736 | https://github.com/Kotlin/dokka/issues/518#issuecomment-744062184 737 | 738 | # Best practices 739 | 740 | [best-practices.md](markdown/best-practices.md) 741 | 742 | # Coroutines and Flow 743 | 744 | [coroutines-flow.md](markdown/coroutines-flow.md) 745 | 746 | # Misc 747 | 748 | [misc.md](markdown/misc.md) 749 | 750 | # RxJava 751 | 752 | [rxjava.md](markdown/rxjava.md) 753 | 754 | # Scope functions 755 | 756 | [scope-functions.md](markdown/scope-functions.md) 757 | --------------------------------------------------------------------------------