├── gradle.properties ├── src ├── main │ ├── resources │ │ └── Hello.txt │ ├── kotlin │ │ ├── coroutine │ │ │ ├── blogpost │ │ │ │ ├── structuredconcurrency │ │ │ │ │ ├── hello.txt │ │ │ │ │ ├── job │ │ │ │ │ │ ├── CancelJobFiboSuspendDemo.kt │ │ │ │ │ │ ├── NestedCallJobDemo.kt │ │ │ │ │ │ ├── SimpleJobDemo.kt │ │ │ │ │ │ ├── ParentChildByJobDemo.kt │ │ │ │ │ │ ├── NestedCallCancelDemo.kt │ │ │ │ │ │ ├── CancelJobDemo.kt │ │ │ │ │ │ ├── CancelJobFiboDemo.kt │ │ │ │ │ │ └── ParentChildByContextDemo.kt │ │ │ │ │ ├── FileClosedDemoFixed.kt │ │ │ │ │ ├── BrokenAbstractionDemo.kt │ │ │ │ │ ├── SayHelloWorldInContext.kt │ │ │ │ │ ├── UseContextDemo.kt │ │ │ │ │ ├── SayHelloWorldInJob.kt │ │ │ │ │ ├── FileClosedDemo.kt │ │ │ │ │ ├── JobDemo.kt │ │ │ │ │ └── StructuredJobDemo.kt │ │ │ │ ├── CancelJobDemo.kt │ │ │ │ ├── ContextIsLikeSet.kt │ │ │ │ ├── AsyncApiCatchDemo.kt │ │ │ │ ├── GlobalScopeCallstack.kt │ │ │ │ ├── SuspendContextDemo.kt │ │ │ │ ├── ContextElementDemo.kt │ │ │ │ ├── GoStyleCancelDemo.kt │ │ │ │ ├── ContextDemo.kt │ │ │ │ ├── CaptureContextDemo.kt │ │ │ │ └── ArrowDemo.kt │ │ │ ├── MyInterfaceJava.java │ │ │ ├── Test2.java │ │ │ ├── Log.kt │ │ │ ├── CoroutineAndCallback.kt │ │ │ ├── continuation │ │ │ │ ├── SuspendContinuationDemo.kt │ │ │ │ ├── CallDelayManuallyDemo.kt │ │ │ │ └── ManualContinuationExercise.kt │ │ │ ├── flow │ │ │ │ ├── toy │ │ │ │ │ ├── WrongContextDemo.kt │ │ │ │ │ ├── FlowImplDemo.kt │ │ │ │ │ └── FlowImplDemoWithoutReceiver.kt │ │ │ │ ├── ContextPreservationDemo.kt │ │ │ │ ├── FlatmapDemo.kt │ │ │ │ ├── SearchPrototype.kt │ │ │ │ ├── ExceptionHandlingDemo.kt │ │ │ │ ├── NoBackpressure.kt │ │ │ │ ├── SharedFlowMutableList.kt │ │ │ │ ├── CancelFlowByException.kt │ │ │ │ ├── StateFlowWithCombine.kt │ │ │ │ ├── StateFlowWithListDemo.kt │ │ │ │ ├── ConvertToSharedFlow.kt │ │ │ │ ├── SharedFlowDemo.kt │ │ │ │ ├── TakeOperatorDemo.kt │ │ │ │ ├── ChannelToFlow.kt │ │ │ │ ├── StateFlowWrappedMutableListDemo.kt │ │ │ │ ├── CancelingFlow.kt │ │ │ │ ├── LoadMoreRequestDemo.kt │ │ │ │ ├── InitStateFlow.kt │ │ │ │ ├── FlowQ3.kt │ │ │ │ ├── EndlessFlowDemo.kt │ │ │ │ ├── FlowQuestion.kt │ │ │ │ ├── FlowOverheadDemo.kt │ │ │ │ ├── FlowQ4.kt │ │ │ │ ├── CombineFlow.kt │ │ │ │ ├── FlowQ2.kt │ │ │ │ ├── StateFlowCollect.kt │ │ │ │ ├── FrpState.kt │ │ │ │ └── FunctionalReactiveUI.kt │ │ │ ├── CompletableDeferredDemo.kt │ │ │ ├── caching │ │ │ │ ├── Mutable.kt │ │ │ │ ├── StateFlowEnd.kt │ │ │ │ ├── Immutable.kt │ │ │ │ ├── StateFlow.kt │ │ │ │ └── CachingFlowDemo.kt │ │ │ ├── DeepRecursionDemo.kt │ │ │ ├── cancellation │ │ │ │ └── PropogateThroughScope.kt │ │ │ ├── RetriableCoroutineFactoryDemo.kt │ │ │ ├── CoroutineContextDemo.kt │ │ │ ├── SuspendCoroutineDemo.kt │ │ │ ├── channel │ │ │ │ ├── ChannelDemo.kt │ │ │ │ └── SuspendUntilValue.kt │ │ │ ├── SuspendLamdaDemo.kt │ │ │ ├── SuspendCoroutineBuilderDemo.kt │ │ │ ├── FlowDemo.kt │ │ │ ├── CoroutineScopeDemo.kt │ │ │ ├── CancelCouroutineByException.kt │ │ │ ├── ContinuationDemo.kt │ │ │ ├── LazyCoroutine.kt │ │ │ ├── Cancel.kt │ │ │ ├── ErrorPropogation.kt │ │ │ ├── AsyncCrash.kt │ │ │ ├── RaceDeferredDemo.kt │ │ │ ├── actor │ │ │ │ ├── ActorOverheadDemo.kt │ │ │ │ └── DataStoreDemo.kt │ │ │ ├── ExceptionDemo.kt │ │ │ ├── ContinuationInterceptorDemo.kt │ │ │ ├── SuspendingLambdaDemo.kt │ │ │ └── RetriableCoroutine.kt │ │ ├── types │ │ │ ├── UnitDemo.kt │ │ │ ├── Unsigned.ws.kts │ │ │ ├── MutipleInterface.kt │ │ │ ├── CompanionAsync.kt │ │ │ └── DelegationDemo.kt │ │ ├── string │ │ │ ├── ParseUrl.ws.kts │ │ │ ├── StringDemo.kt │ │ │ └── Quadratic.kt │ │ ├── stdlib │ │ │ ├── Require.kt │ │ │ ├── Sublist.kt │ │ │ └── Set.ws.kts │ │ ├── oop │ │ │ ├── Trait.kt │ │ │ ├── EnumDemo.kt │ │ │ ├── JavaPrefix.java │ │ │ └── LeakingThisDemo.kt │ │ ├── algorithms │ │ │ ├── sort │ │ │ │ ├── Swap.kt │ │ │ │ ├── TestDrive.kt │ │ │ │ ├── SelectionSort.kt │ │ │ │ ├── InsertionSort.kt │ │ │ │ ├── QuickSort.kt │ │ │ │ ├── QuickSelect2.kt │ │ │ │ ├── QuickSelect.kt │ │ │ │ └── quick │ │ │ │ │ ├── Quickselect.kt │ │ │ │ │ └── Quicksort.kt │ │ │ ├── leetcode │ │ │ │ ├── backtrack │ │ │ │ │ ├── 78_Subsets.kt │ │ │ │ │ ├── 46_Permutation.kt │ │ │ │ │ ├── 22_GenerateParenthesis.kt │ │ │ │ │ └── 22_GenerateParenthesisDeepRecursive.kt │ │ │ │ ├── Test.kt │ │ │ │ ├── 105_ConstructTree.kt │ │ │ │ ├── 132pattern.kt │ │ │ │ ├── 3_LongestNonRepeatingSubstring.kt │ │ │ │ ├── RemoveDuplicateNode.kt │ │ │ │ ├── Contest.kt │ │ │ │ ├── calculator │ │ │ │ │ ├── Expression.kt │ │ │ │ │ ├── BasicCalculator1.kt │ │ │ │ │ └── ShuntingYard.kt │ │ │ │ ├── LRUCache.kt │ │ │ │ └── GoodPair.kt │ │ │ ├── MarkovGenerator.kt │ │ │ └── StackToQueue.kt │ │ ├── jvm │ │ │ ├── FinallyDemo.kt │ │ │ └── ExceptionDemo.kt │ │ ├── resources │ │ │ └── ResourceDeclaration.kt │ │ ├── fp │ │ │ ├── ArrowExceptionDslDemo.kt │ │ │ ├── ArrowEitherDemo.kt │ │ │ ├── ArrowExceptionDemo.kt │ │ │ ├── SyntaxDepInjectDemo.kt │ │ │ └── ReaderMonadDemo.kt │ │ ├── ktor │ │ │ ├── KtorConnectionDemoClient.kt │ │ │ ├── KtorConnectionDemo.kt │ │ │ └── HtmlDemo.kt │ │ ├── concurrency │ │ │ ├── IncrementCounterDemo.kt │ │ │ ├── hashmap.kt │ │ │ ├── CompletableFututureAnyOfDemo.kt │ │ │ ├── DeadlockDemo.kt │ │ │ ├── jcip │ │ │ │ ├── CatchCancellationExceptionDemo.kt │ │ │ │ ├── MemoizerJcipDemo.kt │ │ │ │ ├── MemoizerSuspendableDemo.kt │ │ │ │ └── AqsValueLatch.kt │ │ │ └── bank │ │ │ │ ├── optimistic │ │ │ │ ├── TransferMoneyTest.kt │ │ │ │ ├── SimNetworkErrorTest.kt │ │ │ │ └── BankService.kt │ │ │ │ └── twopc │ │ │ │ ├── BankServiceImpl.kt │ │ │ │ └── BankService.kt │ │ ├── tips │ │ │ ├── Singleton.ws.kts │ │ │ └── List.ws.kts │ │ ├── io │ │ │ ├── ReadClassFile.kt │ │ │ ├── server.kt │ │ │ ├── ConcurrentFileWriterDemo.kt │ │ │ ├── client.kt │ │ │ └── retrofit.kt │ │ ├── cli │ │ │ ├── CliDemo.kt │ │ │ └── ObjectOrientedStyle.kt │ │ ├── dataloader │ │ │ ├── JavaDataLoader.kt │ │ │ └── DataloaderDemo.kt │ │ ├── rx │ │ │ ├── ParallelComposition.kt │ │ │ ├── RxApiDemo.kt │ │ │ ├── RxDemo.kt │ │ │ └── ComparisonWithSequenceDemo.kt │ │ ├── serialization │ │ │ ├── SerializableDemo.kt │ │ │ ├── SerializationDemo.kt │ │ │ └── MoshiDemo.kt │ │ ├── kmongo.kt │ │ ├── excercise │ │ │ └── LazyListImpl.kt │ │ ├── ktorm │ │ │ ├── KtormPgDemo.kt │ │ │ └── KtormDemo.kt │ │ ├── rutang │ │ │ └── WeeklyActiveData.kt │ │ ├── compose │ │ │ └── ComposeToy.kt │ │ └── TransferMoneyDemo.kt │ └── java │ │ ├── HandlerDemo.java │ │ ├── Gender.java │ │ ├── StringDemo.java │ │ └── StaticDemo.java └── test │ └── kotlin │ ├── ResourceTest.kt │ ├── StateFlowTest.kt │ ├── HookTest.kt │ ├── DynamicTest.kt │ └── CustomConsumerTest.kt ├── .gitignore ├── settings.gradle.kts ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── modules │ └── kotlin-playground.main.iml ├── compiler.xml ├── vcs.xml ├── .gitignore ├── modules.xml ├── misc.xml ├── gradle.xml └── jarRepositories.xml ├── out.txt └── gradlew.bat /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /src/main/resources/Hello.txt: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /.gradle/ 3 | /build/ -------------------------------------------------------------------------------- /src/test/kotlin/ResourceTest.kt: -------------------------------------------------------------------------------- 1 | 2 | fun main() { 3 | 4 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "kotlin-playground" 2 | 3 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/hello.txt: -------------------------------------------------------------------------------- 1 | hello world! -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/CancelJobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/HandlerDemo.java: -------------------------------------------------------------------------------- 1 | public class HandlerDemo { 2 | // a = Void.TYPE; 3 | } 4 | -------------------------------------------------------------------------------- /src/main/kotlin/types/UnitDemo.kt: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | object A 4 | 5 | fun main() { 6 | println(Unit) 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/string/ParseUrl.ws.kts: -------------------------------------------------------------------------------- 1 | import java.net.URI 2 | 3 | 4 | val destination = URI("/subject?id=1") 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yujinyan/kotlin-playground/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/kotlin/stdlib/Require.kt: -------------------------------------------------------------------------------- 1 | package stdlib 2 | 3 | fun main() { 4 | val a: String? = null 5 | requireNotNull(a) 6 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/MyInterfaceJava.java: -------------------------------------------------------------------------------- 1 | package coroutine; 2 | 3 | public interface MyInterfaceJava { 4 | void hi(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/kotlin/oop/Trait.kt: -------------------------------------------------------------------------------- 1 | package oop 2 | 3 | class Repository 4 | 5 | interface HasRepository { 6 | val repository: Repository 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/types/Unsigned.ws.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("EXPERIMENTAL_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE") 2 | 3 | val v: Short = -12345 4 | println(v.toUShort()) -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/Test2.java: -------------------------------------------------------------------------------- 1 | package coroutine; 2 | 3 | public class Test2 { 4 | Integer getP() { 5 | return 1; 6 | 7 | } 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/main/kotlin/types/MutipleInterface.kt: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | interface I1 { 4 | fun foo(): Int 5 | } 6 | 7 | interface I2 { 8 | fun foo(): String 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/main/java/Gender.java: -------------------------------------------------------------------------------- 1 | public enum Gender { 2 | Male, Female; 3 | 4 | public static void main(String[] args) { 5 | var name = Gender.Male.name(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/Swap.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | fun IntArray.swap(i: Int, j: Int) { 4 | val temp = this[j] 5 | this[j] = this[i] 6 | this[i] = temp 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/Log.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | //fun log(msg: String) = println("[${Thread.currentThread().name}] $msg") 4 | 5 | fun log(msg: String) = println("$msg") 6 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/modules/kotlin-playground.main.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /out.txt: -------------------------------------------------------------------------------- 1 | 33333333333333333333444444444444444444447777777777777777777788888888888888888888000000000000000000009999999999999999999922222222222222222222111111111111111111115555555555555555555566666666666666666666 -------------------------------------------------------------------------------- /src/main/kotlin/stdlib/Sublist.kt: -------------------------------------------------------------------------------- 1 | package stdlib 2 | 3 | fun main() { 4 | val oldList = mutableListOf(1, 2, 3) 5 | val subList = oldList.subList(0, 2) 6 | oldList[0] = 100 7 | println(subList) 8 | } -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/kotlin/oop/EnumDemo.kt: -------------------------------------------------------------------------------- 1 | package oop 2 | 3 | enum class Prefix(val value: String) { 4 | Post("post"), InviteCode("invite-code") 5 | } 6 | 7 | fun main() { 8 | println(Prefix.Post.name) 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/StringDemo.java: -------------------------------------------------------------------------------- 1 | public class StringDemo { 2 | public static void main(String[] args) { 3 | StringBuilder sb = new StringBuilder(); 4 | sb.append("hi"); 5 | sb.toString(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/ContextIsLikeSet.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlinx.coroutines.CoroutineName 4 | 5 | fun main() { 6 | println(CoroutineName("foo") + CoroutineName("bar") == CoroutineName("bar")) 7 | } -------------------------------------------------------------------------------- /src/main/kotlin/oop/JavaPrefix.java: -------------------------------------------------------------------------------- 1 | package oop; 2 | 3 | public enum JavaPrefix { 4 | Post; 5 | 6 | public static void main(String[] args) { 7 | String p = JavaPrefix.Post.name(); 8 | } 9 | } 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/kotlin/jvm/FinallyDemo.kt: -------------------------------------------------------------------------------- 1 | 2 | fun test(): Int { 3 | try { 4 | return 1 5 | } finally { 6 | println("hi") 7 | return 2 8 | } 9 | } 10 | 11 | fun main() { 12 | val result = test() 13 | println(result) 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/resources/ResourceDeclaration.kt: -------------------------------------------------------------------------------- 1 | package resources 2 | 3 | val hello = {}.javaClass.getResourceAsStream("/Hello.txt").reader().readText() 4 | 5 | fun printHello() = println(hello) 6 | 7 | fun main() { 8 | println(hello) 9 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/kotlin/oop/LeakingThisDemo.kt: -------------------------------------------------------------------------------- 1 | package oop 2 | 3 | class Foo { 4 | init { 5 | printBar(this) 6 | } 7 | val bar = "Bar" 8 | } 9 | 10 | fun printBar(foo: Foo) { 11 | println(foo.bar) 12 | } 13 | 14 | fun main() { 15 | Foo() 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/fp/ArrowExceptionDslDemo.kt: -------------------------------------------------------------------------------- 1 | package fp 2 | 3 | import arrow.core.Either 4 | import arrow.core.computations.either 5 | import arrow.core.left 6 | import arrow.core.right 7 | import kotlin.random.Random 8 | 9 | 10 | fun main() { 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /../../../../../../../:\Users\yujinyan\code\study\kotlin-playground\.idea/dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/CoroutineAndCallback.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | fun getToken(block: (String) -> Unit) { 4 | 5 | } 6 | 7 | fun upload(token: String) { 8 | // upload... 9 | } 10 | 11 | 12 | fun main() { 13 | getToken { token -> 14 | upload(token) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/jvm/ExceptionDemo.kt: -------------------------------------------------------------------------------- 1 | package jvm 2 | 3 | import java.lang.IllegalArgumentException 4 | 5 | fun foo(): IllegalArgumentException { 6 | return bar() 7 | } 8 | 9 | fun bar() = IllegalArgumentException() 10 | 11 | fun main() { 12 | val e = foo() 13 | e.printStackTrace() 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/string/StringDemo.kt: -------------------------------------------------------------------------------- 1 | 2 | fun textLength(value: String) = value.codePoints().toArray().fold(0) { acc, c -> 3 | if (c > 256) acc + 2 4 | else (acc + 1) 5 | } 6 | 7 | fun main() { 8 | // println(textLength("你好")) 9 | // println(textLength("hello")) 10 | textLength("🎈🎆🎇a你好") 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/continuation/SuspendContinuationDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.continuation 2 | 3 | import kotlinx.coroutines.delay 4 | 5 | //suspend fun foo() = bar() 6 | 7 | suspend fun bar(): String { 8 | delay(100) 9 | return "bar" 10 | } 11 | 12 | suspend fun main() { 13 | val b = bar() 14 | println(b) 15 | } -------------------------------------------------------------------------------- /src/main/kotlin/ktor/KtorConnectionDemoClient.kt: -------------------------------------------------------------------------------- 1 | package ktor 2 | 3 | import io.ktor.client.* 4 | import io.ktor.client.engine.cio.* 5 | import io.ktor.client.request.* 6 | 7 | 8 | suspend fun main() { 9 | HttpClient(CIO).use { 10 | val result = it.get("http://localhost:8080/") 11 | println(result) 12 | } 13 | } -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/IncrementCounterDemo.kt: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.launch 5 | 6 | suspend fun main() { 7 | var counter = 0 8 | coroutineScope { 9 | repeat(10_000) { 10 | launch { counter++ } 11 | } 12 | } 13 | println("counter: $counter") 14 | } 15 | -------------------------------------------------------------------------------- /src/main/kotlin/tips/Singleton.ws.kts: -------------------------------------------------------------------------------- 1 | @file:Suppress("HasPlatformType", "unused") 2 | 3 | import java.math.BigInteger 4 | import java.util.* 5 | 6 | fun initObject() = BigInteger 7 | .probablePrime(128, Random()) 8 | 9 | // singleton, eager 10 | val obj1 = initObject() 11 | 12 | // singleton, lazy, thread-safe 13 | val obj2 by lazy { initObject() } 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/TestDrive.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | 4 | private val IntArray.isSorted: Boolean 5 | get() { 6 | for (i in 1..lastIndex) { 7 | if (get(i) <= get(i - 1)) return false 8 | } 9 | return true 10 | } 11 | 12 | fun main() { 13 | (1..1000).shuffled().toIntArray() 14 | .let { 15 | insertionSort(it) 16 | require(it.isSorted) { "not sorted" } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/SelectionSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | fun selectionSort(arr: IntArray) { 4 | for (i in 0..arr.lastIndex) { 5 | var min = i 6 | for (j in i + 1..arr.lastIndex) { 7 | if (arr[j] < arr[min]) min = j 8 | } 9 | arr.swap(i, min) 10 | } 11 | } 12 | 13 | fun main() { 14 | val array = intArrayOf(3, 1, 5, 2) 15 | selectionSort(array) 16 | println(array.asList()) 17 | } 18 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/toy/WrongContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow.toy 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.flow.collect 5 | import kotlinx.coroutines.withContext 6 | import kotlin.coroutines.coroutineContext 7 | 8 | suspend fun main() { 9 | FlowImpl { 10 | withContext(Dispatchers.IO) { 11 | emit(1) 12 | } 13 | }.collect { 14 | println(coroutineContext) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/CompletableDeferredDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | 5 | suspend fun main() { 6 | 7 | coroutineScope { 8 | val c = CompletableDeferred(coroutineContext[Job]) 9 | launch { 10 | delay(1000) 11 | c.complete(1) 12 | } 13 | 14 | launch { 15 | val result = c.await() 16 | println("got $result") 17 | println("got ${c.await()}") 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/ReadClassFile.kt: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import java.io.File 4 | 5 | 6 | fun main() { 7 | val file = File("C:\\Users\\yujinyan\\code\\study\\java\\Adder.class") 8 | .readBytes() 9 | 10 | val seq = file.iterator().asSequence() 11 | // seq. 12 | // seq.take(4).toList().also { println(it) } 13 | seq.take(2).toList().also { println(it) } 14 | 15 | // file.take(4).also { println(it) } 16 | // file.take(2).also { println(it) } 17 | } -------------------------------------------------------------------------------- /src/main/java/StaticDemo.java: -------------------------------------------------------------------------------- 1 | @SuppressWarnings("ALL") 2 | public class StaticDemo { 3 | static class Foo { 4 | static int foo = loadFoo(); 5 | 6 | static int loadFoo() { 7 | throw new IllegalStateException(); 8 | } 9 | 10 | static class Bar { 11 | static int bar = 1; 12 | } 13 | } 14 | 15 | public static void main(String[] args) { 16 | System.out.println(Foo.foo); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/CancelJobFiboSuspendDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import kotlinx.coroutines.* 4 | 5 | suspend fun main() { 6 | val job = GlobalScope.launch { 7 | printFibonacciSlowly(50) 8 | } 9 | delay(100) 10 | job.cancelAndJoin() 11 | } 12 | 13 | suspend fun printFibonacciSlowly(n: Int) { 14 | for (i in 0..n) { 15 | yield() 16 | println(fibonacci(i)) 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/ContextPreservationDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.flow.collect 5 | import kotlinx.coroutines.flow.flow 6 | import kotlinx.coroutines.withContext 7 | 8 | suspend fun main() { 9 | val f = flow { // BAD!! 10 | emit(1) 11 | val value = withContext(Dispatchers.IO) { 2 } 12 | emit(value) 13 | } 14 | 15 | f.collect { 16 | println(it) 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/InsertionSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | import algorithms.sort.quick.swap 4 | 5 | // eg. 3,5,1,2,4 6 | fun insertionSort(arr: IntArray) { 7 | for (i in 1..arr.lastIndex) { 8 | for (j in i downTo 1) { 9 | if (arr[j] < arr[j - 1]) { 10 | swap(arr, j, j - 1) 11 | } else break 12 | } 13 | } 14 | } 15 | 16 | fun main() { 17 | val array = intArrayOf(3, 1, 5, 2) 18 | insertionSort(array) 19 | println(array.asList()) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/types/CompanionAsync.kt: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import kotlinx.coroutines.CoroutineScope 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.async 6 | import types.SomeDialog.Companion.newInstanceAsync 7 | 8 | class SomeDialog { 9 | companion object { 10 | fun CoroutineScope.newInstanceAsync() = async { 11 | SomeDialog() 12 | } 13 | } 14 | } 15 | 16 | suspend fun main() { 17 | val result = GlobalScope.newInstanceAsync() 18 | result.await() 19 | } -------------------------------------------------------------------------------- /src/test/kotlin/StateFlowTest.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.ExperimentalCoroutinesApi 2 | import kotlinx.coroutines.flow.MutableStateFlow 3 | import kotlinx.coroutines.flow.collect 4 | import kotlinx.coroutines.flow.map 5 | 6 | @ExperimentalCoroutinesApi 7 | suspend fun main() { 8 | val counter = MutableStateFlow(0) 9 | val doubledCounter = counter.map { it * 2 } 10 | counter.value++ 11 | counter.value++ 12 | doubledCounter.collect { 13 | println(it) 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/FileClosedDemoFixed.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.* 4 | import java.io.InputStream 5 | 6 | 7 | fun CoroutineScope.process(stream: InputStream) { 8 | launch { 9 | delay(1000) 10 | stream.reader().readText() 11 | } 12 | } 13 | 14 | suspend fun main() { 15 | {}.javaClass.getResourceAsStream("/Hello.txt").use { 16 | coroutineScope { 17 | process(it) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/AsyncApiCatchDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlin.concurrent.thread 4 | 5 | object AsyncApiCatchDemo { 6 | fun foo() { 7 | thread { 8 | error("whoops") 9 | } 10 | // Thread { 11 | // println("thread is ${Thread.currentThread().name}") 12 | // error("whoops") 13 | // }.start() 14 | } 15 | } 16 | 17 | fun main() { 18 | try { 19 | AsyncApiCatchDemo.foo() 20 | } catch (e: Throwable) { 21 | println(e) 22 | } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/stdlib/Set.ws.kts: -------------------------------------------------------------------------------- 1 | package stdlib 2 | 3 | data class Carrot(val id: Int, val name: String) 4 | 5 | val carrots1: Map = listOf( 6 | Carrot(1, "1"), 7 | Carrot(2, "1"), 8 | ).associateBy { it.id } 9 | 10 | val carrots2: Map = listOf( 11 | Carrot(3, "1"), 12 | Carrot(4, "1"), 13 | ).associateBy { it.id } 14 | 15 | carrots1 + carrots2 16 | carrots1 - carrots2 17 | carrots1.values intersect carrots2.values 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/kotlin/io/server.kt: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import io.ktor.application.* 4 | import io.ktor.response.* 5 | import io.ktor.routing.* 6 | import io.ktor.server.engine.* 7 | import io.ktor.server.netty.* 8 | import kotlinx.coroutines.delay 9 | 10 | fun main() { 11 | val server = embeddedServer(Netty, port = 8080) { 12 | routing { 13 | get("/test") { 14 | delay(1000) 15 | call.respondText("hi, ${call.request.queryParameters["id"]}") 16 | } 17 | } 18 | } 19 | server.start(wait = true) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/hashmap.kt: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlin.concurrent.thread 5 | 6 | suspend fun main() { 7 | val map = HashMap() 8 | 9 | // coroutineScope { 10 | // repeat(10000) { i -> map[i.toString()] = 1.toString() } 11 | // } 12 | 13 | repeat(100) { i -> 14 | thread { 15 | map[i.toString()] = 1.toString() 16 | } 17 | } 18 | 19 | Thread.sleep(6000) 20 | 21 | // println(map.size) 22 | map.forEach { t, u -> println(t) } 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/caching/Mutable.kt: -------------------------------------------------------------------------------- 1 | package coroutine.caching 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.launch 8 | 9 | suspend fun main() { 10 | val state = MutableStateFlow(mutableListOf()) 11 | 12 | GlobalScope.launch { 13 | delay(100) 14 | state.value = state.value.apply { add(1) } 15 | } 16 | state.collect { 17 | println(it) // [] 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/BrokenAbstractionDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.launch 7 | 8 | 9 | 10 | 11 | fun main() { 12 | writeData() 13 | // 🤔 数据写完了吗?可以读这个数据了吗? 14 | } 15 | 16 | fun writeData() { 17 | GlobalScope.launch(Dispatchers.IO) { 18 | // doing some work .. 19 | delay(1000) 20 | // write data 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/DeepRecursionDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | class Tree(val left: Tree?, val right: Tree?) 4 | 5 | @ExperimentalStdlibApi 6 | val depth = DeepRecursiveFunction { tree: Tree? -> 7 | if (tree == null) 0 else maxOf( 8 | callRecursive(tree.left), 9 | callRecursive(tree.right), 10 | ) + 1 11 | } 12 | 13 | @ExperimentalStdlibApi 14 | fun main() { 15 | val treeGenerator = generateSequence(Tree(null, null)) { prev -> Tree(prev, null) } 16 | val deepTree = treeGenerator.take(100_000).last() 17 | println(depth(deepTree)) 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/NestedCallJobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.isActive 6 | import kotlinx.coroutines.launch 7 | import kotlin.coroutines.coroutineContext 8 | 9 | suspend fun main() = 10 | GlobalScope.launch { 11 | foo() 12 | }.join() 13 | 14 | suspend fun foo() = 15 | bar() 16 | 17 | suspend fun bar() { 18 | println(coroutineContext.isActive) 19 | delay(1000) 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/SimpleJobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import coroutine.blogpost.structuredconcurrency.printAsTree 4 | import io.ktor.util.* 5 | import kotlinx.coroutines.* 6 | import kotlin.time.ExperimentalTime 7 | 8 | @ExperimentalTime 9 | @InternalAPI 10 | suspend fun main() = coroutineScope { 11 | val job = Job() 12 | 13 | launch(job) { 14 | delay(1000) 15 | println("foo") 16 | } 17 | launch(job) { 18 | delay(1500) 19 | println("bar") 20 | } 21 | 22 | Unit 23 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/GlobalScopeCallstack.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.launch 5 | 6 | object GlobalScopeCallstack { 7 | suspend fun foo() { 8 | println("called foo") 9 | bar() 10 | } 11 | 12 | suspend fun bar() { 13 | println("called bar") 14 | GlobalScope.launch { 15 | baz() 16 | } 17 | } 18 | 19 | suspend fun baz() { 20 | error("whoops") 21 | } 22 | } 23 | 24 | suspend fun main() { 25 | GlobalScopeCallstack.foo() 26 | while (true) { 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/SayHelloWorldInContext.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.coroutineContext 5 | 6 | suspend fun sayHelloWorldInContext() { 7 | GlobalScope.launch(coroutineContext) { 8 | delay(500) 9 | print("Hello ") 10 | } 11 | GlobalScope.launch(coroutineContext) { 12 | delay(1000) 13 | print("World!") 14 | } 15 | } 16 | 17 | fun main() = runBlocking { 18 | launch { 19 | sayHelloWorldInContext() 20 | }.cancelAndJoin() 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/cancellation/PropogateThroughScope.kt: -------------------------------------------------------------------------------- 1 | package coroutine.cancellation 2 | 3 | import kotlinx.coroutines.CancellationException 4 | import kotlinx.coroutines.supervisorScope 5 | 6 | suspend fun main() { 7 | // coroutineScope { 8 | // launch { 9 | // throw IllegalStateException("1") 10 | //// throw CancellationException("1") 11 | // } 12 | // } 13 | // 14 | // coroutineScope { 15 | // throw CancellationException("2") 16 | // } 17 | 18 | supervisorScope { 19 | throw CancellationException() 20 | } 21 | 22 | println("end") 23 | 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FlatmapDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.flow.collect 6 | import kotlinx.coroutines.flow.flatMapLatest 7 | import kotlinx.coroutines.flow.flow 8 | import kotlinx.coroutines.flow.flowOf 9 | 10 | @ExperimentalCoroutinesApi 11 | suspend fun main() { 12 | val f = flowOf(1, 2, 3).flatMapLatest { 13 | flow { 14 | emit(it * it) 15 | emit(it * it * it) 16 | } 17 | } 18 | 19 | f.collect { 20 | log("collected $it") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/RetriableCoroutineFactoryDemo.kt: -------------------------------------------------------------------------------- 1 | import coroutine.log 2 | import kotlinx.coroutines.channels.Channel 3 | import java.util.concurrent.atomic.AtomicInteger 4 | 5 | fun attemptFactory() = { block: suspend () -> T -> 6 | val retryRequest = Channel() 7 | val failedCount = AtomicInteger(0) 8 | suspend { 9 | while (true) { 10 | try { 11 | block() 12 | } catch (e: Throwable) { 13 | log("caught exception $e") 14 | failedCount.incrementAndGet() 15 | retryRequest.receive() 16 | } 17 | } 18 | } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/kotlin/cli/CliDemo.kt: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import kotlinx.cli.ArgParser 4 | import kotlinx.cli.ArgType 5 | import kotlinx.cli.default 6 | 7 | fun useParser(block: ArgParser.() -> Unit) { 8 | val parser = ArgParser("") 9 | parser.block() 10 | } 11 | 12 | 13 | fun main(args: Array) = useParser { 14 | val output by option(ArgType.String).default("output.csv") 15 | val shouldSend by option(ArgType.Boolean).default(false) 16 | 17 | parse(args) 18 | 19 | println("writing to file $output") 20 | 21 | if (shouldSend) { 22 | println("sending file $shouldSend") 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/kotlin/dataloader/JavaDataLoader.kt: -------------------------------------------------------------------------------- 1 | package dataloader 2 | 3 | import org.dataloader.BatchLoader 4 | import org.dataloader.DataLoader 5 | import java.util.concurrent.CompletableFuture 6 | 7 | 8 | fun main() { 9 | val batchLoader = BatchLoader { 10 | CompletableFuture.supplyAsync { 11 | listOf(User(1, 3), User(2, 4)) 12 | } 13 | } 14 | 15 | val userLoader = DataLoader.newDataLoader(batchLoader) 16 | 17 | val user1 = userLoader.load(1).thenAccept { println(it) } 18 | val user2 = userLoader.load(2).thenAccept { println(it) } 19 | 20 | userLoader.dispatchAndJoin() 21 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/SuspendContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.CoroutineContext 5 | import kotlin.coroutines.coroutineContext 6 | 7 | suspend fun main() { 8 | 9 | suspend fun foo(context: CoroutineContext) { 10 | println(coroutineContext) 11 | println(context) 12 | Dispatchers.Default 13 | // withContext() 14 | // println(coroutineContext == context) 15 | } 16 | 17 | coroutineScope { 18 | val context = Dispatchers.IO 19 | launch(context) { 20 | foo(context) 21 | } 22 | } 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | IDE 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/kotlin/types/DelegationDemo.kt: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | interface OnClickHandler { 4 | fun onClick() 5 | } 6 | 7 | class Handler(val r: Any): OnClickHandler { 8 | init { 9 | println((r as AnotherActivity).someField) 10 | } 11 | override fun onClick() { 12 | } 13 | } 14 | 15 | 16 | 17 | 18 | class SomeActivity: OnClickHandler by Handler(1) { 19 | 20 | } 21 | 22 | class AnotherActivity: OnClickHandler { 23 | val handler = Handler(this) 24 | val someField = Any() 25 | override fun onClick() { 26 | TODO("Not yet implemented") 27 | } 28 | } 29 | 30 | fun main() { 31 | AnotherActivity() 32 | } 33 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/ParentChildByJobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.coroutineContext 5 | 6 | suspend fun sayHelloWorld(): String { 7 | val job = Job() 8 | val task1 = GlobalScope.async(job) { 9 | delay(500) 10 | "Hello " 11 | } 12 | val task2 = GlobalScope.async(job) { 13 | delay(1000); 14 | "World!" 15 | } 16 | return task1.await() + task2.await() 17 | } 18 | 19 | fun main() = runBlocking { 20 | launch { 21 | println(sayHelloWorld()) 22 | } 23 | println("hi") 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/caching/StateFlowEnd.kt: -------------------------------------------------------------------------------- 1 | package coroutine.caching 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.flow.onCompletion 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun main() { 11 | val state = MutableStateFlow(0) 12 | GlobalScope.launch { 13 | while (true) { 14 | state.value += 1 15 | delay(1000) 16 | } 17 | } 18 | 19 | state 20 | .collect { 21 | println(it) 22 | println(state.replayCache) 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/UseContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.coroutineContext 5 | 6 | suspend fun foo() { 7 | delay(1000) 8 | println("foo") 9 | } 10 | 11 | suspend fun bar() { 12 | delay(800) 13 | error("whoops") 14 | println("bar") 15 | } 16 | 17 | suspend fun composed() { 18 | GlobalScope.launch(coroutineContext) { foo() } 19 | GlobalScope.launch(coroutineContext) { bar() } 20 | } 21 | 22 | 23 | suspend fun main() { 24 | composed() 25 | println("after composed") 26 | while (true) {} 27 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/CoroutineContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.CoroutineContext 5 | 6 | suspend fun foo() { 7 | 8 | } 9 | 10 | suspend fun main() { 11 | val context: CoroutineContext = Dispatchers.IO + CoroutineName("CoCo") + CoroutineName("hi") 12 | val name: CoroutineName? = context[CoroutineName] 13 | println(name) 14 | 15 | (Dispatchers.IO + Printer).also { println(it) } 16 | 17 | // foo() 18 | // 19 | // delay(1000) 20 | // 21 | // println(context) 22 | // coroutineScope { 23 | // launch(context) { 24 | // println(coroutineContext) 25 | // } 26 | // } 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/CompletableFututureAnyOfDemo.kt: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import java.util.concurrent.CompletableFuture 4 | import kotlin.time.ExperimentalTime 5 | import kotlin.time.TimeSource 6 | 7 | 8 | @OptIn(ExperimentalTime::class) 9 | fun main() { 10 | val now = TimeSource.Monotonic.markNow() 11 | val data1 = CompletableFuture.supplyAsync { 12 | Thread.sleep(500); 13 | 1 14 | } 15 | 16 | val data2 = CompletableFuture.supplyAsync { 17 | Thread.sleep(1000) 18 | 2 19 | } 20 | 21 | val result = CompletableFuture.anyOf(data1, data2).get() as Int 22 | 23 | println("got $result, elapsed ${now.elapsedNow()}") 24 | } -------------------------------------------------------------------------------- /src/main/kotlin/rx/ParallelComposition.kt: -------------------------------------------------------------------------------- 1 | package rx 2 | 3 | import io.reactivex.rxjava3.core.Observable 4 | import io.reactivex.rxjava3.core.Scheduler 5 | import io.reactivex.rxjava3.schedulers.Schedulers 6 | 7 | fun main() { 8 | Observable.range(0, 10) 9 | .flatMap { 10 | Observable.just(it) 11 | .subscribeOn(Schedulers.io()) 12 | .map { 13 | println(Thread.currentThread().name) 14 | Thread.sleep(500) 15 | it * it 16 | } 17 | } 18 | .map { 19 | println(Thread.currentThread()) 20 | it * it 21 | } 22 | .blockingSubscribe { 23 | println(it) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/caching/Immutable.kt: -------------------------------------------------------------------------------- 1 | package coroutine.caching 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.launch 8 | 9 | suspend fun main() { 10 | val state = MutableStateFlow(listOf()) 11 | 12 | GlobalScope.launch { 13 | delay(100) 14 | state.value += 1 15 | state.value += listOf(2, 3) 16 | } 17 | 18 | state.collect { 19 | println(it) // [] 20 | // [1] 21 | // [1, 2, 3] 22 | 23 | val a = "Hello" 24 | val b = "$a World" 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/ConcurrentFileWriterDemo.kt: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import java.io.File 4 | import java.util.concurrent.locks.ReentrantLock 5 | import kotlin.concurrent.thread 6 | 7 | fun main() { 8 | val o = {} 9 | val lock = ReentrantLock() 10 | (0..9).map { i -> 11 | thread { 12 | lock.lock() 13 | File("out.txt").apply { 14 | repeat(20) { 15 | appendText(i.toString()) 16 | } 17 | } 18 | lock.unlock() 19 | // File("out.txt").printWriter().use { writer -> 20 | // repeat(20) { 21 | // writer.print(i) 22 | // } 23 | // } 24 | } 25 | }.forEach { 26 | it.join() 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/SearchPrototype.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.flow.MutableSharedFlow 5 | import kotlinx.coroutines.flow.collect 6 | import kotlinx.coroutines.flow.map 7 | import kotlinx.coroutines.launch 8 | 9 | val searchQuery = MutableSharedFlow(1) 10 | 11 | val staticSections = searchQuery.map { 12 | "static sections for $it" 13 | } 14 | 15 | suspend fun main() { 16 | coroutineScope { 17 | launch { 18 | searchQuery.emit("query 1") 19 | } 20 | 21 | launch { 22 | staticSections.collect { 23 | println(it) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/SayHelloWorldInJob.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.coroutineContext 5 | 6 | suspend fun sayHelloWorld() { 7 | val job = Job(parent = coroutineContext[Job]) 8 | GlobalScope.launch(job) { 9 | delay(500) 10 | print("Hello ") 11 | } 12 | GlobalScope.launch(job) { 13 | delay(1000) 14 | print("World!") 15 | } 16 | job.complete() 17 | job.join() 18 | } 19 | 20 | fun main() = runBlocking { 21 | val job = launch { 22 | sayHelloWorld() 23 | } 24 | 25 | delay(100) 26 | job.cancelAndJoin() 27 | delay(2000) 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/ExceptionHandlingDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.flow.catch 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.flow.flow 8 | import kotlinx.coroutines.flow.onCompletion 9 | 10 | suspend fun main() { 11 | val flow = flow { 12 | emit(1) 13 | emit(2) 14 | emit(3) 15 | error("whoops") 16 | emit(4) 17 | emit(5) 18 | } 19 | // .catch { 20 | // 21 | // } 22 | .onCompletion { 23 | log("e: $it") 24 | } 25 | .catch {} 26 | 27 | flow.collect { 28 | log("collected $it") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/FileClosedDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import java.io.File 7 | import java.io.InputStream 8 | 9 | fun process(stream: InputStream) { 10 | GlobalScope.launch { 11 | delay(1000) 12 | stream.reader().readText() 13 | } 14 | } 15 | 16 | fun demo() { 17 | File("./hello.txt").inputStream().use { 18 | process(it) 19 | } 20 | } 21 | 22 | fun main() { 23 | {}.javaClass.getResourceAsStream("/Hello.txt").use { 24 | process(it) 25 | } 26 | 27 | while (true) { 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/SuspendCoroutineDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.runBlocking 4 | import kotlin.coroutines.resume 5 | import kotlin.coroutines.suspendCoroutine 6 | 7 | object SuspendCoroutineDemo { 8 | fun fetchData(block: (Int) -> Unit) { 9 | block(1) 10 | } 11 | 12 | suspend fun fetchData(): Int = suspendCoroutine { cont -> 13 | fetchData { 14 | cont.resume(it) 15 | } 16 | } 17 | } 18 | 19 | fun main() { 20 | // uses callback 21 | SuspendCoroutineDemo.fetchData { 22 | println(it) 23 | } 24 | 25 | // uses suspend function 26 | runBlocking { 27 | val data = SuspendCoroutineDemo.fetchData() 28 | println(data) 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/ktor/KtorConnectionDemo.kt: -------------------------------------------------------------------------------- 1 | package ktor 2 | 3 | import io.ktor.application.* 4 | import io.ktor.response.* 5 | import io.ktor.routing.* 6 | import io.ktor.server.engine.* 7 | import io.ktor.server.netty.* 8 | import kotlinx.coroutines.delay 9 | import kotlinx.coroutines.yield 10 | 11 | fun main() { 12 | embeddedServer(Netty, port = 8080) { 13 | routing { 14 | get("/") { 15 | heavyLifting() 16 | call.respondText("hello world!") 17 | } 18 | } 19 | }.start(wait = true) 20 | } 21 | 22 | suspend fun heavyLifting() { 23 | println("starting to do heavy lifting") 24 | while (true) { 25 | delay(100) 26 | print(".") 27 | yield() 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/backtrack/78_Subsets.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode.backtrack 2 | 3 | import algorithms.TracingDeepRecursiveFunction 4 | import algorithms.invoke 5 | 6 | 7 | suspend fun main() { 8 | val result = mutableListOf>() 9 | val arr = listOf(1, 2, 3) 10 | 11 | val list = mutableListOf() 12 | 13 | val subsets = TracingDeepRecursiveFunction f@{ i: Int -> 14 | if (i == arr.size) { 15 | result += list.toList() 16 | return@f 17 | } 18 | list += arr[i] 19 | callRecursive(i + 1) 20 | list.removeAt(list.lastIndex) 21 | callRecursive(i + 1) 22 | } 23 | 24 | subsets(0) 25 | println(result) 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/channel/ChannelDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.channel 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.launch 8 | 9 | suspend fun main() { 10 | val channel = Channel(1) 11 | 12 | coroutineScope { 13 | launch { 14 | delay(1000) 15 | channel.send(Unit) 16 | delay(1000) 17 | channel.send(Unit) 18 | } 19 | 20 | delay(3000) 21 | 22 | launch { 23 | for (i in channel) { 24 | println(i) 25 | } 26 | // channel.receive() 27 | // log("received") 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/NoBackpressure.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun main() { 11 | val state = MutableStateFlow(0) 12 | coroutineScope { 13 | launch { 14 | while (true) { 15 | delay(1000) 16 | log("updating state") 17 | state.value += 1 18 | } 19 | } 20 | 21 | launch { 22 | state.collect { 23 | log("collecting $it") 24 | delay(20000) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/SuspendLamdaDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlin.coroutines.Continuation 4 | import kotlin.coroutines.CoroutineContext 5 | import kotlin.coroutines.EmptyCoroutineContext 6 | import kotlin.jvm.functions.Function1 7 | 8 | val s: suspend () -> Int = suspend { 9 | println("in suspend") 10 | 1 11 | } 12 | 13 | fun main() { 14 | println(s::class.java.superclass.name) 15 | s::class.java.interfaces.forEach { println(it) } 16 | 17 | val f = s as Function1, Int> 18 | 19 | f(object : Continuation { 20 | override val context: CoroutineContext = EmptyCoroutineContext 21 | override fun resumeWith(result: Result): Unit = TODO() 22 | }) 23 | } -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/SuspendCoroutineBuilderDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import io.ktor.utils.io.* 4 | import kotlin.concurrent.thread 5 | import kotlin.coroutines.resume 6 | import kotlin.coroutines.suspendCoroutine 7 | import kotlin.time.ExperimentalTime 8 | import kotlin.time.TimeSource 9 | 10 | suspend fun await() = suspendCoroutine{ cont -> 11 | // if (true) cont.resumeWith() 12 | thread { 13 | Thread.sleep(1000) 14 | cont.resume(Unit) 15 | } 16 | } 17 | 18 | @ExperimentalTime 19 | suspend fun main() { 20 | mutableMapOf().remove(1) 21 | val time = TimeSource.Monotonic.markNow() 22 | await() 23 | await() 24 | await() 25 | println("elapsed ${time.elapsedNow()}") 26 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/SharedFlowMutableList.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun main() { 11 | 12 | val state = MutableSharedFlow>(replay = 1) 13 | 14 | val data = mutableListOf() 15 | 16 | coroutineScope { 17 | launch { 18 | while (true) { 19 | data.add(0) 20 | delay(1000) 21 | state.emit(data) 22 | } 23 | } 24 | 25 | state.collect { 26 | log("collected $it") 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/Test.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | fun maxDistance(nums1: IntArray, nums2: IntArray): Int { 4 | var max = 0 5 | 6 | for (i in nums1.indices) { 7 | val target = nums1[i] 8 | 9 | var lo = i 10 | var hi = nums2.lastIndex 11 | 12 | while (lo <= hi) { 13 | val mi = lo + (hi - lo) / 2 14 | if (nums2[mi] < target) { 15 | hi = mi - 1 16 | } else if (nums2[mi] > target) { 17 | lo = mi + 1 18 | } else { 19 | lo++ 20 | } 21 | } 22 | 23 | max = Math.max(max, lo) 24 | } 25 | 26 | return max 27 | } 28 | 29 | fun main() { 30 | maxDistance(intArrayOf(55,30,5,4,2), intArrayOf(100,20,10,10,5)).also { println(it) } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/CancelFlowByException.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.CancellationException 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.launch 10 | 11 | suspend fun doWork() { 12 | delay(1000) 13 | throw CancellationException() 14 | } 15 | 16 | suspend fun main() { 17 | val flow = flow { 18 | emit(1) 19 | delay(1000) 20 | emit(2) 21 | emit(3) 22 | doWork() 23 | } 24 | 25 | coroutineScope { 26 | launch { 27 | flow.collect { 28 | log("collected $it") 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/client.kt: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import io.ktor.client.* 4 | import io.ktor.client.engine.cio.* 5 | import io.ktor.client.request.* 6 | import io.ktor.util.* 7 | import kotlinx.coroutines.coroutineScope 8 | import kotlinx.coroutines.launch 9 | import kotlin.system.measureTimeMillis 10 | 11 | @KtorExperimentalAPI 12 | suspend fun main() { 13 | val client = HttpClient(CIO) 14 | 15 | val time = measureTimeMillis { 16 | coroutineScope { 17 | repeat(1000) { 18 | launch { 19 | val result = client.get("http://localhost:8080/test?id=$it") 20 | println("${Thread.currentThread().name}: received res $result") 21 | } 22 | } 23 | } 24 | } 25 | 26 | println("spent $time") 27 | } 28 | -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/DeadlockDemo.kt: -------------------------------------------------------------------------------- 1 | package concurrency 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.sync.Mutex 7 | 8 | suspend fun main() { 9 | val s1 = Mutex() 10 | val s2 = Mutex() 11 | 12 | repeat(5) { 13 | coroutineScope { 14 | launch { 15 | s1.lock() 16 | delay(100) 17 | println("doing stuff in s1") 18 | s2.lock() 19 | s1.unlock() 20 | s2.unlock() 21 | } 22 | 23 | launch { 24 | s2.lock() 25 | delay(100) 26 | println("doing stuff in s2") 27 | s1.lock() 28 | s1.unlock() 29 | s2.unlock() 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/backtrack/46_Permutation.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode.backtrack 2 | 3 | import algorithms.TracingDeepRecursiveFunction 4 | import algorithms.invoke 5 | 6 | suspend fun main() { 7 | val arr = listOf(1, 2, 3) 8 | val result = mutableListOf>() 9 | val list = mutableListOf() 10 | 11 | val permute = TracingDeepRecursiveFunction, Unit> f@{ 12 | if (list.size == arr.size) { 13 | result += list.toList() 14 | return@f 15 | } 16 | 17 | for (n in arr) { 18 | if (n in list) continue 19 | list += n 20 | callRecursive(list.toList()) // parameter only for tracing 21 | list.removeAt(list.lastIndex) 22 | } 23 | } 24 | 25 | permute(list) 26 | println(result) 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/backtrack/22_GenerateParenthesis.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode.backtrack 2 | 3 | private fun gen( 4 | left: Int, right: Int, str: String, 5 | depth: Int, 6 | block: (String) -> Unit, 7 | ) { 8 | repeat(depth * 4) { 9 | print(" ") 10 | } 11 | print("($left, $right, \"$str\")") 12 | if (left == 0 && right == 0) { 13 | block(str) 14 | print(" // collect: $str\n") 15 | return 16 | } 17 | print("\n") 18 | val nextDepth = depth + 1 19 | if (left > 0) gen(left - 1, right + 1, str + '(', nextDepth, block) 20 | if (right > 0) gen(left, right - 1, str + ')', nextDepth, block) 21 | } 22 | 23 | fun main() { 24 | val result = mutableListOf() 25 | 26 | gen(3, 0, "", 0) { result += it } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/ContextElementDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlinx.coroutines.CoroutineExceptionHandler 4 | import kotlinx.coroutines.CoroutineName 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlin.coroutines.Continuation 7 | import kotlin.coroutines.ContinuationInterceptor 8 | import kotlin.coroutines.CoroutineContext 9 | 10 | fun main() { 11 | (CoroutineName("Coco") + Dispatchers.IO).also { it: CoroutineContext -> 12 | println(it[CoroutineName] == CoroutineName("Coco")) // true 13 | println(it[ContinuationInterceptor] == Dispatchers.IO) // true 14 | } 15 | 16 | (CoroutineName("foo") + CoroutineName("bar")).also { it: CoroutineContext -> 17 | println(it[CoroutineName] == CoroutineName("bar")) // true 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/GoStyleCancelDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.channels.ReceiveChannel 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.launch 8 | import kotlinx.coroutines.runBlocking 9 | 10 | @ExperimentalCoroutinesApi 11 | suspend fun work(done: ReceiveChannel) { 12 | while (true) { 13 | if (done.isClosedForReceive) break 14 | println("working...") 15 | delay(1000) 16 | } 17 | } 18 | 19 | @ExperimentalCoroutinesApi 20 | fun main() = runBlocking{ 21 | val c = Channel() 22 | 23 | launch { 24 | work(c) 25 | } 26 | 27 | delay(5000) 28 | c.close() 29 | 30 | Unit 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/FlowDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.* 6 | import kotlinx.coroutines.launch 7 | import kotlinx.coroutines.runBlocking 8 | import kotlin.concurrent.thread 9 | 10 | fun main() = runBlocking{ 11 | // flowOf(1, 2, 3).map { it * 2 }.filter { it % 2 == 0 } 12 | 13 | val flow = MutableStateFlow(1) 14 | 15 | launch { 16 | flow.collect { 17 | println(it) 18 | 19 | } 20 | } 21 | 22 | launch { 23 | delay(1000) 24 | flow.value = 10 25 | } 26 | 27 | launch { 28 | flow.collect { 29 | println(it) 30 | } 31 | } 32 | 33 | 34 | thread(isDaemon = true) { 35 | println("hi") 36 | } 37 | 38 | Unit 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/StateFlowWithCombine.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.flow.combine 9 | import kotlinx.coroutines.flow.flow 10 | import kotlinx.coroutines.launch 11 | 12 | suspend fun main() { 13 | val state = MutableStateFlow(1) 14 | 15 | val flow = flow { 16 | delay(1000); emit("a") 17 | delay(1000); emit("b") 18 | } 19 | 20 | GlobalScope.launch { 21 | delay(5000) 22 | state.emit(2) 23 | } 24 | 25 | flow.combine(state) { i1, _ -> 26 | i1 27 | }.collect { 28 | log("collected flow: $it, state: ${state.value}") 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/retrofit.kt: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.launch 5 | import retrofit2.Retrofit 6 | import retrofit2.create 7 | import retrofit2.http.GET 8 | import retrofit2.http.Query 9 | 10 | interface TestService { 11 | 12 | @GET("/test") 13 | suspend fun testApi(@Query("id") id: Int) 14 | } 15 | 16 | suspend fun main() { 17 | 18 | val retrofit = Retrofit.Builder() 19 | .baseUrl("http://localhost:8080") 20 | .build() 21 | 22 | val service = retrofit.create() 23 | 24 | coroutineScope { 25 | repeat(100) { 26 | launch { 27 | service.testApi(it) 28 | val result = "" 29 | println("${Thread.currentThread().name}: received res $result") 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/NestedCallCancelDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.coroutineContext 5 | import kotlin.time.Duration 6 | import kotlin.time.ExperimentalTime 7 | 8 | @ExperimentalTime 9 | object NestedCallCancelDemo { 10 | suspend fun foo() { 11 | try { 12 | bar() 13 | } catch (e: CancellationException) { 14 | println("caught $e") 15 | } 16 | } 17 | 18 | suspend fun bar() { 19 | println(coroutineContext.isActive) 20 | delay(Duration.INFINITE) 21 | } 22 | } 23 | 24 | @ExperimentalTime 25 | suspend fun main() { 26 | val job = GlobalScope.launch { 27 | NestedCallCancelDemo.foo() 28 | } 29 | 30 | job.cancelAndJoin() 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/StateFlowWithListDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun main() { 11 | val _state = MutableStateFlow(listOf(0)) 12 | val flow: Flow> = _state 13 | coroutineScope { 14 | launch { 15 | delay(1000) 16 | _state.value = listOf(1) 17 | delay(1000) 18 | _state.value = listOf(2) 19 | delay(1000) 20 | _state.value = listOf(3) 21 | 22 | _state.value += 1 23 | _state.value -= 1 24 | } 25 | 26 | flow.collect { 27 | println(it) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/serialization/SerializableDemo.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.Dispatchers 2 | import kotlinx.coroutines.GlobalScope 3 | import kotlinx.coroutines.channels.Channel 4 | import kotlinx.coroutines.plus 5 | import java.io.File 6 | import java.io.ObjectOutputStream 7 | import java.io.Serializable 8 | 9 | data class Value(val value: T) : Serializable 10 | 11 | data class Person(val name: String, val gender: String = "abc") : Serializable { 12 | companion object { 13 | @JvmStatic private val serialVersionUID = 1381488341778288004 14 | } 15 | } 16 | 17 | suspend fun main() { 18 | Channel { } 19 | GlobalScope + Dispatchers.IO 20 | val p = Person("Peter", "M") 21 | 22 | val fs = File("test.bin").outputStream() 23 | ObjectOutputStream(fs).use { 24 | it.writeObject(p) 25 | } 26 | } -------------------------------------------------------------------------------- /src/test/kotlin/HookTest.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.GlobalScope 2 | import kotlinx.coroutines.launch 3 | import kotlinx.html.* 4 | 5 | fun main() { 6 | val consumer = TestConsumer() 7 | val (counter, setCount) = useState(0) 8 | val (switch, setSwitch) = useState(true) 9 | val (data, setData) = useState(listOf(1, 2, 3)) 10 | 11 | HTML(mapOf(), consumer).apply { 12 | body { 13 | p(classes = "foo bar") { +"counter is $counter" } 14 | div { 15 | ul { 16 | for (i in data) li { +i.toString() } 17 | } 18 | } 19 | if (switch) p { +"Hello World!" } 20 | } 21 | } 22 | 23 | GlobalScope.launch { 24 | setCount(1) 25 | } 26 | } 27 | 28 | 29 | fun useState(value: T): Pair Unit> { 30 | 31 | return value to { newValue: T -> Unit } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/CoroutineScopeDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | import kotlin.coroutines.coroutineContext 5 | 6 | fun test1(scope: CoroutineScope) { 7 | scope.launch { 8 | delay(2000) 9 | throw IllegalStateException("whoops in test 1") 10 | } 11 | } 12 | 13 | fun test2(scope: CoroutineScope) { 14 | scope.launch { 15 | while (true) { 16 | delay(500) 17 | log("test2: I'm working") 18 | } 19 | } 20 | } 21 | 22 | suspend fun sayHi() { 23 | println(coroutineContext) 24 | } 25 | 26 | suspend fun doNotDoThis() { 27 | CoroutineScope(coroutineContext).launch { 28 | println("delaying") 29 | delay(1000) 30 | println("hello") 31 | } 32 | } 33 | 34 | suspend fun main() { 35 | coroutineScope { 36 | doNotDoThis() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/CancelJobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import coroutine.blogpost.structuredconcurrency.printAsTree 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.launch 7 | import kotlin.time.Duration.Companion.INFINITE 8 | import kotlin.time.ExperimentalTime 9 | 10 | @ExperimentalTime 11 | suspend fun main() = coroutineScope { 12 | val job = 13 | launch { 14 | launch { 15 | launch { delay(INFINITE) } 16 | launch { delay(INFINITE) } 17 | } 18 | launch { 19 | launch { delay(INFINITE) } 20 | launch { delay(INFINITE) } 21 | } 22 | } 23 | 24 | delay(500) 25 | job.printAsTree() 26 | job.cancel() 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/caching/StateFlow.kt: -------------------------------------------------------------------------------- 1 | package coroutine.caching 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.launch 8 | 9 | private var n = 0 10 | suspend fun someHeavyWeightCalculation(): Int { 11 | delay(1000) 12 | return (n++) 13 | } 14 | 15 | suspend fun main() { 16 | val state = MutableStateFlow(0) 17 | 18 | GlobalScope.launch { 19 | while (true) { 20 | state.value = someHeavyWeightCalculation().also { println("f calculated $it") } 21 | delay(1000) 22 | } 23 | } 24 | 25 | state.collect { 26 | println("consumer collected: $it") 27 | println("consumer sleeping a while") 28 | delay(2000) 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/ConvertToSharedFlow.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.* 7 | import kotlinx.coroutines.launch 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.seconds 10 | 11 | @ExperimentalTime 12 | suspend fun main() { 13 | suspend fun callSomeApi(): String { 14 | delay(1.seconds) 15 | return "hello world".also { log("calculated result: $it") } 16 | } 17 | coroutineScope { 18 | val flow = flow { 19 | emit(callSomeApi()) 20 | }.shareIn(this, SharingStarted.Lazily) 21 | 22 | 23 | repeat(5) { 24 | launch { 25 | flow.collect { 26 | log("collected $it") 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/fp/ArrowEitherDemo.kt: -------------------------------------------------------------------------------- 1 | package fp 2 | 3 | import arrow.core.Either 4 | import arrow.core.computations.either 5 | import arrow.core.flatMap 6 | import arrow.core.left 7 | import arrow.core.right 8 | import fp.ArrowEitherDemo.uploadImage 9 | import fp.ArrowEitherDemo.uploadVideo 10 | import kotlin.random.Random 11 | 12 | object ArrowEitherDemo { 13 | fun uploadImage(): Either = 14 | if (Random.nextBoolean()) NetworkUnstable.left() else "xxx".right() 15 | 16 | fun uploadVideo(): Either = 17 | if (Random.nextBoolean()) NetworkUnstable.left() else "xxx".right() 18 | } 19 | 20 | suspend fun main() { 21 | val result = either> { 22 | listOf(uploadImage().bind(), uploadVideo().bind()) 23 | } 24 | println(result) 25 | } 26 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/CancelCouroutineByException.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.flow.collect 5 | import kotlinx.coroutines.flow.flow 6 | 7 | fun main() = runBlocking { 8 | val job = GlobalScope.launch { 9 | delay(1000) 10 | throw CancellationException() 11 | } 12 | 13 | job.join() 14 | 15 | coroutineScope { 16 | launch { 17 | delay(1000) 18 | log("delayed 1000") 19 | throw CancellationException() 20 | } 21 | } 22 | 23 | coroutineScope { 24 | val f = flow { 25 | emit(1) 26 | delay(1000) 27 | emit(2) 28 | delay(1000) 29 | throw CancellationException() 30 | } 31 | coroutineScope { 32 | f.collect { 33 | log("collected $it") 34 | } 35 | } 36 | } 37 | 38 | Unit 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/SharedFlowDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun main() { 11 | val state = MutableSharedFlow(replay = 1) 12 | 13 | coroutineScope { 14 | launch { 15 | delay(2000) 16 | var i = 0 17 | while (true) { 18 | val valueToEmit = (i++).also { log("preparing to emit $it") } 19 | state.emit(valueToEmit) 20 | } 21 | } 22 | 23 | launch { 24 | state.collect { 25 | log("received $it") 26 | log("cache is ${state.replayCache}") 27 | delay(1000) 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/TakeOperatorDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.flow.first 9 | import kotlinx.coroutines.flow.take 10 | import kotlinx.coroutines.launch 11 | 12 | suspend fun main() { 13 | val flow = MutableSharedFlow(1) 14 | coroutineScope { 15 | launch { 16 | delay(500) 17 | flow.emit(Unit) 18 | } 19 | 20 | launch { 21 | flow.first() 22 | log("1: collect done") 23 | } 24 | 25 | launch { 26 | delay(1000) 27 | flow.first() 28 | log("2: collect done") 29 | } 30 | listOf(1, 2, 3).subList(0, 5) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/JobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.InternalCoroutinesApi 5 | import kotlinx.coroutines.Job 6 | import kotlinx.coroutines.launch 7 | import org.litote.kmongo.index 8 | 9 | @InternalCoroutinesApi 10 | suspend fun main() { 11 | val job = GlobalScope.launch { 12 | launch { 13 | launch { } 14 | launch { } 15 | } 16 | launch { 17 | launch { } 18 | launch { } 19 | } 20 | }.apply { join() } 21 | 22 | printJobTree(job) 23 | } 24 | 25 | fun printJobTree(job: Job, builder: StringBuilder = StringBuilder()) { 26 | println(builder.toString() + job) 27 | job.children.forEach { 28 | printJobTree(it, builder.append(" ")) 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/test/kotlin/DynamicTest.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.html.* 2 | 3 | fun template() { 4 | 5 | } 6 | 7 | fun main() { 8 | val consumer = TestConsumer() 9 | val data = listOf(1, 2, 3) 10 | var counter = 0 11 | var switch = true 12 | HTML(mapOf(), consumer).apply { 13 | body { 14 | p(classes = "foo bar") { 15 | +"counter is ${dynamic(counter)}" 16 | } 17 | div { 18 | ul { 19 | for (i in dynamic(data)) { 20 | li { +i.toString() } 21 | } 22 | } 23 | } 24 | if (dynamic(switch)) { 25 | p { +"Hello World!" } 26 | } 27 | } 28 | } 29 | counter++ 30 | switch = false 31 | } 32 | 33 | fun HTMLTag.dynamic(value: T): T { 34 | (consumer as? TestConsumer)?.apply { 35 | registerDynamic(value) 36 | } 37 | 38 | return value 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/CancelJobFiboDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import kotlinx.coroutines.* 4 | 5 | suspend fun main() { 6 | val job = GlobalScope.launch(Dispatchers.IO) { 7 | for (i in 0..50) { 8 | // 除了 return 之外还可以抛 `CancellationException` 9 | // 协程库提供的 `ensureActive` 封装了这一方法 10 | // 另外也可以使用 `yield` 11 | // -----下面这些写法都可以----- 12 | // if (!isActive) throw CancellationException() 13 | // ensureActive() 14 | // yield() 15 | if (!isActive) return@launch 16 | println(fibonacci(i)) 17 | } 18 | } 19 | delay(100) 20 | // 取消 job 并等待,避免 jvm 直接退出 21 | job.cancelAndJoin() 22 | } 23 | 24 | // deliberately slow fibonacci 25 | fun fibonacci(n: Int): Int = if (n <= 1) 26 | n else fibonacci(n - 1) + fibonacci(n - 2) 27 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/ChannelToFlow.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.flow.* 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun main() { 11 | val refreshRequest = MutableStateFlow(0) 12 | val search = flow { 13 | delay(1000); emit("foo") 14 | delay(1000); emit("bar") 15 | } 16 | 17 | coroutineScope { 18 | launch { 19 | delay(5000) 20 | delay(1000); refreshRequest.value += 1 21 | delay(1000); refreshRequest.value += 1 22 | } 23 | 24 | search.combine(refreshRequest) { query, _ -> 25 | delay(500) 26 | "search result for $query" 27 | }.collect { 28 | log("collected $it") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/ContinuationDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import io.ktor.client.utils.* 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.launch 6 | import java.io.File 7 | import kotlin.coroutines.* 8 | import kotlin.coroutines.intrinsics.createCoroutineUnintercepted 9 | 10 | fun run(block: suspend () -> Unit) { 11 | // (block as Function1 12 | 13 | } 14 | 15 | // https://github.com/arrow-kt/arrow-fx/tree/master/arrow-fx-coroutines 16 | object ContinuationDemo { 17 | suspend fun test(): Int = 1 18 | suspend fun fail(): Int = TODO() 19 | 20 | } 21 | 22 | 23 | fun main() { 24 | val cont = ContinuationDemo::test.createCoroutine(Continuation(EmptyCoroutineContext, ::println)) 25 | cont.resume(Unit) 26 | 27 | 28 | ContinuationDemo::fail.startCoroutine(Continuation(EmptyCoroutineContext, ::println)) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/main/kotlin/kmongo.kt: -------------------------------------------------------------------------------- 1 | import com.mongodb.client.model.UpdateOptions 2 | import org.litote.kmongo.* 3 | 4 | class Shop( 5 | val meta: Meta 6 | ) 7 | 8 | class Meta( 9 | val status: Int 10 | ) 11 | 12 | fun getShop(): Shop = TODO() 13 | 14 | fun main() { 15 | 16 | Shop::meta / Meta::status gt 1 17 | Meta::status gt 1 18 | // Meta::status gt "1" 19 | 20 | val client = KMongo.createClient() //get com.mongodb.MongoClient new instance 21 | val database = client.getDatabase("test") //normal java driver usage 22 | val col = database.getCollection() //KMongo extension method 23 | //here the name of the collection by convention is "jedi" 24 | //you can use getCollection("otherjedi") if the collection name is different 25 | 26 | val shop = getShop() 27 | 28 | 29 | 30 | col.updateOne(shop, UpdateOptions().bypassDocumentValidation(true) ) 31 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/StateFlowWrappedMutableListDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.launch 8 | 9 | sealed class State { 10 | data class Success(val data: List) 11 | data class Error(val error: Throwable) : State() 12 | } 13 | 14 | suspend fun main() { 15 | val list = mutableListOf(0) 16 | val state = MutableStateFlow(State.Success(list)) 17 | 18 | coroutineScope { 19 | launch { 20 | list.add(1) 21 | state.value = State.Success(list) 22 | delay(1000) 23 | list.add(2) 24 | state.value = State.Success(list) 25 | delay(1000) 26 | } 27 | 28 | state.collect { 29 | println(it) 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/LazyCoroutine.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.flow.MutableSharedFlow 6 | import kotlinx.coroutines.flow.collect 7 | import kotlin.coroutines.CoroutineContext 8 | 9 | 10 | suspend fun main() { 11 | log("starting...") 12 | 13 | val jobs = MutableSharedFlow(replay = 5) 14 | 15 | // val jobs = Channel(capacity = 1) 16 | 17 | coroutineScope { 18 | 19 | repeat(5) { 20 | val job: Job = launch(start = CoroutineStart.LAZY, context = coroutineContext) { 21 | log("job $it started") 22 | delay(2000) 23 | log("job $it end") 24 | } 25 | jobs.emit(job) 26 | } 27 | 28 | log("prepare to collect") 29 | jobs.collect { 30 | log("collected $it") 31 | it.start() 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/105_ConstructTree.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | class TreeNode( 4 | val `val`: Int, 5 | var left: TreeNode? = null, 6 | var right: TreeNode? = null, 7 | ) 8 | 9 | fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { 10 | fun buildTree(lo: Int, hi: Int): TreeNode? { 11 | if (hi < lo) return null 12 | val rootValue = preorder[lo] 13 | val node = TreeNode(rootValue) 14 | val iRootIndex = inorder.indexOf(rootValue) 15 | val leftSize = iRootIndex 16 | val rightSize = hi - iRootIndex 17 | node.left = buildTree(lo + 1, lo + leftSize) 18 | node.right = buildTree(lo + leftSize + 1, lo + leftSize + rightSize) 19 | return node 20 | } 21 | 22 | return buildTree(0, preorder.lastIndex) 23 | } 24 | 25 | fun main() { 26 | buildTree(intArrayOf(3, 9, 20, 15, 7), intArrayOf(9, 3, 15, 20, 7)) 27 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/jcip/CatchCancellationExceptionDemo.kt: -------------------------------------------------------------------------------- 1 | package concurrency.jcip 2 | 3 | import kotlinx.coroutines.coroutineScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | import kotlin.random.Random 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.TimeSource 10 | 11 | suspend fun fetchData(): Int { 12 | delay(1000) 13 | if (Random.nextInt(0, 10) < 2) 14 | error("whoops") 15 | return 1 16 | } 17 | 18 | @OptIn(ExperimentalTime::class) 19 | fun main() = runBlocking { 20 | val now = TimeSource.Monotonic.markNow() 21 | val job = launch { 22 | runCatching { 23 | fetchData() 24 | } 25 | 26 | // Pretend we continue to do other work... 27 | while (true) { 28 | } 29 | } 30 | 31 | delay(200) 32 | job.cancel() 33 | println(now.elapsedNow()) 34 | } 35 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/channel/SuspendUntilValue.kt: -------------------------------------------------------------------------------- 1 | package coroutine.channel 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.launch 8 | 9 | suspend fun main() { 10 | val channel = Channel() 11 | 12 | suspend fun Channel.waitUntil(value: Boolean) { 13 | for (v in this) { 14 | if (v == value) break 15 | } 16 | } 17 | 18 | coroutineScope { 19 | launch { 20 | channel.waitUntil(true) 21 | log("received true") 22 | } 23 | 24 | launch { 25 | log("sending false") 26 | channel.send(false) 27 | delay(500) 28 | log("sending false") 29 | channel.send(false) 30 | delay(500) 31 | log("sending true") 32 | channel.send(true) 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/main/kotlin/excercise/LazyListImpl.kt: -------------------------------------------------------------------------------- 1 | package excercise 2 | 3 | //interface Sequence { 4 | // fun emit(value: Int) 5 | //} 6 | 7 | interface LazyList { 8 | fun collect(block: (Int) -> Unit) 9 | } 10 | 11 | class LazyListImpl( 12 | val list: List 13 | ) : LazyList { 14 | 15 | override fun collect(block: (Int) -> Unit) { 16 | list.forEach(block) 17 | } 18 | } 19 | 20 | fun LazyList.map(mapper: (Int) -> Int): LazyList { 21 | val self = this 22 | return object : LazyList { 23 | override fun collect(block: (Int) -> Unit) { 24 | self.collect { 25 | block(mapper(it)) 26 | } 27 | } 28 | } 29 | } 30 | 31 | fun main() { 32 | val list = LazyListImpl(listOf(1, 2, 3, 4, 5)) 33 | list 34 | .map { value -> 35 | (value * 2).also { println("mapped $it") } 36 | } 37 | .collect { 38 | println("collected $it") 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/Cancel.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.launch 10 | 11 | fun getFlow() = flow { 12 | var n = 0 13 | while (true) { 14 | emit(n++) 15 | delay(1000) 16 | } 17 | } 18 | 19 | suspend fun main() { 20 | val state = MutableStateFlow(0) 21 | 22 | val job = GlobalScope.launch { 23 | getFlow().collect { 24 | state.value = it 25 | } 26 | } 27 | 28 | GlobalScope.launch { 29 | state.collect { 30 | println("consumer collected: $it") 31 | } 32 | } 33 | 34 | GlobalScope.launch { 35 | delay(5000) 36 | job.cancel() 37 | 38 | state.value = 100 39 | }.join() 40 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/job/ParentChildByContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency.job 2 | 3 | import arrow.core.extensions.either.applicativeError.catch 4 | import kotlinx.coroutines.* 5 | import kotlin.coroutines.coroutineContext 6 | 7 | suspend fun sayHelloWorldInContext(): String { 8 | val a = GlobalScope.async(coroutineContext) { 9 | delay(500); error("whoops") 10 | println("a running") 11 | "Hello " 12 | } 13 | val b = GlobalScope.async(coroutineContext) { 14 | try { 15 | delay(1000) 16 | println("b running") 17 | " World!" 18 | } catch (e: Throwable) { 19 | println(e) 20 | } 21 | } 22 | return a.await() + b.await() 23 | } 24 | 25 | fun main() = runBlocking { 26 | val job = launch { 27 | println(sayHelloWorldInContext()) 28 | } 29 | delay(600) 30 | job.cancelAndJoin() 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/MarkovGenerator.kt: -------------------------------------------------------------------------------- 1 | package algorithms 2 | 3 | import kotlin.random.Random 4 | 5 | class MarkovGenerator(private val k: Int, text: String) { 6 | private val ngramStore = mutableMapOf>().apply { 7 | val ngramsSequence = text.splitToSequence("") 8 | .filterNot { it.isBlank() } 9 | .windowed(k) { it.joinToString("") } 10 | ngramsSequence.windowed(2) 11 | .forEach { 12 | getOrPut(it[0]) { mutableListOf() }.add(it[1].last().toString()) 13 | } 14 | } 15 | 16 | fun generate(length: Int): String = buildString { 17 | val keys = ngramStore.keys.toList() 18 | var cur = keys[Random.nextInt(0, ngramStore.size - 1)].also { append(it) } 19 | var size = 0 20 | while (size < length) { 21 | size += k 22 | cur = cur.drop(1) + ngramStore[cur]!!.random().also { append(it) } 23 | } 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/CancelingFlow.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.collect 7 | import kotlinx.coroutines.flow.flatMapLatest 8 | import kotlinx.coroutines.flow.flow 9 | 10 | @ExperimentalCoroutinesApi 11 | suspend fun main() { 12 | fun searchResults(query: String) = flow { 13 | var page = 1 14 | while (true) { 15 | delay(500) 16 | emit("result for $query, page ${page++}") 17 | } 18 | } 19 | 20 | fun userSearchQuery() = flow { 21 | val interval = (1000..3000) 22 | var query = 1 23 | while (true) { 24 | delay(interval.random().toLong()) 25 | emit("user query: ${query++}".also { log(it) }) 26 | } 27 | } 28 | 29 | userSearchQuery() 30 | .flatMapLatest { searchResults(it) } 31 | .collect { log(it) } 32 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/132pattern.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | import java.util.* 4 | import kotlin.math.max 5 | 6 | fun find132pattern(nums: IntArray): Boolean { 7 | val stack: Deque = ArrayDeque() 8 | var three = Int.MIN_VALUE 9 | 10 | for (i in nums.indices.reversed()) { 11 | val one = nums[i] 12 | if (one < three) { 13 | println("result is ${listOf(one, stack.peek(), three)}") 14 | return true 15 | } 16 | while (!stack.isEmpty() && stack.peek() < one) { 17 | three = max(stack.pop(), three) 18 | } 19 | // We're outside the while loop, stack.peek() >= one 20 | stack.push(one) 21 | } 22 | 23 | return false 24 | } 25 | 26 | fun main() { 27 | // find132pattern(intArrayOf(1, 0, 1, -4, -3)).also { println(it) } 28 | find132pattern(intArrayOf(3, 1, 4, 2)).also { println(it) } 29 | find132pattern(intArrayOf(-1, 3, 2, 0)).also { println(it) } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/QuickSort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | fun quickSort(arr: IntArray) { 4 | fun swap(i: Int, j: Int) { 5 | val temp = arr[j] 6 | arr[j] = arr[i] 7 | arr[i] = temp 8 | } 9 | 10 | fun partition(lo: Int, hi: Int): Int { 11 | var i: Int = lo 12 | var j: Int = hi + 1 13 | val pivotValue = arr[lo] 14 | while (true) { 15 | while (arr[++i] < pivotValue) if (i == hi) break 16 | while (arr[--j] > pivotValue) if (j == lo) break 17 | if (i >= j) break 18 | swap(i, j) 19 | } 20 | swap(lo, j) 21 | return j 22 | } 23 | 24 | fun sort(lo: Int, hi: Int) { 25 | if (hi <= lo) return 26 | val pivot = partition(lo, hi) 27 | sort(lo, pivot - 1) 28 | sort(pivot + 1, hi) 29 | } 30 | 31 | sort(0, arr.lastIndex) 32 | } 33 | 34 | fun main() { 35 | val arr = intArrayOf(5, 1, 4, 2, 3) 36 | quickSort(arr) 37 | println(arr.toList()) 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/backtrack/22_GenerateParenthesisDeepRecursive.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("ConvertToStringTemplate") 2 | 3 | package algorithms.leetcode.backtrack 4 | 5 | import algorithms.TracingDeepRecursiveFunction 6 | import algorithms.invoke 7 | 8 | data class Param( 9 | val left: Int, 10 | val right: Int, 11 | val str: String, 12 | ) { 13 | override fun toString(): String { 14 | return "$left, $right, str=$str" 15 | } 16 | } 17 | 18 | suspend fun main() { 19 | val result = mutableListOf() 20 | val generateParenthesis = TracingDeepRecursiveFunction { (left, right, str) -> 21 | if (left == 0 && right == 0) result += str 22 | if (left > 0) callRecursive(Param(left - 1, right + 1, str + '(')) 23 | if (right > 0) callRecursive(Param(left, right - 1, str + ')')) 24 | } 25 | 26 | val param = Param(3, 0, "") 27 | generateParenthesis(param) 28 | println(result) 29 | } 30 | 31 | -------------------------------------------------------------------------------- /src/main/kotlin/ktor/HtmlDemo.kt: -------------------------------------------------------------------------------- 1 | import io.ktor.application.* 2 | import io.ktor.html.* 3 | import io.ktor.http.* 4 | import io.ktor.response.* 5 | import io.ktor.routing.* 6 | import io.ktor.server.engine.* 7 | import io.ktor.server.netty.* 8 | import kotlinx.html.body 9 | import kotlinx.html.h1 10 | 11 | fun main(args: Array) { 12 | val server = embeddedServer(Netty, port = 8080) { 13 | routing { 14 | get("/") { 15 | call.respondText("Hello World!", ContentType.Text.Plain) 16 | } 17 | get("/demo") { 18 | call.respondText("HELLO WORLD!") 19 | } 20 | get("/html") { 21 | call.respondHtml { 22 | body { 23 | h1 { +"Hello World!" } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | server.start(wait = true) 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/kotlin/ktorm/KtormPgDemo.kt: -------------------------------------------------------------------------------- 1 | package ktorm 2 | 3 | import ktorm.User.primaryKey 4 | import org.ktorm.database.Database 5 | import org.ktorm.dsl.Query 6 | import org.ktorm.dsl.forEach 7 | import org.ktorm.dsl.from 8 | import org.ktorm.dsl.select 9 | import org.ktorm.schema.Table 10 | import org.ktorm.schema.int 11 | import org.ktorm.support.postgresql.PostgreSqlDialect 12 | import org.ktorm.support.postgresql.textArray 13 | 14 | 15 | val pg = Database.connect( 16 | url = "jdbc:postgresql://localhost:49156/postgres", 17 | user = "postgres", 18 | password = "123456", 19 | dialect = PostgreSqlDialect() 20 | ) 21 | 22 | object PgUser : Table("users") { 23 | val id = int("id") 24 | val friends = textArray("friends") 25 | } 26 | 27 | 28 | fun main() { 29 | val q: Query = pg.from(PgUser).select(PgUser.id, PgUser.friends) 30 | q.forEach { 31 | println("${it[PgUser.id]}, ${it[PgUser.friends]}") 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/ErrorPropogation.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.launch 6 | import kotlinx.coroutines.runBlocking 7 | 8 | fun main() = runBlocking { 9 | val job = GlobalScope.launch { // root coroutine with launch 10 | println("Throwing exception from launch") 11 | throw IndexOutOfBoundsException() // Will be printed to the console by Thread.defaultUncaughtExceptionHandler 12 | } 13 | job.join() 14 | println("Joined failed job") 15 | val deferred = GlobalScope.async { // root coroutine with async 16 | println("Throwing exception from async") 17 | throw ArithmeticException() // Nothing is printed, relying on user to call await 18 | } 19 | // try { 20 | // deferred.await() 21 | // println("Unreached") 22 | // } catch (e: ArithmeticException) { 23 | // println("Caught ArithmeticException") 24 | // } 25 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/LoadMoreRequestDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.MutableSharedFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.flow.take 9 | import kotlinx.coroutines.flow.takeWhile 10 | import kotlinx.coroutines.launch 11 | 12 | suspend fun main() { 13 | val loadMoreRequest = MutableSharedFlow() 14 | coroutineScope { 15 | launch { 16 | repeat(20) { 17 | delay(100) 18 | loadMoreRequest.emit(Unit) 19 | } 20 | } 21 | 22 | launch { 23 | while (true) { 24 | // loadMoreRequest.collect { 25 | // log("received event") 26 | // } 27 | 28 | loadMoreRequest.take(1).collect { 29 | log("received event") 30 | } 31 | 32 | log("calling api") 33 | delay(1000) 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/InitStateFlow.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.CoroutineScope 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.flow.MutableStateFlow 8 | import kotlinx.coroutines.flow.StateFlow 9 | import kotlinx.coroutines.flow.collect 10 | import kotlinx.coroutines.launch 11 | 12 | class Client { 13 | fun CoroutineScope.publish(): StateFlow { 14 | val state = MutableStateFlow(1) 15 | launch { 16 | // delay(1000) 17 | state.value = 2 18 | delay(1000) 19 | state.value = 3 20 | } 21 | return state 22 | } 23 | 24 | suspend fun publish2(flow: MutableStateFlow) { 25 | 26 | } 27 | } 28 | 29 | suspend fun main() { 30 | val client = Client() 31 | coroutineScope { 32 | val flow = with(client) { publish() } 33 | flow.collect { 34 | log("collected $it") 35 | } 36 | log("asdc") 37 | } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FlowQ3.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.flow.* 6 | 7 | @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) 8 | fun Flow.concurrentOnEach( 9 | scope: CoroutineScope, 10 | concurrency: Int = 10, 11 | block: suspend (T) -> Unit, 12 | ) = channelFlow { 13 | val chan = Channel(capacity = concurrency) 14 | scope.launch { 15 | for (item in chan) block(item) 16 | } 17 | collect { 18 | chan.send(it) 19 | send(it) 20 | } 21 | chan.close() 22 | } 23 | 24 | suspend fun main() { 25 | coroutineScope { 26 | flow { 27 | println("only once") 28 | repeat(20) { 29 | emit(it) 30 | } 31 | } 32 | .concurrentOnEach(this) { 33 | println("concurrent onEach: $it") 34 | } 35 | .collect { 36 | delay(500) 37 | println("onEach $it") 38 | } 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/RxApiDemo.kt: -------------------------------------------------------------------------------- 1 | package rx 2 | 3 | import io.reactivex.rxjava3.core.Observable 4 | import io.reactivex.rxjava3.core.ObservableTransformer 5 | import kotlin.random.Random 6 | 7 | data class ApiResponse(val code: Int, val data: String) 8 | 9 | 10 | fun generateResponse() = if (Random.nextBoolean()) 11 | ApiResponse(0, "Hello") else ApiResponse(50000, "Hello") 12 | 13 | val apiHandler = ObservableTransformer { upstream -> 14 | upstream 15 | ?.flatMap { 16 | if (it.code != 0) Observable.error(IllegalArgumentException("Whoops")) 17 | else Observable.just(it) 18 | } 19 | ?.doOnError { 20 | println("caught error: $it") 21 | } 22 | } 23 | 24 | fun main() { 25 | Observable.create { 26 | it.onNext(generateResponse()) 27 | }.compose(apiHandler) 28 | .doOnError { println("caught on ui: $it") } 29 | .retry(3) 30 | .onErrorComplete() 31 | .subscribe { 32 | println("success: received $it") 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/continuation/CallDelayManuallyDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.continuation 2 | 3 | import kotlinx.coroutines.delay 4 | import java.util.concurrent.CountDownLatch 5 | import kotlin.coroutines.Continuation 6 | import kotlin.coroutines.CoroutineContext 7 | import kotlin.coroutines.EmptyCoroutineContext 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.TimeSource 10 | 11 | @ExperimentalTime 12 | fun main() { 13 | val delay: suspend (Long) -> Unit = ::delay 14 | 15 | @Suppress("UNCHECKED_CAST") 16 | val df = delay as Function2, Any> 17 | 18 | val now = TimeSource.Monotonic.markNow() 19 | val latch = CountDownLatch(1) 20 | 21 | df(1000, object : Continuation { 22 | override val context: CoroutineContext = EmptyCoroutineContext 23 | 24 | override fun resumeWith(result: Result) { 25 | println("resumed: $result, elapsed: ${now.elapsedNow()}") 26 | latch.countDown() 27 | } 28 | }) 29 | 30 | latch.await() 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/toy/FlowImplDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow.toy 2 | 3 | import kotlinx.coroutines.InternalCoroutinesApi 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.FlowCollector 7 | import kotlinx.coroutines.flow.collect 8 | 9 | 10 | class FlowImpl( 11 | private val builder: suspend FlowCollector.() -> Unit 12 | ) : Flow { 13 | 14 | @OptIn(InternalCoroutinesApi::class) 15 | override suspend fun collect(collector: FlowCollector) = collector.builder() 16 | } 17 | 18 | fun Flow.map(block: suspend (value: T) -> R) = FlowImpl { 19 | collect { emit(block(it)) } 20 | } 21 | 22 | fun Flow.filter(block: suspend (value: T) -> Boolean) = FlowImpl { 23 | collect { if (block(it)) emit(it) } 24 | } 25 | 26 | 27 | suspend fun main() = FlowImpl { emit(1); emit(2) } 28 | .map { it * it } 29 | .filter { 30 | delay(1000) 31 | it % 2 == 0 32 | } 33 | .collect { println("collected $it") } 34 | 35 | -------------------------------------------------------------------------------- /src/main/kotlin/ktorm/KtormDemo.kt: -------------------------------------------------------------------------------- 1 | package ktorm 2 | 3 | import io.ktor.util.* 4 | import org.ktorm.database.Database 5 | import org.ktorm.dsl.* 6 | import org.ktorm.schema.Table 7 | import org.ktorm.schema.int 8 | import org.ktorm.schema.varchar 9 | import org.ktorm.support.mysql.insertOrUpdate 10 | 11 | val database = Database.connect( 12 | url = "jdbc:mysql://api.beta.jojotu.cn:3306/jojotoo_test", 13 | user = "test", 14 | password = "a0a40zDod4HjM84t" 15 | ) 16 | 17 | object User : Table("user") { 18 | val id = int("id").primaryKey() 19 | val name = varchar("user_nickname") 20 | val tel = varchar("user_tel_zone") 21 | val cityId = int("city_id") 22 | } 23 | 24 | fun main() { 25 | val q1 = database.from(User).select(User.id, User.name, User.tel) 26 | .limit(10, 10) 27 | val q2 = q1.where { 28 | User.cityId eq 1 29 | } 30 | println(q2.sql) 31 | 32 | 33 | val u1 = User.aliased("u1") 34 | val u2 = User.aliased("u2") 35 | database.from(u1) 36 | 37 | User.insertOrUpdate { } 38 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/jcip/MemoizerJcipDemo.kt: -------------------------------------------------------------------------------- 1 | package concurrency.jcip 2 | 3 | import java.util.concurrent.CancellationException 4 | import java.util.concurrent.ConcurrentHashMap 5 | import java.util.concurrent.Future 6 | import java.util.concurrent.FutureTask 7 | 8 | fun interface Computable { 9 | fun compute(arg: A): V 10 | } 11 | 12 | /** 13 | * Reference: JCIP Section 5.6 14 | */ 15 | class Memoizer( 16 | private val c: Computable, 17 | ) : Computable { 18 | 19 | private val cache = ConcurrentHashMap>() 20 | 21 | override fun compute(arg: A): V { 22 | while (true) { 23 | var f: Future? = cache[arg] 24 | if (f == null) { 25 | val ft = FutureTask { c.compute(arg) } 26 | f = cache.putIfAbsent(arg, ft) 27 | if (f == null) { 28 | f = ft 29 | ft.run() 30 | } 31 | } 32 | try { 33 | return f.get() 34 | } catch (e: CancellationException) { 35 | cache.remove(arg, f) 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/ContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlinx.coroutines.CoroutineName 4 | import kotlinx.coroutines.Job 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.launch 7 | import kotlin.coroutines.coroutineContext 8 | import kotlin.coroutines.resume 9 | import kotlin.coroutines.suspendCoroutine 10 | 11 | 12 | suspend fun main() { 13 | coroutineScope { 14 | // 在 Context 中添加 CoroutineName[Coco] 元素 15 | launch(CoroutineName("Coco")) { 16 | contextCheck() 17 | } 18 | } 19 | } 20 | 21 | // 调用链:foo->bar->baz 22 | suspend fun contextCheck() = bar() 23 | suspend fun bar() = baz() 24 | suspend fun baz() { 25 | // 在调用链中获取 Context 中的元素 26 | println(coroutineContext[CoroutineName]) 27 | 28 | val context = coroutineContext[Job] 29 | 30 | suspendCoroutine { 31 | println(it.context === context) 32 | it.resume(Unit) 33 | } 34 | 35 | coroutineScope { 36 | launch { 37 | println(coroutineContext[CoroutineName]) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/caching/CachingFlowDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.caching 2 | 3 | import kotlinx.coroutines.GlobalScope 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.* 6 | import kotlinx.coroutines.launch 7 | import kotlinx.coroutines.runBlocking 8 | 9 | class Box(val value: Int) 10 | 11 | 12 | suspend fun main() { 13 | 14 | val data = mutableListOf() 15 | 16 | val flow = flow { 17 | println("start emitting") 18 | emit(Box(1)) 19 | emit(Box(2)) 20 | emit(Box(3)) 21 | println("end emitting") 22 | }.cacheIn(data) 23 | 24 | 25 | flow.collect { 26 | println("1: $it") 27 | } 28 | 29 | println("data is $data") 30 | 31 | flow.collect { 32 | println("2: $it") 33 | } 34 | println("data is $data") 35 | } 36 | 37 | private fun Flow.cacheIn(mutableList: MutableList): Flow = flow { 38 | if (mutableList.isNotEmpty()) { 39 | mutableList.forEach { emit(it) } 40 | return@flow 41 | } 42 | collect { 43 | mutableList.add(it) 44 | emit(it) 45 | } 46 | } 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/kotlin/serialization/SerializationDemo.kt: -------------------------------------------------------------------------------- 1 | package serialization 2 | 3 | import kotlinx.serialization.SerialName 4 | import kotlinx.serialization.Serializable 5 | import kotlinx.serialization.decodeFromString 6 | import kotlinx.serialization.encodeToString 7 | import kotlinx.serialization.json.Json 8 | 9 | @Serializable 10 | data class ApiResponse( 11 | @Suppress("SpellCheckingInspection") 12 | val errcode: Int, 13 | val msg: String, 14 | val data: T, 15 | ) 16 | 17 | @Serializable 18 | sealed class Section 19 | 20 | @Serializable 21 | @SerialName("shop") 22 | data class ShopSection(val name: String) : Section() 23 | 24 | @Serializable 25 | @SerialName("subject") 26 | data class SubjectSection(val id: Int) : Section() 27 | 28 | fun main() { 29 | val jsonString = """ 30 | {"errcode":0,"msg":"ok","data":[{"type":"shop","name":"hello"},{"type":"subject","id":1}]} 31 | """.trimIndent() 32 | val result = Json.decodeFromString>>(jsonString) 33 | 34 | println(Json.encodeToString(result)) 35 | 36 | 37 | 38 | 39 | 40 | 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/AsyncCrash.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.async 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | 7 | suspend fun test(): Int { 8 | delay(1000) 9 | log("test") 10 | return 1 11 | // throw IllegalStateException("whoops in test") 12 | } 13 | 14 | suspend fun main() { 15 | // val result = GlobalScope.async { test() } 16 | // 17 | // result.await() 18 | 19 | val deferred = coroutineScope { 20 | async { 21 | test() 22 | }.also { 23 | log("in deferred end") 24 | } 25 | } 26 | 27 | val value = deferred.await() 28 | 29 | log("received $value") 30 | 31 | 32 | // try { 33 | // deferred.await() 34 | // } catch (e: Throwable) { 35 | // log("caught $e") 36 | // } 37 | 38 | 39 | // try { 40 | // val result = async { test() } 41 | // } catch (e: Throwable) { 42 | // log("caught $e") 43 | // } 44 | 45 | // delay(2000) 46 | // try { 47 | // result.await() 48 | // } catch (e: Throwable) { 49 | // log("caught $e") 50 | // } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/toy/FlowImplDemoWithoutReceiver.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow.toy 2 | 3 | import kotlinx.coroutines.delay 4 | 5 | typealias FlowCollector = suspend (value: T) -> Unit 6 | 7 | class FlowImpl2(private val builder: suspend (collector: FlowCollector) -> Unit) { 8 | 9 | suspend fun collect(collector: FlowCollector) = builder(collector) 10 | 11 | fun map(block: suspend (value: T) -> R) = FlowImpl2 { collector -> 12 | collect { value -> collector(block(value)) } 13 | } 14 | 15 | fun filter(block: suspend (value: T) -> Boolean) = FlowImpl2 { collector -> 16 | collect { value -> if (block(value)) collector(value) } 17 | } 18 | } 19 | 20 | suspend fun main() { 21 | FlowImpl2 { emit -> 22 | emit(1) 23 | emit(2) 24 | } 25 | .map { 26 | println("$it: in map") 27 | it * it 28 | } 29 | .filter { 30 | println("$it: in filter") 31 | println("$it: delaying in filter...") 32 | delay(3000) 33 | it % 2 == 0 34 | } 35 | .collect { 36 | println("$it: in collect") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/fp/ArrowExceptionDemo.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("UNUSED_PARAMETER") 2 | 3 | package fp 4 | 5 | import arrow.* 6 | import arrow.core.* 7 | import arrow.core.computations.either 8 | import arrow.core.extensions.* 9 | import arrow.typeclasses.* 10 | import kotlin.random.Random 11 | 12 | 13 | sealed class ServiceException 14 | object NetworkUnstable : ServiceException() 15 | object WrongToken : ServiceException() 16 | 17 | fun getToken(): Either = 18 | if (Random.nextBoolean()) "token".right() else NetworkUnstable.left() 19 | 20 | fun uploadFile(token: String): Either = 21 | if (Random.nextBoolean()) WrongToken.left() else "success".right() 22 | 23 | fun sendMessage(messageId: String): Either = "success".right() 24 | 25 | suspend fun doWork() = either { 26 | val token = getToken().bind() 27 | val messageId = uploadFile(token).bind() 28 | return@either sendMessage(messageId).bind() 29 | } 30 | 31 | suspend fun main() { 32 | doWork().also { 33 | println(it) 34 | } 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/jcip/MemoizerSuspendableDemo.kt: -------------------------------------------------------------------------------- 1 | package concurrency.jcip 2 | 3 | import kotlinx.coroutines.* 4 | import java.util.concurrent.ConcurrentHashMap 5 | import kotlin.time.ExperimentalTime 6 | import kotlin.time.TimeSource 7 | 8 | class SuspendableMemoizer( 9 | private val scope: CoroutineScope, 10 | private val computable: suspend (id: ID) -> V, 11 | ) { 12 | private val cache = ConcurrentHashMap>() 13 | suspend fun calculate(id: ID): V = cache.getOrPut(id) { 14 | scope.async(start = CoroutineStart.LAZY) { 15 | computable(id) 16 | } 17 | }.apply { start() }.await() 18 | } 19 | 20 | @OptIn(ExperimentalTime::class) 21 | suspend fun main() { 22 | val start = TimeSource.Monotonic.markNow() 23 | 24 | coroutineScope { 25 | val memoizer = SuspendableMemoizer(this) { id: Int -> 26 | println("computing id: $id") 27 | delay(id.toLong()) 28 | } 29 | launch { 30 | memoizer.calculate(500) 31 | } 32 | launch { 33 | memoizer.calculate(500) 34 | } 35 | } 36 | 37 | println("elapsed: ${start.elapsedNow()}") 38 | } 39 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/CaptureContextDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import kotlin.coroutines.CoroutineContext 4 | import kotlin.coroutines.coroutineContext 5 | import kotlin.coroutines.resume 6 | import kotlin.coroutines.suspendCoroutine 7 | 8 | suspend fun main() { 9 | println(checkCallerContext(coroutineContext)) // true 10 | println(checkContinuationContext()) // true 11 | } 12 | 13 | suspend fun checkCallerContext(callerContext: CoroutineContext): Boolean = 14 | // 不更新 context 的情况下和调用方的 context 相同 15 | callerContext === coroutineContext 16 | 17 | suspend fun checkContinuationContext(): Boolean { 18 | // suspendCoroutine 是连接 suspend 和回调的桥梁。 19 | // 传给它的 lambda 属于桥回调的那一边,不是 suspend 的 block, 20 | // 所以没有 coroutineContext。因此我们在桥的 suspend 这一边的时候 21 | // 保存一下这个 suspend 的 context 22 | val currentContext = coroutineContext 23 | 24 | // 通过 suspendCoroutine 获取当前 Continuation 25 | return suspendCoroutine { cont -> 26 | val contContext = cont.context 27 | // 两个 context 是相同的 28 | val isTheSame = contContext === currentContext 29 | cont.resume(isTheSame) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/RaceDeferredDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.Deferred 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.selects.select 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.TimeSource 10 | 11 | suspend fun data1(): Int { 12 | delay(500) 13 | return 1 14 | } 15 | 16 | suspend fun data2(): Int { 17 | delay(1000) 18 | return 2 19 | } 20 | 21 | /** 22 | * Race [Deferred] a la `Promise.race` in JavaScript. 23 | */ 24 | // https://github.com/Kotlin/kotlinx.coroutines/issues/424 25 | @OptIn(ExperimentalTime::class) 26 | suspend fun main() { 27 | val start = TimeSource.Monotonic.markNow() 28 | coroutineScope { 29 | val deferred: List> = listOf(async { data1() }, async { data2() }) 30 | 31 | val data = select { 32 | deferred.forEach { d -> d.onAwait { it } } 33 | } 34 | 35 | deferred.forEach { it.cancel() } 36 | println(data) 37 | } 38 | 39 | // A little more than 500ms. 40 | println("elapsed ${start.elapsedNow()}") 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/actor/ActorOverheadDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.actor 2 | 3 | import kotlinx.coroutines.ObsoleteCoroutinesApi 4 | import kotlinx.coroutines.channels.actor 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.runBlocking 7 | import kotlinx.coroutines.yield 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.measureTime 10 | 11 | val testSeq = (1..1000).asSequence() 12 | 13 | object ActorOverheadDemo { 14 | @ObsoleteCoroutinesApi 15 | suspend fun useActor() = coroutineScope { 16 | val actor = actor { 17 | val map = mutableSetOf() 18 | for (i in channel) map.add(i) 19 | } 20 | actor.offer(1) 21 | testSeq.forEach { actor.send(it) } 22 | actor.close() 23 | } 24 | 25 | suspend fun baseline() { 26 | val map = mutableSetOf() 27 | testSeq.forEach { map.add(it); yield() } 28 | } 29 | } 30 | 31 | @ObsoleteCoroutinesApi 32 | @ExperimentalTime 33 | fun main() = runBlocking { 34 | measureTime { ActorOverheadDemo.baseline() }.also { println(it) } 35 | measureTime { ActorOverheadDemo.useActor() }.also { println(it) } 36 | Unit 37 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/structuredconcurrency/StructuredJobDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost.structuredconcurrency 2 | 3 | import kotlinx.coroutines.* 4 | 5 | suspend fun main() = coroutineScope { 6 | val job = 7 | launch { 8 | launch { 9 | launch { delay(1000) } 10 | launch { delay(1000) } 11 | } 12 | launch { 13 | launch { delay(1000) } 14 | launch { delay(1000) } 15 | } 16 | } 17 | /** 18 | * After [Job] completes, child jobs are no longer visible in [Job.children]. 19 | * So we [delay] a bit in order to observe the complete tree. 20 | * 21 | * I've also tried other alternatives: 22 | * * [yield] does not work. 23 | * * [launch] root with [Dispatchers.Unconfined] works. 24 | * 25 | * This example also shows that [Job.children] attribute 26 | * could be accessed concurrently. 27 | */ 28 | delay(500) 29 | 30 | job.cancel() 31 | 32 | job.printAsTree() 33 | } 34 | 35 | fun Job.printAsTree(indent: Int = 0) { 36 | println("${" ".repeat(indent)}$this") 37 | children.forEach { 38 | it.printAsTree(indent + 2) 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/main/kotlin/fp/SyntaxDepInjectDemo.kt: -------------------------------------------------------------------------------- 1 | package fp 2 | 3 | class DslContext 4 | class HttpClient 5 | 6 | class Subject { 7 | var a: String = "" 8 | get() = "old" 9 | private set 10 | 11 | companion object { 12 | fun batchUpdate(subjects: List) { 13 | subjects.forEach { 14 | it.a = "new" 15 | } 16 | } 17 | } 18 | } 19 | 20 | class Poi 21 | 22 | interface FindSubjects { 23 | val context: DslContext 24 | fun findByIds(ids: List): List = TODO() 25 | } 26 | 27 | interface FindPoi { 28 | val client: HttpClient 29 | val context: DslContext 30 | fun findPoiById(id: String): Poi = TODO() 31 | } 32 | 33 | //---------------------- notice interface has multiple-inheritance 34 | interface LoadSubjectWithPoi : FindSubjects, FindPoi { 35 | fun load(ids: List) { 36 | val poi = findPoiById("hi") 37 | val subjects = findByIds(ids) 38 | // do stuff with poi and subjects 39 | } 40 | } 41 | 42 | fun main() { 43 | val loader = object : LoadSubjectWithPoi { 44 | override val context = DslContext() 45 | override val client = HttpClient() 46 | } 47 | loader.load(listOf(1, 2, 3)) 48 | } -------------------------------------------------------------------------------- /src/main/kotlin/serialization/MoshiDemo.kt: -------------------------------------------------------------------------------- 1 | package serialization 2 | 3 | import com.squareup.moshi.JsonClass 4 | import com.squareup.moshi.Moshi 5 | import kotlin.system.measureTimeMillis 6 | 7 | val moshi = Moshi.Builder().build() 8 | 9 | @JsonClass(generateAdapter = true) 10 | data class Post(val title: String, val body: String) 11 | 12 | fun main() { 13 | val a = moshi.adapter(Post::class.java).toJson(Post("foo", "bar")) 14 | .also { println(it) } 15 | // 16 | // 17 | // moshi.adapter(Post::class.java) 18 | // .lenient() 19 | // .fromJson("abc") 20 | 21 | 22 | measureTimeMillis { 23 | repeat(1000) { 24 | runCatching { 25 | moshi.adapter(Post::class.java) 26 | .lenient() 27 | .fromJson(""" 28 | {"title":"foo","body":"bar"} 29 | """.trimIndent()) 30 | } 31 | } 32 | }.let { println("result: $it") } 33 | 34 | measureTimeMillis { 35 | repeat(1000) { 36 | moshi.adapter(Post::class.java) 37 | .lenient() 38 | .fromJson(""" 39 | {"title":"foo","body":"bar"} 40 | """.trimIndent()) 41 | } 42 | }.let { println("result: $it") } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/kotlin/rutang/WeeklyActiveData.kt: -------------------------------------------------------------------------------- 1 | package rutang 2 | 3 | import java.time.DayOfWeek 4 | import java.time.LocalDate 5 | import java.time.Period 6 | import java.time.temporal.TemporalAdjuster 7 | import java.time.temporal.TemporalAdjusters 8 | import kotlin.time.days 9 | 10 | val mondays = sequence { 11 | var next = LocalDate.of(2020, 1, 1) 12 | val end = LocalDate.of(2020, 9, 1) 13 | while (true) { 14 | next = next.with(TemporalAdjusters.next(DayOfWeek.MONDAY)) 15 | if (next > end) break 16 | yield(next) 17 | } 18 | } 19 | 20 | fun main() { 21 | val sqlStatements = mutableListOf() 22 | for (monday in mondays) { 23 | val next = monday + Period.ofDays(7) 24 | val sql = """ 25 | select min(d), sum(r[1]), sum(r[2]) 26 | from ( 27 | select min(date) d, retention(toStartOfWeek(date, 1) = '$monday', toStartOfWeek(date, 1) = '$next') r 28 | from user_daily_api_records 29 | where toStartOfWeek(date, 1)>= '$monday' and toStartOfWeek(date , 1)<= '$next' 30 | and city_id = 107 31 | group by user_id ) 32 | """.trimIndent() 33 | sqlStatements.add(sql) 34 | } 35 | 36 | println(sqlStatements.joinToString(" union all ")) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/QuickSelect2.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | fun smallestK2(arr: IntArray, k: Int): IntArray { 4 | 5 | fun swap(x: Int, y: Int) { 6 | val temp = arr[y] 7 | arr[y] = arr[x] 8 | arr[x] = temp 9 | } 10 | 11 | fun partition(lo: Int, hi: Int): Int { 12 | var i = lo - 1 13 | var j = hi + 1 14 | val pv = arr[lo] 15 | 16 | while (true) { 17 | do i++ while (arr[i] < pv) 18 | do j-- while (arr[j] > pv) 19 | if (j <= i) break 20 | swap(i, j) 21 | } 22 | 23 | swap(lo, j) 24 | return j 25 | } 26 | 27 | 28 | var lo = 0 29 | var hi = arr.lastIndex 30 | 31 | while (lo < hi) { 32 | val p = partition(lo, hi) 33 | if (p > k) { 34 | hi = p - 1 35 | } else if (p < k) { 36 | lo = p + 1 37 | 38 | } else break 39 | 40 | } 41 | 42 | val ret = IntArray(k) 43 | 44 | for (i in 0 until k) { 45 | ret[i] = arr[i] 46 | } 47 | 48 | return ret 49 | } 50 | 51 | fun main() { 52 | // val arr = intArrayOf(1, 3, 5, 7, 2, 4, 6, 8) 53 | // println(smallestK(arr, 4).toList()) 54 | 55 | intArrayOf(1, 5, 2, 3, 4) 56 | .let { smallestK2(it, 4) } 57 | .also { println(it.toList()) } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/kotlin/fp/ReaderMonadDemo.kt: -------------------------------------------------------------------------------- 1 | package fp 2 | 3 | class ReaderMonadDemo { 4 | } 5 | 6 | class Reader(val run: (D) -> A) { 7 | 8 | inline fun map(crossinline fa: (A) -> B): Reader = Reader { 9 | d -> fa(run(d)) 10 | } 11 | 12 | inline fun flatMap(crossinline fa: (A) -> Reader): Reader = Reader { 13 | d -> fa(run(d)).run(d) 14 | } 15 | 16 | companion object Factory { 17 | fun just(a: A): Reader = Reader { _ -> a } 18 | 19 | fun ask(): Reader = Reader { it } 20 | } 21 | } 22 | 23 | data class GetHeroesContext( 24 | val view: View, 25 | val getSuperHeroesUseCase: GetSuperHeroesUseCase, 26 | val heroesRepository: HeroesRepository, 27 | val dataSources: List 28 | ) 29 | 30 | class GetSuperHeroesUseCase { 31 | // fun getSuperHeroes(): Reader> = Reader.ask().flatMap { 32 | // it.heroesRepository.getHeroes() 33 | // } 34 | } 35 | class HeroesRepository { 36 | fun getHeroes() = listOf( 37 | SuperHero("Superman"), SuperHero("Spider-Man") 38 | ) 39 | } 40 | class HeroDataSource 41 | class View 42 | 43 | class SuperHero(val name: String) 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/ExceptionDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | import java.lang.IllegalArgumentException 5 | 6 | suspend fun doStuff() { 7 | delay(500) 8 | throw IllegalStateException("whoops") 9 | } 10 | 11 | suspend fun test1() { 12 | supervisorScope { 13 | launch { 14 | doStuff() 15 | } 16 | } 17 | } 18 | 19 | fun someFuncAsync(block: () -> Unit) { 20 | block() 21 | } 22 | 23 | fun test3() { 24 | try { 25 | someFuncAsync { 26 | throw IllegalArgumentException("whoops") 27 | } 28 | 29 | } catch (e: Throwable) { 30 | println("caught $e") 31 | } 32 | } 33 | 34 | suspend fun test2() { 35 | coroutineScope { 36 | launch { 37 | delay(1000) 38 | throw IllegalArgumentException("whoops") 39 | } 40 | 41 | launch { 42 | delay(2000) 43 | println("hi") 44 | } 45 | } 46 | } 47 | 48 | suspend fun test5() { 49 | val scope = CoroutineScope(Job()) 50 | val shared = SupervisorJob() 51 | scope.launch(shared) { 52 | throw IllegalArgumentException("whoops") 53 | } 54 | scope.launch { 55 | delay(1000) 56 | println("hi") 57 | } 58 | } 59 | 60 | suspend fun main() { 61 | coroutineScope { 62 | test2() 63 | } 64 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/StackToQueue.kt: -------------------------------------------------------------------------------- 1 | package algorithms 2 | import java.util.ArrayDeque 3 | 4 | class MyQueue() { 5 | val input = ArrayDeque() 6 | val output = ArrayDeque() 7 | 8 | /** Initialize your data structure here. */ 9 | 10 | /** Push element x to the back of queue. */ 11 | fun push(x: Int) { 12 | input.push(x) 13 | } 14 | 15 | /** Removes the element from in front of queue and returns that element. */ 16 | fun pop(): Int { 17 | if (!output.isEmpty()) { 18 | return output.pop() 19 | } 20 | move() 21 | return output.pop() 22 | } 23 | 24 | /** Get the front element. */ 25 | fun peek(): Int { 26 | move() 27 | return output.peek() 28 | } 29 | 30 | /** Returns whether the queue is empty. */ 31 | fun empty(): Boolean { 32 | return output.isEmpty() && input.isEmpty() 33 | } 34 | 35 | private fun move() { 36 | while (!input.isEmpty()) { 37 | output.push(input.pop()) 38 | } 39 | } 40 | } 41 | 42 | fun main() { 43 | 44 | // ["MyQueue","push","push","peek","push","peek"] 45 | // [[],[1],[2],[],[3],[]] 46 | 47 | val q = MyQueue() 48 | q.push(1) 49 | q.push(2) 50 | q.peek().also { println(it) } 51 | q.push(3) 52 | q.peek().also { println(it) } 53 | 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/tips/List.ws.kts: -------------------------------------------------------------------------------- 1 | import kotlin.random.Random 2 | 3 | val a = listOf(1, 2, 3) 4 | val b = listOf(2, 3, 4) 5 | 6 | println(a + b) 7 | println(a - b) 8 | 9 | 10 | List(5) { 0 } 11 | 12 | 13 | listOf(1, 2, 3).count { it % 2 == 0 } 14 | 15 | listOf(1, 2, 3, 4, 5).take(2) 16 | 17 | "Hello World!".take(5) 18 | 19 | data class Test(val a: String, val b: String) 20 | 21 | val t = Test("Hello", "World") 22 | 23 | val result = with(t) { 24 | when { 25 | a.isEmpty() -> 1 26 | else -> null 27 | } 28 | } 29 | 30 | listOf(1, 2, 3).any { it % 2 == 0 } 31 | 32 | listOf(1, 3).find { it % 2 == 0 } 33 | 34 | 35 | a.size.coerceAtLeast(10) 36 | 37 | class User 38 | 39 | fun toUserModel(value: String) = User() 40 | 41 | fun save(user: User) = Unit 42 | fun toast(msg: String) = Unit 43 | 44 | "Peter" 45 | .let(::toUserModel) 46 | .let(::save) 47 | 48 | 49 | 50 | fun getErrorMsg(): String? = if (Random.nextBoolean()) "whoops" else null 51 | 52 | fun doPublish() { 53 | val msg = getErrorMsg() 54 | if (msg != null) { 55 | toast(msg) 56 | return // I could forget to `return` here. 57 | } 58 | // do other stuff 59 | } 60 | 61 | "abcde".removePrefix("mn") 62 | 63 | buildString { 64 | append("hi") 65 | } 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/EndlessFlowDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.ConflatedBroadcastChannel 5 | import kotlinx.coroutines.channels.actor 6 | import kotlinx.coroutines.flow.asFlow 7 | import kotlinx.coroutines.flow.collect 8 | import kotlinx.coroutines.flow.emitAll 9 | import kotlinx.coroutines.flow.flow 10 | 11 | sealed class Message 12 | 13 | object Read : Message() 14 | class Write(val value: Int) : Message() 15 | 16 | @OptIn(ExperimentalCoroutinesApi::class) 17 | val dataChannel = ConflatedBroadcastChannel(0) 18 | 19 | @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class, ObsoleteCoroutinesApi::class) 20 | suspend fun main() { 21 | 22 | val f = flow { 23 | emitAll(dataChannel.asFlow()) 24 | } 25 | 26 | coroutineScope { 27 | val actor = actor { 28 | for (msg in channel) { 29 | when (msg) { 30 | is Read -> Unit 31 | is Write -> { 32 | delay(500); dataChannel.send(msg.value) 33 | } 34 | } 35 | } 36 | } 37 | launch { 38 | delay(100) 39 | f.collect { println("collected $it") } 40 | } 41 | 42 | launch { 43 | delay(50) 44 | actor.send(Write(1)) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FlowQuestion.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi 4 | import kotlinx.coroutines.async 5 | import kotlinx.coroutines.channels.produce 6 | import kotlinx.coroutines.coroutineScope 7 | import kotlinx.coroutines.delay 8 | import kotlinx.coroutines.flow.* 9 | 10 | @OptIn(ExperimentalCoroutinesApi::class) 11 | fun Flow.concurrent( 12 | concurrency: Int, 13 | transform: suspend (T) -> U, 14 | ): Flow = flow { 15 | 16 | coroutineScope { 17 | val channel = produce(capacity = concurrency) { 18 | collect { send(async { transform(it) }) } 19 | } 20 | for (el in channel) { 21 | emit(el.await()) 22 | } 23 | } 24 | } 25 | 26 | suspend fun main() { 27 | val inputs = flow { 28 | println("will only print once") 29 | repeat(20) { 30 | emit(it) 31 | } 32 | } 33 | inputs // readInputs() 34 | .onEach { 35 | // extract... 36 | } 37 | .onEach { 38 | // 39 | } 40 | .transform { 41 | emit(it) 42 | } 43 | .transform { 44 | emit(it) 45 | emit(it) 46 | } 47 | .concurrent(5) { 48 | println("concurrently: $it") 49 | delay(1000) 50 | } 51 | .count() // or maintain in onEach 52 | .also { println("count: $it") } 53 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FlowOverheadDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.ConflatedBroadcastChannel 5 | import kotlinx.coroutines.flow.asFlow 6 | import kotlinx.coroutines.flow.emitAll 7 | import kotlinx.coroutines.flow.first 8 | import kotlinx.coroutines.flow.flow 9 | import kotlin.time.ExperimentalTime 10 | import kotlin.time.measureTime 11 | 12 | 13 | @OptIn(ObsoleteCoroutinesApi::class, ExperimentalCoroutinesApi::class, FlowPreview::class) 14 | class FlowOverheadDemo(val scope: CoroutineScope) { 15 | val range = (1..1_000) 16 | 17 | val data: Map = range.associateBy { it } 18 | suspend fun baseline() { 19 | for (i in range) { 20 | data[i]; yield() 21 | } 22 | } 23 | 24 | val _dataChannel = ConflatedBroadcastChannel(0) 25 | val flow = flow { 26 | // emit(1) 27 | emitAll(_dataChannel.asFlow()) 28 | } 29 | 30 | suspend fun useFlow() { 31 | for (i in range) { 32 | flow.first() 33 | } 34 | } 35 | } 36 | 37 | @OptIn(ExperimentalTime::class) 38 | suspend fun main() { 39 | coroutineScope { 40 | val demo = FlowOverheadDemo(this) 41 | measureTime { demo.baseline() }.also { println("baseline: $it") } 42 | measureTime { demo.useFlow() }.also { println("useFlow: $it") } 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FlowQ4.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.ExperimentalCoroutinesApi 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.* 6 | import kotlinx.coroutines.launch 7 | 8 | private object EndToken 9 | 10 | @Suppress("LocalVariableName") 11 | @OptIn(ExperimentalCoroutinesApi::class) 12 | fun Flow.concurrently( 13 | concurrency: Int = 10, 14 | block: suspend Flow.() -> Unit, 15 | ) = channelFlow { 16 | val _sharedFlow = MutableSharedFlow() 17 | 18 | @Suppress("UNCHECKED_CAST") 19 | val sharedFlow = _sharedFlow.takeWhile { it !is EndToken } as Flow 20 | 21 | launch { sharedFlow.block() } 22 | launch { sharedFlow.collect { send(it) } } 23 | delay(100) 24 | collect { _sharedFlow.emit(it as Any) } 25 | _sharedFlow.emit(EndToken) 26 | } 27 | 28 | suspend fun main() { 29 | flow { println("only once"); repeat(20) { emit(it + 1) } } 30 | .concurrently { 31 | delay(500) 32 | filter { it % 2 == 0 }.onEach { println("onEach $it") }.count().also { println("concurrent even: $it") } 33 | filter { it % 2 != 0 }.onEach { println("onEach $it") }.count().also { println("concurrent odd: $it") } 34 | } 35 | .collect { 36 | delay(200) 37 | println("collecting $it") 38 | } 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/QuickSelect.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort 2 | 3 | fun smallestK(arr: IntArray, k: Int): IntArray { 4 | 5 | fun swap(x: Int, y: Int) { 6 | val temp = arr[y] 7 | arr[y] = arr[x] 8 | arr[x] = temp 9 | } 10 | 11 | fun partition(lo: Int, hi: Int): Int { 12 | var i = lo + 1 13 | var j = hi 14 | val pv = arr[lo] 15 | 16 | while (true) { 17 | while (arr[i] < pv) { 18 | i++ 19 | if (i == hi) break 20 | } 21 | 22 | while (arr[j] > pv) { 23 | j-- 24 | if (j == 0) break 25 | } 26 | 27 | if (j <= i) break 28 | swap(i, j) 29 | } 30 | 31 | swap(lo, j) 32 | return j 33 | } 34 | 35 | 36 | var lo = 0 37 | var hi = arr.lastIndex 38 | 39 | while (lo < hi) { 40 | val p = partition(lo, hi) 41 | if (p > k) { 42 | hi = p - 1 43 | } else if (p < k) { 44 | lo = p + 1 45 | 46 | } else break 47 | 48 | } 49 | 50 | val ret = IntArray(k) 51 | 52 | for (i in 0 until k) { 53 | ret[i] = arr[i] 54 | } 55 | 56 | return ret 57 | } 58 | 59 | fun main() { 60 | // val arr = intArrayOf(1, 3, 5, 7, 2, 4, 6, 8) 61 | // println(smallestK(arr, 4).toList()) 62 | 63 | intArrayOf(1, 5, 2, 3, 4) 64 | .let { smallestK(it, 4) } 65 | .also { println(it.toList()) } 66 | 67 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/CombineFlow.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.coroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.flow.* 7 | import kotlinx.coroutines.launch 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.seconds 10 | 11 | @ExperimentalTime 12 | suspend fun main() { 13 | suspend fun search(inputSearch: String, appliedSort: String): String { 14 | delay(1.seconds) 15 | return "search result for $inputSearch, sort: $appliedSort" 16 | 17 | } 18 | 19 | val inputSearch = MutableSharedFlow(replay = 1) 20 | val appliedSort = MutableSharedFlow(replay = 1) 21 | 22 | val searchResultFlow = combine(inputSearch, appliedSort) { search, sort -> 23 | search(search, sort) 24 | } 25 | 26 | coroutineScope { 27 | inputSearch.emit("q0") 28 | appliedSort.emit("0") 29 | 30 | log("launch 1") 31 | launch { 32 | delay(2.seconds); inputSearch.emit("q1") 33 | delay(2.seconds); inputSearch.emit("q2") 34 | } 35 | 36 | log("launch 2") 37 | launch { 38 | delay(6.seconds); appliedSort.emit("1") 39 | delay(2.seconds); appliedSort.emit("2") 40 | } 41 | 42 | log("launch 3") 43 | launch { 44 | searchResultFlow.collect { log("collected $it") } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/kotlin/rx/RxDemo.kt: -------------------------------------------------------------------------------- 1 | package rx 2 | 3 | import io.reactivex.rxjava3.core.Flowable 4 | import io.reactivex.rxjava3.core.Observable 5 | import io.reactivex.rxjava3.core.Scheduler 6 | import io.reactivex.rxjava3.disposables.Disposable 7 | import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler 8 | import io.reactivex.rxjava3.internal.schedulers.NewThreadScheduler 9 | import io.reactivex.rxjava3.internal.schedulers.RxThreadFactory 10 | import io.reactivex.rxjava3.schedulers.Schedulers 11 | 12 | fun main() { 13 | 14 | val a: Disposable = Observable.just("hello world") 15 | .map { // 0 16 | println("[${Thread.currentThread().name}/map0]:$it") 17 | it.toUpperCase() 18 | } 19 | .subscribeOn(NewThreadScheduler(RxThreadFactory("demo"))) 20 | .map { // 1 21 | println("[${Thread.currentThread().name}/map1]:$it") 22 | it.toUpperCase() 23 | } 24 | .observeOn(Schedulers.computation()) 25 | .map { // 2 26 | println("[${Thread.currentThread().name}/map2]:$it") 27 | it.toUpperCase() 28 | } 29 | .observeOn(Schedulers.io()) 30 | .map { // 3 31 | println("[${Thread.currentThread().name}/map3]:$it") 32 | it.toUpperCase() 33 | } 34 | .subscribe { 35 | println("[${Thread.currentThread().name}/subscribe]: $it") 36 | } 37 | 38 | while (true) { 39 | 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/main/kotlin/string/Quadratic.kt: -------------------------------------------------------------------------------- 1 | package string 2 | 3 | import kotlin.system.measureTimeMillis 4 | 5 | fun subText(value: String): String { 6 | var len = 0.0 7 | return value.codePoints().toArray().fold("") { acc, c -> 8 | println(acc) 9 | len += (if (c > 256) 1.0 else 0.5) 10 | if (len > 15) { 11 | return@fold acc 12 | } 13 | acc + c.toChar() 14 | } 15 | } 16 | 17 | fun subText2(value: String): String { 18 | var len = 0.0 19 | 20 | return buildString { 21 | value.codePoints().takeWhile { 22 | len += (if (it > 256) 1.0 else 0.5) 23 | len < 15 24 | }.forEach { 25 | println(it.toChar()) 26 | append(it.toChar()) 27 | } 28 | } 29 | } 30 | 31 | private val charPool: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') 32 | 33 | fun randomString() = (1..10000) 34 | .map { i -> kotlin.random.Random.nextInt(0, charPool.size) } 35 | .map(charPool::get) 36 | .joinToString(""); 37 | 38 | 39 | fun main() { 40 | 41 | subText(randomString()) 42 | 43 | // val texts = (1..1000).map { randomString() } 44 | // 45 | // measureTimeMillis { 46 | // texts.map { subText2(it) }.let { println("result new is ${it.last()}") } 47 | // }.let { println("took $it") } 48 | // 49 | // measureTimeMillis { 50 | // texts.map { subText(it) }.let { println("result old is ${it.last()}") } 51 | // }.let { println("took $it") } 52 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FlowQ2.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import kotlinx.coroutines.FlowPreview 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.* 6 | import kotlinx.coroutines.flow.FlowCollector 7 | import kotlin.time.ExperimentalTime 8 | import kotlin.time.TimeSource 9 | 10 | @OptIn(FlowPreview::class) 11 | fun Flow.concurrentTransform( 12 | concurrency: Int, 13 | transform: suspend FlowCollector.(value: T) -> Unit, 14 | ): Flow = map { 15 | flow { transform(it) } 16 | }.flattenMerge(concurrency) 17 | 18 | 19 | suspend fun FlowCollector.second(i: Int) { 20 | delay((10 * i).toLong()) 21 | emit(i * i) 22 | } 23 | 24 | @OptIn(ExperimentalTime::class) 25 | suspend fun main() { 26 | val sourceFlow = flow { 27 | println("only once") 28 | repeat(20) { 29 | emit(it) 30 | } 31 | } 32 | 33 | val now1 = TimeSource.Monotonic.markNow() 34 | var count1 = 0 35 | sourceFlow 36 | .transform { second(it) } 37 | .collect { 38 | count1++ 39 | } 40 | println("counter1: $count1, took ${now1.elapsedNow()}") 41 | 42 | 43 | val now2 = TimeSource.Monotonic.markNow() 44 | var count2 = 0 45 | sourceFlow 46 | .concurrentTransform(5) { 47 | second(2) 48 | print(".") 49 | } 50 | .collect { 51 | count2++ 52 | } 53 | println("counter2: $count2, took ${now2.elapsedNow()}") 54 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/3_LongestNonRepeatingSubstring.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("FunctionName") 2 | 3 | package algorithms.leetcode 4 | 5 | fun lengthOfLongestSubstring(s: String): Int { 6 | // [abc]abcbb 7 | // a[bca]bcbb 8 | // ab[cab]cbb 9 | // abc[abc]bb 10 | // abca[bcb]b 11 | 12 | // [abc]bbcbb 13 | // a[bcb]bcbb 14 | // ab[cbb]cbb 15 | // abc[bbc]bb 16 | // abcabcbb 17 | 18 | if (s.isEmpty()) return 0 19 | val set = mutableMapOf() 20 | var l = 0 21 | var r = 0 22 | set[s[0]] = 1 23 | while (r + 1 < s.length) { 24 | val next = s[r + 1] 25 | if (set[next]?: 0 > 0) { 26 | set[next] = set[next]!! - 1 27 | l++ 28 | r++ 29 | } else { 30 | set[next] = (set[next] ?: 0) + 1 31 | r++ 32 | } 33 | } 34 | return r - l + 1 35 | } 36 | 37 | fun lengthOfLongestSubstring_nick(s: String): Int { 38 | var lo = 0 39 | var hi = 0 40 | var max = 0 41 | val set = hashSetOf() 42 | while (hi < s.length) { 43 | if (s[hi] in set) { 44 | set -= s[lo] 45 | lo++ 46 | // hi++ 47 | } else { 48 | set += s[hi] 49 | hi++ 50 | max = Math.max(set.size, max) 51 | } 52 | } 53 | 54 | return max 55 | } 56 | 57 | fun main() { 58 | // println(lengthOfLongestSubstring("abcabcbb")) 59 | 60 | println(lengthOfLongestSubstring_nick("aab")) 61 | // [a]ab 62 | // [aa]b 63 | // [a]ab 64 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/quick/Quickselect.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort.quick 2 | 3 | fun quickSelect(arr: IntArray, k: Int, partition: (arr: IntArray, i: Int, j: Int) -> Int): Int { 4 | var lo = 0 5 | var hi = arr.lastIndex 6 | while (lo < hi) { 7 | val p = partition(arr, lo, hi) 8 | if (p < k) { 9 | // lo - p - k - hi 10 | lo = p + 1 11 | } else if (p > k) { 12 | // lo - k - p - hi 13 | hi = p - 1 14 | 15 | } else break 16 | } 17 | return arr[k] 18 | } 19 | 20 | fun quickSelectRange(arr: IntArray, k: Int, partition: (arr: IntArray, i: Int, j: Int) -> Int): List { 21 | var lo = 0 22 | var hi = arr.lastIndex 23 | while (lo < hi) { 24 | val p = partition(arr, lo, hi) 25 | if (p < k) { 26 | // lo - p - k - hi 27 | lo = p + 1 28 | } else if (p > k) { 29 | // lo - k - p - hi 30 | hi = p - 1 31 | 32 | } else break 33 | } 34 | return arr.slice(0..k) 35 | } 36 | 37 | fun main() { 38 | // quickSelect(intArrayOf(1, 5, 2, 3, 4), 3, ::myHoarePartition) 39 | // .also { println(it) } 40 | // 41 | // quickSelectRange(intArrayOf(1, 5, 2, 3, 4), 3, ::myHoarePartition) 42 | // .also { println(it) } 43 | 44 | quickSelectRange(intArrayOf(5, 5, 5, 5, 5), 3, ::myHoarePartition) 45 | .also { println(it) } 46 | 47 | quickSelect(intArrayOf(1, 4, 3, 2, 5), 2, ::doWhileHoarePartition) 48 | .also { println(it) } 49 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/ContinuationInterceptorDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.CoroutineName 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.runBlocking 6 | import kotlin.coroutines.* 7 | 8 | object ContinuationInterceptorDemo { 9 | suspend fun hello(): String { 10 | // delay(100) 11 | println("2. in suspend function") 12 | return "Hi" 13 | } 14 | } 15 | 16 | /** 17 | * ``` 18 | * suspend fun main() { 19 | * println(hello()) 20 | * } 21 | * ``` 22 | */ 23 | fun main() { 24 | ContinuationInterceptorDemo::hello.startCoroutine(Continuation(Printer) { 25 | println("4. result is $it") 26 | }) 27 | } 28 | 29 | object Printer : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { 30 | override fun interceptContinuation(continuation: Continuation): Continuation { 31 | val newCont = object : Continuation { 32 | override val context: CoroutineContext = continuation.context 33 | 34 | override fun resumeWith(result: Result) { 35 | println("resuming with $result") 36 | continuation.resumeWith(result) 37 | } 38 | } 39 | println("1. returning $newCont") 40 | return newCont 41 | } 42 | 43 | override fun releaseInterceptedContinuation(continuation: Continuation<*>) { 44 | super.releaseInterceptedContinuation(continuation) 45 | println("3. in release") 46 | } 47 | } -------------------------------------------------------------------------------- /src/main/kotlin/cli/ObjectOrientedStyle.kt: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import kotlinx.cli.* 4 | import kotlinx.cli.default 5 | 6 | abstract class CliCommand : HasParser, HasAfterJobHook { 7 | override val parser = ArgParser("") 8 | 9 | abstract fun run() 10 | 11 | private val jobs = mutableListOf<() -> Unit>() 12 | 13 | fun main(args: Array) { 14 | parser.parse(args) 15 | run() 16 | jobs.forEach { it() } 17 | } 18 | 19 | override fun addPostRunJob(job: () -> Unit) { 20 | jobs.add(job) 21 | } 22 | } 23 | 24 | interface ShouldSendFile : HasOutput, HasAfterJobHook { 25 | fun outputOption(): SingleOption { 26 | val shouldSend by parser.option(ArgType.Boolean, "send").default(false) 27 | val output = parser.option(ArgType.String).default("output.csv") 28 | addPostRunJob { 29 | if (shouldSend) { 30 | println("sending file to ${output.value}") 31 | } 32 | } 33 | return output 34 | } 35 | } 36 | 37 | 38 | interface HasParser { 39 | val parser: ArgParser 40 | } 41 | 42 | 43 | interface HasOutput : HasParser { 44 | val output: String 45 | } 46 | 47 | interface HasAfterJobHook { 48 | fun addPostRunJob(job: () -> Unit) 49 | } 50 | 51 | class MyJob : CliCommand(), ShouldSendFile { 52 | override val output by outputOption() 53 | 54 | override fun run() { 55 | println("writing to file $output") 56 | } 57 | } 58 | 59 | fun main(args: Array) = MyJob().main(args) 60 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/StateFlowCollect.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.GlobalScope 5 | import kotlinx.coroutines.coroutineScope 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.flow.* 8 | import kotlinx.coroutines.launch 9 | 10 | suspend fun testSuspend(value: Int) { 11 | delay(1) 12 | } 13 | 14 | fun testNormal(value: Int) { 15 | 16 | } 17 | 18 | suspend fun hof() { 19 | } 20 | 21 | 22 | suspend fun main() { 23 | 24 | val a: suspend (value: Int) -> Unit = ::testSuspend 25 | // val b: suspend (value: Int) -> Unit = ::testNormal 26 | 27 | // val suspendingLambda: suspend () -> suspend (value: Int) -> Unit = suspend { 28 | // suspend { value -> Int 29 | // delay(1000) 30 | // } 31 | // } 32 | 33 | 34 | fun onReceiveFlow(state: StateFlow) { 35 | state.onEach { 36 | println("onEach $it") 37 | }.launchIn(GlobalScope) 38 | } 39 | 40 | val state = MutableStateFlow(0) 41 | onReceiveFlow(state) 42 | 43 | GlobalScope.launch { 44 | delay(1000) 45 | state.value += 1 46 | 47 | delay(1000) 48 | state.value += 1 49 | 50 | delay(1000) 51 | state.value += 1 52 | } 53 | 54 | while (true) { 55 | } 56 | // coroutineScope { 57 | // launch { 58 | // delay(1000) 59 | // state.value += 1 60 | // 61 | // delay(1000) 62 | // state.value += 1 63 | // 64 | // delay(1000) 65 | // state.value += 1 66 | // } 67 | // } 68 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/actor/DataStoreDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.actor 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.actor 5 | 6 | 7 | private sealed class Message { 8 | class Read(val key: String, val ack: CompletableDeferred) : Message() 9 | class Write(val key: String, val value: String, val ack: CompletableDeferred) : Message() 10 | } 11 | 12 | class SimpleDataStore(coroutineScope: CoroutineScope) { 13 | 14 | @OptIn(ObsoleteCoroutinesApi::class) 15 | private val actor = coroutineScope.actor { // this: ActorScope 16 | // state encapsulated inside the actor 17 | val cache = mutableMapOf() 18 | 19 | // looping over messages one by one 20 | for (msg in channel) { 21 | when (msg) { 22 | is Message.Read -> msg.ack.complete(cache[msg.key]) 23 | is Message.Write -> { 24 | cache[msg.key] = msg.value; 25 | msg.ack.complete(Unit) 26 | } 27 | } 28 | } 29 | } 30 | 31 | suspend fun read(key: String): String? { 32 | val ack = CompletableDeferred() 33 | actor.send(Message.Read(key, ack)) 34 | return ack.await() 35 | } 36 | 37 | suspend fun write(key: String, value: String) { 38 | val ack = CompletableDeferred() 39 | actor.send(Message.Write(key, value, ack)) 40 | return ack.await() 41 | } 42 | } 43 | 44 | suspend fun main() { 45 | coroutineScope { 46 | val ds = SimpleDataStore(this + Job()) 47 | 48 | ds.write("foo", "bar") 49 | println(ds.read("foo")) 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/RemoveDuplicateNode.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | data class ListNode( 4 | val value: Int, 5 | var next: ListNode? = null 6 | ) : Iterable { 7 | fun toDebugString() = "[${joinToString()}]" 8 | 9 | override fun toString(): String { 10 | return value.toString() 11 | } 12 | 13 | override fun iterator(): Iterator { 14 | return object : Iterator { 15 | var next: ListNode? = this@ListNode 16 | override fun hasNext(): Boolean { 17 | return next != null 18 | } 19 | 20 | override fun next(): Int { 21 | return next!!.value.also { 22 | next = next?.next 23 | } 24 | } 25 | } 26 | } 27 | } 28 | 29 | fun listNodesOf(vararg num: Int): ListNode? { 30 | if (num.isEmpty()) return null 31 | val head = ListNode(num.first()) 32 | var cur: ListNode? = head 33 | for (i in 1..num.lastIndex) { 34 | cur?.next = ListNode(num[i]) 35 | cur = cur?.next 36 | } 37 | return head 38 | } 39 | 40 | fun removeDuplicateNodes(head: ListNode?): ListNode? { 41 | val set = hashSetOf() 42 | 43 | var prev: ListNode? = null 44 | var cur = head 45 | 46 | while (cur != null) { 47 | if (cur.value in set) { 48 | prev?.next = cur.next 49 | } else { 50 | set += cur.value 51 | } 52 | prev = cur 53 | cur = cur.next 54 | } 55 | 56 | return head 57 | } 58 | 59 | // [1, 2, 3, 3, 2, 1] 60 | fun main() { 61 | val head = listNodesOf(1, 2, 3, 3, 2, 1) 62 | removeDuplicateNodes(head).also { println(it?.toDebugString()) } 63 | 64 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/bank/optimistic/TransferMoneyTest.kt: -------------------------------------------------------------------------------- 1 | package concurrency.bank.optimistic 2 | 3 | import java.util.concurrent.* 4 | import kotlin.concurrent.thread 5 | 6 | fun main() { 7 | val transactions = TransactionRepository() 8 | val bank = BankService(transactions, Executors.newCachedThreadPool()) 9 | 10 | val account1 = AccountImpl(1).apply { credit(100) } 11 | val account2 = AccountImpl(2).apply { credit(100) } 12 | 13 | // after tx1 - 1: 50, 2: 150 14 | // after tx2 - 1: 60, 2: 140 15 | val t1 = thread { 16 | bank.transfer(account1, account2, 50).getOrThrow() 17 | } 18 | val t2 = thread { 19 | bank.transfer(account2, account1, 10).getOrThrow() 20 | } 21 | 22 | t1.join() 23 | t2.join() 24 | println(account1) 25 | println(account2) 26 | 27 | val times = 1000 28 | val startGate = CountDownLatch(1) 29 | val endGate = CountDownLatch(times) 30 | val failedStats = ConcurrentHashMap() 31 | 32 | repeat(times) { 33 | thread { 34 | startGate.await() 35 | val r = ThreadLocalRandom.current() 36 | val from = if (r.nextBoolean()) account1 else account2 37 | val to = if (from == account1) account2 else account1 38 | bank.transfer(from, to, r.nextInt(10, 50)).getOrElse { 39 | failedStats.compute(it.toString()) { _, v: Int? -> (v ?: 0) + 1 } 40 | } 41 | endGate.countDown() 42 | } 43 | } 44 | 45 | startGate.countDown() 46 | endGate.await() 47 | 48 | println(account1) 49 | println(account2) 50 | require(account1.balance + account2.balance == 200) 51 | println("failed stats: $failedStats") 52 | } 53 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/Contest.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | import java.util.HashSet 4 | import java.util.HashMap 5 | 6 | 7 | fun numDifferentIntegers(word: String): Int { 8 | if (word.length == 1) { 9 | return if (Character.isDigit(word[0])) 1 else 0 10 | } 11 | val set: MutableSet = LinkedHashSet() 12 | var prev = word[0] 13 | var n = 0 14 | if (Character.isDigit(prev)) n = prev.toInt() 15 | for (i in 1 until word.length) { 16 | val c = word[i] 17 | if (Character.isDigit(c)) { 18 | n = 10 * n + (c - '0') 19 | } else { 20 | if (Character.isDigit(prev)) { 21 | set.add(n) 22 | n = 0 23 | } 24 | } 25 | prev = c 26 | } 27 | if (n != 0) set.add(n) 28 | return set.size 29 | } 30 | 31 | fun evaluate(s: String, knowledge: List>): String? { 32 | val dict: MutableMap = HashMap() 33 | val sb = StringBuilder() 34 | for (k in knowledge) { 35 | dict[k[0]] = k[1] 36 | } 37 | var i = 0 38 | while (i < s.length) { 39 | val c = s[i] 40 | if (c == '(') { 41 | for ((k, v) in dict) { 42 | var match = true 43 | for (j in 0 until k.length) { 44 | if (k[j] != s[i + j + 1]) { 45 | match = false 46 | break 47 | } 48 | } 49 | if (match) { 50 | sb.append(v) 51 | i = k.length + 2 52 | } 53 | } 54 | } else { 55 | sb.append(c) 56 | i++ 57 | } 58 | } 59 | return sb.toString() 60 | } 61 | 62 | fun main() { 63 | println(evaluate("(name)is(age)yearsold", listOf(listOf("name", "bob"), listOf("age", "two")))) 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/calculator/Expression.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode.calculator 2 | 3 | import java.util.* 4 | 5 | sealed class Expression { 6 | abstract fun interpret(): Int 7 | } 8 | 9 | class Number( 10 | private val n: Int, 11 | ) : Expression() { 12 | override fun interpret() = n 13 | } 14 | 15 | class Plus( 16 | private val lhs: Expression, 17 | private val rhs: Expression, 18 | ) : Expression() { 19 | override fun interpret(): Int = lhs.interpret() + rhs.interpret() 20 | } 21 | 22 | class Minus( 23 | private val lhs: Expression, 24 | private val rhs: Expression, 25 | ) : Expression() { 26 | override fun interpret(): Int = lhs.interpret() - rhs.interpret() 27 | } 28 | 29 | class Multiply( 30 | private val lhs: Expression, 31 | private val rhs: Expression, 32 | ) : Expression() { 33 | override fun interpret(): Int = lhs.interpret() * rhs.interpret() 34 | } 35 | 36 | class Divide( 37 | private val lhs: Expression, 38 | private val rhs: Expression, 39 | ) : Expression() { 40 | override fun interpret(): Int = lhs.interpret() / rhs.interpret() 41 | } 42 | 43 | fun evalRPN(tokens: Array): Int { 44 | val operands = Stack() 45 | for (s in tokens) { 46 | val i = s.toIntOrNull() 47 | if (i != null) { 48 | operands.push(i) 49 | continue 50 | } 51 | 52 | with(operands) { 53 | when (s) { 54 | "+" -> push(pop() + pop()) 55 | "-" -> push(pop().let { pop() - it }) 56 | "*" -> push(pop() * pop()) 57 | "/" -> push(pop().let { pop() / it }) 58 | else -> TODO() 59 | } 60 | } 61 | } 62 | return operands.pop() 63 | } 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/SuspendingLambdaDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import org.litote.kmongo.fields 4 | 5 | 6 | val normalLambda: (value: Int) -> Int = { 1 } 7 | 8 | val normalHigherLambda: () -> (value: Int) -> Int = { { 1 } } 9 | 10 | //val suspendingLambda: suspend (value: Int) -> Int = suspend { v -> 1 } 11 | 12 | val suspendingLambdaNoArg: suspend () -> Int = suspend { 1 } 13 | 14 | val suspendingHigherLambdaNoArg: suspend () -> suspend () -> Unit = suspend { suspend { } } 15 | 16 | class Test(val p: Int) { 17 | 18 | } 19 | 20 | fun interface MyInterface { 21 | fun hi() 22 | } 23 | 24 | interface MyInterface2: MyInterface { 25 | fun bonjour() 26 | fun greeting() 27 | } 28 | 29 | class Test3 { 30 | var myInterface: MyInterface? = null 31 | set(value) { 32 | field = value 33 | } 34 | 35 | var myInterfaceJava: MyInterfaceJava? = null 36 | set(value) { 37 | field = value 38 | } 39 | 40 | fun setListener(value: MyInterfaceJava) { 41 | 42 | } 43 | 44 | fun setListenerKt(value: MyInterface) { 45 | 46 | } 47 | } 48 | 49 | fun main() { 50 | 51 | val t = Test(1) 52 | t.p 53 | 54 | val t2 = Test2() 55 | t2.p 56 | 57 | // var a: MyInterface se 58 | 59 | val t3 = Test3().apply { 60 | myInterface = object: MyInterface2 { 61 | override fun bonjour() { 62 | TODO("Not yet implemented") 63 | } 64 | 65 | override fun greeting() { 66 | TODO("Not yet implemented") 67 | } 68 | 69 | override fun hi() { 70 | TODO("Not yet implemented") 71 | } 72 | 73 | } 74 | } 75 | 76 | Test3().setListenerKt { 77 | 78 | } 79 | 80 | Test3().setListener { 81 | 82 | } 83 | } 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/blogpost/ArrowDemo.kt: -------------------------------------------------------------------------------- 1 | package coroutine.blogpost 2 | 3 | import arrow.core.Either 4 | import arrow.core.computations.either 5 | import arrow.core.flatMap 6 | import arrow.core.left 7 | import arrow.core.right 8 | import coroutine.blogpost.CookingException.* 9 | import kotlinx.coroutines.CoroutineScope 10 | import kotlinx.coroutines.coroutineScope 11 | 12 | sealed class CookingException { 13 | object LettuceIsRotten : CookingException() 14 | object KnifeNeedsSharpening : CookingException() 15 | data class InsufficientAmount(val quantityInGrams: Int) : CookingException() 16 | } 17 | 18 | object Lettuce; object Knife; object Salad 19 | 20 | fun takeFoodFromRefrigerator(): Either = Lettuce.right() 21 | fun getKnife(): Either = Knife.right() 22 | fun lunch(knife: Knife, food: Lettuce): Either = InsufficientAmount(5).left() 23 | 24 | fun getSalad(): Either = 25 | takeFoodFromRefrigerator() 26 | .flatMap { lettuce -> 27 | getKnife() 28 | .flatMap { knife -> 29 | val salad = lunch(knife, lettuce) 30 | salad 31 | } 32 | } 33 | 34 | suspend fun getSalad2() = either { 35 | val lettuce = takeFoodFromRefrigerator().bind() 36 | val knife = getKnife().bind() 37 | val salad = lunch(knife, lettuce).bind() 38 | salad 39 | } 40 | 41 | suspend fun main() { 42 | // CoroutineScope() 43 | val salad = either { 44 | val lettuce = takeFoodFromRefrigerator().bind() 45 | val knife = getKnife().bind() 46 | val salad = lunch(knife, lettuce).bind() 47 | salad 48 | } 49 | println(salad) 50 | } 51 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/calculator/BasicCalculator1.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode.calculator 2 | 3 | import java.util.* 4 | 5 | fun calculate(s: String): Int { 6 | val exp: List = toRPN(s) 7 | val operands = Stack() 8 | with(operands) { 9 | for (c in exp) { 10 | if (c is Int) { 11 | push(c) 12 | continue 13 | } 14 | when (c) { 15 | '+' -> push(pop() + pop()) 16 | '-' -> push(pop().let { pop() - it }) 17 | '*' -> push(pop() * pop()) 18 | '/' -> push(pop().let { pop() / it }) 19 | } 20 | } 21 | } 22 | return operands.pop() 23 | } 24 | 25 | //val Char.weight get() = when(this) { 26 | // '+' -> 1 27 | // '-' -> 1 28 | // '(' -> 0 29 | // else -> TODO() 30 | //} 31 | 32 | fun toRPN(s: String): List { 33 | val ret = mutableListOf() 34 | val ops = Stack(); 35 | 36 | var i = 0 37 | while (i < s.length) { 38 | val c = s[i] 39 | if (c.isDigit()) { 40 | var n = c - '0' 41 | while (++i < s.length && s[i].isDigit()) { 42 | n = 10 * n + (s[i] - '0') 43 | } 44 | ret += n 45 | continue 46 | } 47 | 48 | when { 49 | c == ' ' -> { } 50 | c == '(' -> ops.push(c) 51 | c == ')' -> { 52 | while (ops.peek() != '(') ret += ops.pop() 53 | ops.pop() 54 | } 55 | c.isDigit() -> ret += c 56 | else -> { 57 | while (!ops.isEmpty() && ops.peek().weight >= c.weight) { 58 | ret += ops.pop() 59 | } 60 | ops.push(c) 61 | } 62 | } 63 | 64 | i++ 65 | } 66 | while (!ops.isEmpty()) ret += ops.pop() 67 | return ret 68 | } 69 | 70 | fun main() { 71 | calculate("2147483647").also { println(it) } 72 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/jcip/AqsValueLatch.kt: -------------------------------------------------------------------------------- 1 | package concurrency.jcip 2 | 3 | import java.util.concurrent.FutureTask 4 | import java.util.concurrent.locks.AbstractQueuedSynchronizer 5 | import kotlin.concurrent.thread 6 | 7 | /** 8 | * Reference 9 | * - JCIP, p.184 10 | * - FutureTask before jdk8 https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/concurrent/FutureTask.java 11 | */ 12 | class ValueLatch { 13 | private class Sync : AbstractQueuedSynchronizer() { 14 | private var result: T? = null 15 | 16 | override fun tryAcquireShared(arg: Int): Int { 17 | return if (state == 1) 1 else -1 18 | } 19 | 20 | override fun tryReleaseShared(arg: Int): Boolean { 21 | state = 1 22 | return true 23 | } 24 | 25 | fun innerGet(): T { 26 | acquireSharedInterruptibly(0) 27 | return result!! 28 | } 29 | 30 | fun innerSet(value: T) { 31 | result = value 32 | while (true) { 33 | if (state == 1) return 34 | if (compareAndSetState(0, 1)) { 35 | releaseShared(0) 36 | result = value 37 | } 38 | } 39 | } 40 | 41 | fun isSet(): Boolean { 42 | return state == 1 43 | } 44 | } 45 | 46 | private val sync = Sync() 47 | 48 | fun isSet(): Boolean { 49 | return sync.isSet() 50 | } 51 | 52 | fun setValue(value: T) { 53 | sync.innerSet(value) 54 | } 55 | 56 | fun getValue(): T { 57 | return sync.innerGet() 58 | } 59 | } 60 | 61 | fun main() { 62 | val latch = ValueLatch() 63 | 64 | thread { 65 | Thread.sleep(500) 66 | latch.setValue(10) 67 | } 68 | 69 | repeat(10) { 70 | thread { 71 | println("value is ${latch.getValue()}") 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/main/kotlin/dataloader/DataloaderDemo.kt: -------------------------------------------------------------------------------- 1 | package dataloader 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.Channel 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import java.util.concurrent.ConcurrentHashMap 7 | 8 | //interface BatchLoader { 9 | // suspend fun load(ids: List) 10 | //} 11 | 12 | class BatchLoader( 13 | block: suspend (id: List) -> List, 14 | ) { 15 | suspend fun CoroutineScope.load(id: ID): T { 16 | TODO() 17 | 18 | } 19 | } 20 | 21 | data class User(val id: Int, val invitedBy: Int) 22 | 23 | class BatchLoader2( 24 | val scope: CoroutineScope, 25 | val block: suspend (id: List) -> List, 26 | ) { 27 | private val cache = ConcurrentHashMap() 28 | 29 | private val ids = mutableSetOf() 30 | 31 | private val ids2Index = mutableMapOf() 32 | 33 | private val result = Channel>() 34 | 35 | 36 | @Synchronized 37 | fun load(id: ID): Deferred { 38 | ids2Index[id] = ids.size 39 | ids.add(id) 40 | 41 | return scope.async { 42 | val items = result.receive() 43 | val index = checkNotNull(ids2Index[id]) 44 | items[index] 45 | } 46 | } 47 | 48 | suspend fun dispatchAndJoin() { 49 | result.send(block(ids.toList())) 50 | result.close() 51 | } 52 | } 53 | 54 | suspend fun main() { 55 | // val loader = BatchLoader { 56 | // delay(1000) 57 | // listOf(User(1, 3), User(2, 4)) 58 | // } 59 | 60 | coroutineScope { 61 | val loader = BatchLoader2(this) { 62 | delay(1000) 63 | listOf(User(1, 3), User(2, 4)) 64 | } 65 | 66 | loader.load(1).also { println(it.await()) } 67 | loader.load(2).also { println(it.await()) } 68 | loader.dispatchAndJoin() 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/calculator/ShuntingYard.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode.calculator 2 | 3 | import java.lang.StringBuilder 4 | import java.util.* 5 | 6 | 7 | val Char.weight 8 | get() = when (this) { 9 | '+' -> 1 10 | '-' -> 1 11 | '*' -> 2 12 | '/' -> 2 13 | '(' -> 0 14 | else -> TODO("unrecognized [$this]") 15 | } 16 | 17 | fun shuntingYard(string: String): String { 18 | val ret = StringBuilder() 19 | val operators = Stack() 20 | 21 | for (c in string.toCharArray()) { 22 | if (Character.isLetter(c)) { 23 | ret.append(c) 24 | } else { 25 | while ( 26 | !operators.isEmpty() 27 | && operators.peek().weight >= c.weight 28 | ) ret.append(operators.pop()) 29 | 30 | operators.push(c) 31 | } 32 | } 33 | 34 | while (!operators.isEmpty()) ret.append(operators.pop()) 35 | 36 | return ret.toString() 37 | } 38 | 39 | fun shuntingYardWithBrackets(string: String): String { 40 | val ret = StringBuilder() 41 | val ops = Stack() 42 | 43 | for (c in string.toCharArray()) { 44 | when { 45 | Character.isLetter(c) -> ret.append(c) 46 | c == '(' -> ops.push(c) 47 | c == ')' -> { 48 | while (ops.peek() != '(') ret.append(ops.pop()) 49 | ops.pop() 50 | } 51 | else -> { 52 | while (!ops.isEmpty() && ops.peek().weight >= c.weight) { 53 | ret.append(ops.pop()) 54 | } 55 | ops.push(c) 56 | } 57 | } 58 | } 59 | 60 | while (!ops.isEmpty()) ret.append(ops.pop()) 61 | 62 | return ret.toString() 63 | } 64 | 65 | fun main() { 66 | shuntingYard("a+b*c-d").also { println(it) } // 123*+4- 67 | shuntingYardWithBrackets("a+(b+c)+d").also { println(it) } 68 | shuntingYardWithBrackets("a+(m+n*o-p)+d").also { println(it) } 69 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/RetriableCoroutine.kt: -------------------------------------------------------------------------------- 1 | package coroutine 2 | 3 | import kotlinx.coroutines.* 4 | import kotlinx.coroutines.channels.Channel 5 | import java.util.concurrent.atomic.AtomicInteger 6 | import kotlin.random.Random 7 | import kotlin.time.ExperimentalTime 8 | import kotlin.time.seconds 9 | 10 | suspend fun fetchCredential(): String { 11 | delay(1000) 12 | return "xxx-xxx-xxx" 13 | } 14 | 15 | suspend fun uploadImage(token: String, imageId: Int): String { 16 | log("starting to upload image $imageId with token $token") 17 | delay(1000) 18 | if (Random.nextBoolean()) throw RuntimeException("whoops, image $imageId failed") 19 | return "https://example.com/$imageId" 20 | } 21 | 22 | 23 | val retryRequest = Channel() 24 | 25 | val failedCount = AtomicInteger(0) 26 | 27 | suspend fun attempt(block: suspend () -> T): T { 28 | while (true) { 29 | try { 30 | return block() 31 | } catch (e: Throwable) { 32 | log("caught exception $e") 33 | failedCount.incrementAndGet() 34 | retryRequest.receive() 35 | } 36 | } 37 | } 38 | 39 | @ExperimentalTime 40 | suspend fun main() { 41 | val imagesToUpload = (1..5) 42 | 43 | // simulate user clicking retry button 44 | GlobalScope.launch { 45 | while (true) { 46 | delay(5.seconds) 47 | log("sending retry request") 48 | repeat(failedCount.getAndSet(0)) { retryRequest.send(Unit) } 49 | } 50 | } 51 | 52 | coroutineScope { 53 | val token = attempt { 54 | fetchCredential().also { log("fetched token $it") } 55 | } 56 | 57 | // val urls = imagesToUpload.map { 58 | // attempt { uploadImage(token, it) } 59 | // } 60 | 61 | val futures = imagesToUpload.map { 62 | async { attempt { uploadImage(token, it) } } 63 | } 64 | 65 | val urls = futures.map { it.await() } 66 | log("finished uploading $urls") 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FrpState.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("DuplicatedCode") 2 | 3 | package coroutine.flow 4 | 5 | import coroutine.log 6 | import kotlinx.coroutines.* 7 | import kotlinx.coroutines.flow.* 8 | 9 | @FlowPreview 10 | @ExperimentalCoroutinesApi 11 | suspend fun main() { 12 | 13 | fun Flow.handleLike(state: UiState): Flow = transform { 14 | emit(state.apply { loading = true }) 15 | log("call like api, position: ${it.position}") 16 | delay(500) 17 | emit(state.apply { likedPosition = it.position; loading = false }) 18 | } 19 | 20 | fun Flow.handleSearch(state: UiState): Flow = transform { 21 | emit(state.apply { loading = true }) 22 | log("call search api, query: ${it.value}") 23 | delay(200) 24 | emit(state.apply { query = it.value; loading = false }) 25 | } 26 | 27 | val refresh = MutableSharedFlow() 28 | val like = MutableSharedFlow() 29 | val search = MutableSharedFlow() 30 | 31 | coroutineScope { 32 | val actions: Flow = merge(refresh, like, search) 33 | 34 | launch { // simulate user actions 35 | delay(500); like.emit(UiAction.LikeArticle(1)) 36 | delay(500); search.emit(UiAction.SearchQuery("foo")) 37 | delay(500); like.emit(UiAction.LikeArticle(2)) 38 | delay(500); search.emit(UiAction.SearchQuery("bar")) 39 | } 40 | 41 | val state: Flow = flow { 42 | var last = UiState().also { emit(it) } 43 | 44 | val merged = merge( 45 | actions.filterIsInstance().handleSearch(last), 46 | actions.filterIsInstance().handleLike(last) 47 | ).onEach { last = it } 48 | 49 | emitAll(merged) 50 | } 51 | 52 | state.collect { 53 | log("💡----------------------------$it") 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/bank/optimistic/SimNetworkErrorTest.kt: -------------------------------------------------------------------------------- 1 | package concurrency.bank.optimistic 2 | 3 | import java.util.concurrent.* 4 | import kotlin.concurrent.thread 5 | 6 | private fun observe( 7 | account1: Account, 8 | account2: Account, 9 | transactions: TransactionRepository 10 | ) { 11 | println("account1: ${account1.balance}, account2: ${account2.balance}") 12 | 13 | val remaining = transactions.asSequence() 14 | .filter { it.status == Transaction.Status.DebitSuccess } 15 | .sumOf { it.amount } 16 | .also { println("remaining $it") } 17 | 18 | val total = account1.balance + account2.balance + remaining == 200 19 | val count = transactions.count { it.status == Transaction.Status.TxSuccess } 20 | println("total money: $total, success count $count") 21 | } 22 | 23 | fun main() { 24 | val executor = Executors.newCachedThreadPool() 25 | val transactions = TransactionRepository() 26 | val bank = BankService(transactions, executor) 27 | 28 | val account1 = RemoteAccount(1).apply { credit(100) } 29 | val account2 = RemoteAccount(2).apply { credit(100) } 30 | 31 | val times = 1000 32 | val startGate = CountDownLatch(1) 33 | val endGate = CountDownLatch(times) 34 | val failedStats = ConcurrentHashMap() 35 | 36 | repeat(times) { 37 | thread { 38 | startGate.await() 39 | val r = ThreadLocalRandom.current() 40 | val from = if (r.nextBoolean()) account1 else account2 41 | val to = if (from == account1) account2 else account1 42 | bank.transfer(from, to, r.nextInt(10, 50)).getOrElse { 43 | failedStats.compute(it.toString()) { _, v: Int? -> (v ?: 0) + 1 } 44 | } 45 | endGate.countDown() 46 | } 47 | } 48 | 49 | startGate.countDown() 50 | 51 | thread { 52 | Thread.sleep(50) 53 | account2.disconnect() 54 | Thread.sleep(70) 55 | account2.reconnect() 56 | } 57 | 58 | 59 | endGate.await() 60 | 61 | observe(account1, account2, transactions) 62 | println("failed stats: $failedStats") 63 | 64 | executor.shutdown() 65 | executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS) 66 | observe(account1, account2, transactions) 67 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/sort/quick/Quicksort.kt: -------------------------------------------------------------------------------- 1 | package algorithms.sort.quick 2 | 3 | fun swap(arr: IntArray, i: Int, j: Int) { 4 | val temp = arr[j] 5 | arr[j] = arr[i] 6 | arr[i] = temp 7 | } 8 | 9 | fun myHoarePartition(arr: IntArray, lo: Int, hi: Int): Int { 10 | val pv = arr[lo] 11 | var i = lo + 1 12 | var j = hi 13 | while (true) { 14 | while (arr[i] < pv) { 15 | i++ 16 | if (i == hi) break 17 | } 18 | while (arr[j] > pv) { 19 | j-- 20 | if (j == lo) break 21 | } 22 | if (j <= i) break 23 | swap(arr, i, j) 24 | } 25 | 26 | swap(arr, lo, j) 27 | return j 28 | } 29 | 30 | fun doWhileHoarePartition(arr: IntArray, lo: Int, hi: Int): Int { 31 | var i = lo 32 | var j = hi + 1 33 | val pv = arr[lo] 34 | while (true) { 35 | do i++ while (i <= hi && arr[i] < pv) 36 | do j-- while (j >= lo && arr[j] > pv) 37 | if (j < i) break 38 | swap(arr, i, j) 39 | } 40 | swap(arr, lo, j) 41 | return j 42 | } 43 | 44 | fun quickSort( 45 | arr: IntArray, 46 | partition: (arr: IntArray, lo: Int, hi: Int) -> Int 47 | ) { 48 | fun sort(lo: Int, hi: Int) { 49 | if (lo < hi) { 50 | val p = partition(arr, lo, hi) 51 | sort(lo, p - 1) 52 | sort(p + 1, hi) 53 | } 54 | } 55 | sort(0, arr.lastIndex) 56 | } 57 | 58 | fun quickSort(partition: (arr: IntArray, lo: Int, hi: Int) -> Int): (IntArray) -> Unit { 59 | return { arr: IntArray -> quickSort(arr, partition) } 60 | } 61 | 62 | fun main() { 63 | // intArrayOf(1, 5, 2, 3, 4) 64 | // .apply { quickSort(::myHoarePartition)(this) } 65 | // .also { println(it.toList()) } 66 | // 67 | // intArrayOf(1, 5, 5, 3, 4) 68 | // .apply { quickSort(::myHoarePartition)(this) } 69 | // .also { println(it.toList()) } 70 | 71 | intArrayOf(1, 5, 2, 3, 4) 72 | .apply { quickSort(::doWhileHoarePartition)(this) } 73 | .also { println(it.toList()) } 74 | 75 | intArrayOf(3, 3, 3, 3, 3) 76 | .apply { quickSort(::doWhileHoarePartition)(this) } 77 | .also { println(it.toList()) } 78 | 79 | // stuck... 80 | intArrayOf(3, 3, 3, 3, 3) 81 | .apply { quickSort(::myHoarePartition)(this) } 82 | .also { println(it.toList()) } 83 | } -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/LRUCache.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | class LRUCache(val capacity: Int) { 4 | private val map = mutableMapOf() 5 | private val head = Node(null, null, 0, 0) 6 | private val tail = Node(null, null, 0, 0) 7 | 8 | init { 9 | head.next = tail 10 | tail.prev = head 11 | } 12 | 13 | fun get(key: Int): Int { 14 | val node = map[key] ?: return -1 15 | remove(node) 16 | insertToHead(node) 17 | return node.value 18 | } 19 | 20 | fun put(key: Int, value: Int) { 21 | if (key in map) { 22 | val node = map[key]!! 23 | node.value = value 24 | remove(node) 25 | node.insertAfter(head) 26 | return 27 | } 28 | val node = Node(null, null, key, value) 29 | map[key] = node 30 | node.insertAfter(head) 31 | if (map.size > capacity) { 32 | map.remove(removeTail().key) 33 | } 34 | } 35 | 36 | private fun removeTail(): Node { 37 | val removing = tail.prev!! 38 | remove(removing) 39 | return removing 40 | } 41 | 42 | private fun remove(node: Node) { 43 | node.prev?.next = node.next 44 | node.next?.prev = node.prev 45 | } 46 | 47 | // node -> this 48 | private fun Node.insertAfter(node: Node) { 49 | val last = node.next 50 | node.next = this 51 | this.prev = node 52 | this.next = last 53 | last?.prev = this 54 | } 55 | 56 | 57 | private fun insertToHead(node: Node) { 58 | val replacing = head.next!! 59 | replacing.prev = node 60 | node.prev = head 61 | node.next = replacing 62 | head.next = node 63 | } 64 | 65 | private class Node( 66 | var prev: Node?, 67 | var next: Node?, 68 | val key: Int, 69 | var value: Int 70 | ) { 71 | override fun toString() = key.toString() 72 | } 73 | } 74 | 75 | fun main() { 76 | 77 | with(LRUCache(2)) { 78 | put(2, 1) 79 | put(1, 1) 80 | put(2, 3) 81 | put(4, 1) 82 | get(1).also { println(it) } // -1 83 | get(2).also { println(it) } 84 | } 85 | // 86 | // with(LRUCache(2)) { 87 | // put(1, 1) 88 | // put(2, 2) 89 | // get(1) 90 | // put(3, 3) 91 | // get(2).also { println(it) } 92 | // put(4, 4) 93 | // get(1) 94 | // get(3) 95 | // get(4) 96 | // } 97 | 98 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/bank/twopc/BankServiceImpl.kt: -------------------------------------------------------------------------------- 1 | package concurrency.bank.twopc 2 | 3 | import concurrency.bank.twopc.Transaction.State.* 4 | import java.util.concurrent.Executor 5 | import java.util.concurrent.atomic.AtomicInteger 6 | 7 | class AccountImpl( 8 | override val id: Int 9 | ) : Account { 10 | private val _balance = AtomicInteger() 11 | override val balance: Int get() = _balance.get() 12 | private val pending = mutableSetOf() 13 | 14 | override fun credit(amount: Int) { 15 | _balance.getAndUpdate { it + amount } 16 | } 17 | 18 | override fun debit(amount: Int) { 19 | _balance.getAndUpdate { it - amount } 20 | } 21 | 22 | override fun apply(id: Int, amount: Int) { 23 | synchronized(this) { 24 | if (id in pending) return 25 | _balance.getAndUpdate { it + amount } 26 | pending += id 27 | } 28 | } 29 | 30 | override fun commit(id: Int) { 31 | pending -= id 32 | } 33 | } 34 | 35 | class BankServiceImpl( 36 | private val executor: Executor 37 | ) : BankService { 38 | private val nextTxId = AtomicInteger(0) 39 | private val transactions = mutableMapOf() 40 | 41 | private fun save(tx: Transaction) { 42 | transactions[tx.id] = tx 43 | } 44 | 45 | private fun process(tx: Transaction): Transaction { 46 | val from = tx.from 47 | val to = tx.to 48 | return when (tx.state) { 49 | Initial -> process( 50 | tx.copy(state = Pending).also { save(it) } 51 | ) 52 | Pending -> { 53 | from.apply(tx.id, -tx.amount) 54 | to.apply(tx.id, tx.amount) 55 | process(tx.copy(state = Applied).also { save(it) }) 56 | } 57 | Applied -> { 58 | from.commit(tx.id) 59 | to.commit(tx.id) 60 | process(tx.copy(state = Done).also { save(it) }) 61 | } 62 | Done -> tx 63 | } 64 | } 65 | 66 | override fun transfer(from: Account, to: Account, amount: Int): Result { 67 | val tx = Transaction(nextTxId.getAndIncrement(), from, to, amount, Initial) 68 | return runCatching { 69 | process(tx) 70 | }.onFailure { 71 | executor.execute { 72 | var tx = tx 73 | while (tx.state != Done) { 74 | tx = process(tx) 75 | } 76 | } 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | 54 | 55 | -------------------------------------------------------------------------------- /src/main/kotlin/rx/ComparisonWithSequenceDemo.kt: -------------------------------------------------------------------------------- 1 | package rx.comparison 2 | 3 | import io.reactivex.rxjava3.core.Observable 4 | import kotlinx.coroutines.delay 5 | import kotlinx.coroutines.flow.* 6 | import kotlinx.coroutines.runBlocking 7 | import java.util.concurrent.Phaser 8 | import kotlin.time.ExperimentalTime 9 | import kotlin.time.seconds 10 | 11 | @ExperimentalTime 12 | suspend fun uploadImage(url: String): String { 13 | delay(1.seconds); println(url) 14 | return "url" 15 | } 16 | 17 | @ExperimentalTime 18 | fun withCoroutine() { 19 | sequenceOf("image-1", "image-2", "image-3") 20 | .map { 21 | return@map runBlocking { uploadImage(it) } 22 | } 23 | .filter { 24 | it.startsWith("abc") 25 | } 26 | } 27 | 28 | fun withAsyncCallback() { 29 | fun uploadImageAsync(url: String, onSuccess: (url: String) -> Unit) { 30 | val thread = Thread { 31 | Thread.sleep(1000) 32 | } 33 | thread.start() 34 | thread.join() 35 | onSuccess(url) 36 | } 37 | 38 | sequenceOf("image-1", "image-2", "image-3") 39 | .map { 40 | uploadImageAsync(it) { 41 | // ??? 42 | } 43 | } 44 | // .filter 45 | } 46 | 47 | fun withJdkConcurrencyControl() { 48 | fun uploadImageAsync(url: String, onSuccess: (url: String) -> Unit) { 49 | val thread = Thread { 50 | Thread.sleep(2000) 51 | } 52 | thread.start() 53 | thread.join() 54 | onSuccess("http://$url") 55 | } 56 | 57 | val phaser = Phaser(0) 58 | sequenceOf("image-1", "image-2", "image-3") 59 | .onEach { 60 | phaser.register() 61 | } 62 | .map { 63 | println("starting to upload $it") 64 | var url: String? = null 65 | uploadImageAsync(it) { 66 | println("received $it") 67 | phaser.arriveAndDeregister() 68 | url = it 69 | } 70 | return@map url!! 71 | } 72 | .filter { 73 | it.endsWith("1") 74 | } 75 | .forEach { 76 | println("Got it $it") 77 | } 78 | } 79 | 80 | fun withRxJava() { 81 | fun uploadImage(image: String): Observable = Observable.just("http://$image") 82 | 83 | Observable.just("image-1", "image-2", "image-3") 84 | .flatMap { 85 | uploadImage(it) 86 | } 87 | .filter { 88 | it.endsWith("1") 89 | } 90 | .subscribe { 91 | println(it) 92 | } 93 | } 94 | 95 | 96 | @ExperimentalTime 97 | suspend fun withFlow() { 98 | flowOf("image-1", "image-2", "image-3") 99 | .map { 100 | uploadImage(it) 101 | } 102 | .filter { it.endsWith("1") } 103 | .collectLatest { 104 | println(it) 105 | } 106 | } 107 | 108 | 109 | @ExperimentalTime 110 | fun main() { 111 | println("------------------------") 112 | withJdkConcurrencyControl() 113 | 114 | flow { 115 | emit(1) 116 | } 117 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/continuation/ManualContinuationExercise.kt: -------------------------------------------------------------------------------- 1 | package coroutine.continuation 2 | 3 | import kotlinx.coroutines.delay 4 | import java.util.concurrent.CountDownLatch 5 | import kotlin.coroutines.Continuation 6 | import kotlin.coroutines.CoroutineContext 7 | import kotlin.coroutines.EmptyCoroutineContext 8 | import kotlin.coroutines.resume 9 | import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED 10 | import kotlin.time.ExperimentalTime 11 | import kotlin.time.TimeSource 12 | 13 | 14 | /** 15 | * At compile time, Kotlin generates state-machine code for each suspend function. 16 | * 17 | * This exercise simulates the following suspend function. 18 | * 19 | * ```kotlin 20 | * suspend fun foo() { 21 | * delay(1000) 22 | * return 1 23 | * } 24 | * 25 | * suspend fun main() { 26 | * println(foo()) 27 | * } 28 | * ``` 29 | */ 30 | @ExperimentalTime 31 | fun main() { 32 | // Use a latch to keep the JVM spinning. 33 | val latch = CountDownLatch(1) 34 | // Record the elapsed time. 35 | val timer = TimeSource.Monotonic.markNow() 36 | 37 | foo(object : Continuation { 38 | // CoroutineContext is irrelevant in this example. 39 | override val context: CoroutineContext = EmptyCoroutineContext 40 | 41 | override fun resumeWith(result: Result) { 42 | println(result.getOrThrow()) 43 | latch.countDown() 44 | } 45 | }) 46 | 47 | latch.await() 48 | println("elapsed: ${timer.elapsedNow()}") 49 | } 50 | 51 | fun foo(continuation: Continuation): Any { 52 | // In effect, state machines sub-classes kotlin.coroutines.jvm.internal.ContinuationImpl. 53 | class FooContinuation : Continuation { 54 | var label: Int = 0 55 | 56 | // See kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith. 57 | // Source code does recursion unrolling, which complicates the code a bit. 58 | // Relevant commit: https://github.com/jetbrains/kotlin/commit/822faca75a14d27026f2f1034ac9a99976318f22 59 | // Note that the pre-optimization code in the above link is much easier to read. 60 | // Relevant issue: https://youtrack.jetbrains.com/issue/KT-18987 61 | override fun resumeWith(result: Result) { 62 | val outcome = invokeSuspend() 63 | if (outcome === COROUTINE_SUSPENDED) return 64 | continuation.resume(outcome) 65 | } 66 | 67 | fun invokeSuspend(): Any { 68 | return foo(this) 69 | } 70 | 71 | override val context: CoroutineContext = EmptyCoroutineContext 72 | } 73 | 74 | val cont = (continuation as? FooContinuation) ?: FooContinuation() 75 | return when (cont.label) { 76 | 0 -> { 77 | val delay: suspend (Long) -> Unit = ::delay 78 | 79 | @Suppress("UNCHECKED_CAST") 80 | val df = delay as Function2, Any> 81 | cont.label++ 82 | df(1000, cont) 83 | COROUTINE_SUSPENDED 84 | } 85 | 1 -> 1 // return 1 86 | else -> error("shouldn't happen") 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/test/kotlin/CustomConsumerTest.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.* 2 | import kotlinx.coroutines.flow.Flow 3 | import kotlinx.coroutines.flow.MutableStateFlow 4 | import kotlinx.coroutines.flow.StateFlow 5 | import kotlinx.coroutines.flow.collect 6 | import kotlinx.html.* 7 | import org.w3c.dom.events.Event 8 | import org.w3c.dom.html.HTMLElement 9 | import kotlin.coroutines.CoroutineContext 10 | import kotlin.coroutines.EmptyCoroutineContext 11 | import kotlin.coroutines.coroutineContext 12 | 13 | class TestConsumer : TagConsumer { 14 | override fun finalize() { 15 | TODO("Not yet implemented") 16 | } 17 | 18 | override fun onTagAttributeChange(tag: Tag, attribute: String, value: String?) { 19 | println("tag: $tag") 20 | println("attribute: $attribute") 21 | println("value: $value") 22 | } 23 | 24 | override fun onTagComment(content: CharSequence) { 25 | TODO("Not yet implemented") 26 | } 27 | 28 | override fun onTagContent(content: CharSequence) { 29 | println("content: $content") 30 | } 31 | 32 | override fun onTagContentEntity(entity: Entities) { 33 | TODO("Not yet implemented") 34 | } 35 | 36 | override fun onTagContentUnsafe(block: Unsafe.() -> Unit) { 37 | TODO("Not yet implemented") 38 | } 39 | 40 | override fun onTagEnd(tag: Tag) { 41 | println("tag end: $tag") 42 | } 43 | 44 | override fun onTagEvent(tag: Tag, event: String, value: (Event) -> Unit) { 45 | TODO("Not yet implemented") 46 | } 47 | 48 | override fun onTagStart(tag: Tag) { 49 | println("<${tag.tagName}> start: $tag") 50 | } 51 | 52 | suspend fun observe(data: Flow) { 53 | data.collect { 54 | println("change observed: $it") 55 | } 56 | } 57 | 58 | fun registerDynamic(value: Any) { 59 | println("registering dynamic $value") 60 | } 61 | } 62 | 63 | @ExperimentalCoroutinesApi 64 | class ObservableContent(content: Any) : 65 | CharSequence by content.toString(), 66 | MutableStateFlow by MutableStateFlow(content.toString()) { 67 | } 68 | 69 | @ExperimentalCoroutinesApi 70 | suspend fun main() { 71 | coroutineScope { 72 | val consumer = TestConsumer() 73 | val counter = MutableStateFlow(0) 74 | HTML(mapOf(), consumer).apply { 75 | body { 76 | p(classes = "foo bar") { 77 | withReactive(counter, this@coroutineScope) { +"counter is $it" } 78 | } 79 | } 80 | } 81 | counter.value++ 82 | } 83 | // while (true) {} 84 | } 85 | 86 | 87 | @ExperimentalCoroutinesApi 88 | fun Tag.withReactive(content: StateFlow, scope: CoroutineScope = GlobalScope, block: (CharSequence) -> Unit) { 89 | (consumer as? TestConsumer).also { 90 | // println("do reactive stuff") 91 | scope.launch { 92 | it?.observe(content) 93 | } 94 | } 95 | block(content.value.toString()) 96 | } 97 | 98 | 99 | -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | 88 | @rem Execute Gradle 89 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 90 | 91 | :end 92 | @rem End local scope for the variables with windows NT shell 93 | if "%ERRORLEVEL%"=="0" goto mainEnd 94 | 95 | :fail 96 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 97 | rem the _cmd.exe /c_ return code! 98 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 99 | exit /b 1 100 | 101 | :mainEnd 102 | if "%OS%"=="Windows_NT" endlocal 103 | 104 | :omega 105 | -------------------------------------------------------------------------------- /src/main/kotlin/algorithms/leetcode/GoodPair.kt: -------------------------------------------------------------------------------- 1 | package algorithms.leetcode 2 | 3 | import java.util.HashMap 4 | 5 | 6 | fun countNicePairs(nums: IntArray): Int { 7 | // int[] sub = new int[nums.length]; 8 | val map: MutableMap = HashMap() 9 | for (i in nums.indices) { 10 | val result = nums[i] - rev(nums[i]) 11 | val newValue = map.getOrDefault(result, 0) + 1 12 | map[result] = newValue 13 | } 14 | var ret = 0 15 | for (n in map.values) { 16 | if (n > 1) 17 | ret += fact(n) / (fact(n - 2) * 2) 18 | } 19 | return ret 20 | // return fact(n) / fact(n - 1); 21 | 22 | //int[] revs = new int[nums.length]; 23 | //for (int i = 0; i < nums.length; i++) { 24 | // revs[i] = rev(nums[i]); 25 | //} 26 | //int ret = 0; 27 | //for (int i = 0; i < nums.length; i++) { 28 | // for (int j = i + 1; j < nums.length; j++) { 29 | // if (nums[i] + revs[j] == nums[j] + revs[i]) { 30 | // ret++; 31 | // } 32 | // } 33 | //} 34 | //return ret; 35 | } 36 | 37 | private fun c(n: Int): Int { 38 | // n * 39 | 40 | TODO() 41 | } 42 | 43 | private fun fact(n: Int): Int { 44 | var ret = 1 45 | for (i in 1..n) ret *= i 46 | return ret 47 | } 48 | 49 | private fun rev(n: Int): Int { 50 | var n = n 51 | var ret = 0 52 | val t = 10 53 | while (n != 0) { 54 | ret = ret * 10 + n % t 55 | n /= 10 56 | } 57 | return ret 58 | } 59 | 60 | 61 | fun main() { 62 | countNicePairs(intArrayOf(8047408, 63 | 192867140, 64 | 497837845, 65 | 279787822, 66 | 151999002, 67 | 168514912, 68 | 193424242, 69 | 399636844, 70 | 132424231, 71 | 476736524, 72 | 267958611, 73 | 493350382, 74 | 476382727, 75 | 232939232, 76 | 197000791, 77 | 295291645, 78 | 126313621, 79 | 374645524, 80 | 7956597, 81 | 1376731, 82 | 496463745, 83 | 234481430, 84 | 359130803, 85 | 287625836, 86 | 250572050, 87 | 42311324, 88 | 477434624, 89 | 493231448, 90 | 493231244, 91 | 150494051, 92 | 184645534, 93 | 365252413, 94 | 495764582, 95 | 335976531, 96 | 384564332, 97 | 377151623, 98 | 198736741, 99 | 335161533, 100 | 245552540, 101 | 194897341, 102 | 83911938, 103 | 220562020, 104 | 496645745, 105 | 287151782, 106 | 374635526, 107 | 372483324, 108 | 485101584, 109 | 271797172, 110 | 244949442, 111 | 254333303, 112 | 251635002, 113 | 459181805, 114 | 472392123, 115 | 241350140, 116 | 256121502, 117 | 336895621, 118 | 354635302, 119 | 358909704, 120 | 194525491, 121 | 3606063, 122 | 194150341, 123 | 63477436, 124 | 341936141, 125 | 60299206, 126 | 69811896, 127 | 369928813, 128 | 229926920, 129 | 435310522, 130 | 299542980, 131 | 463777364, 132 | 164534512, 133 | 305885501, 134 | 437181734, 135 | 74288247, 136 | 487281835, 137 | 171161022, 138 | 423966312, 139 | 496989544, 140 | 452633252, 141 | 252433101, 142 | 141565141, 143 | 315895501, 144 | 478897927, 145 | 232532230, 146 | 472451262, 147 | 160504114, 148 | 476666674, 149 | 6179716, 150 | 251483002, 151 | 474777474, 152 | 443532332, 153 | 475808424, 154 | 457514604, 155 | 400936002, 156 | 384878483, 157 | 172616122, 158 | 283292232, 159 | 165645615, 160 | 392000144, 161 | 378636873)) 162 | // countNicePairs(intArrayOf(432835222, 112141211, 5408045, 456281503, 283322436, 414281561, 37773, 286505682)) 163 | // fact(3).also {println(it)} 164 | // countNicePairs(intArrayOf(42,11,1,97)).also { println(it) } 165 | // countNicePairs(intArrayOf(13,10,35,24,76)).also{ println(it) } 166 | } -------------------------------------------------------------------------------- /src/main/kotlin/coroutine/flow/FunctionalReactiveUI.kt: -------------------------------------------------------------------------------- 1 | package coroutine.flow 2 | 3 | import coroutine.log 4 | import kotlinx.coroutines.* 5 | import kotlinx.coroutines.flow.* 6 | 7 | sealed class UiAction { 8 | object Refresh : UiAction() 9 | class LikeArticle(val position: Int) : UiAction() 10 | class SearchQuery(val value: String) : UiAction() 11 | } 12 | 13 | data class UiState( 14 | var likedPosition: Int? = null, 15 | var query: String? = null, 16 | var loading: Boolean = false 17 | ) 18 | 19 | fun Flow.handleLike(state: UiState) = transform { 20 | emit(state.apply { loading = true }) 21 | log("call like api, position: ${it.position}") 22 | delay(500) 23 | emit(state.apply { likedPosition = it.position; loading = false }) 24 | } 25 | 26 | fun Flow.test1(): Flow = transform { 27 | log("in test1 LikeArticleTransformer") 28 | emit(UiState().apply { likedPosition = it.position }) 29 | } 30 | 31 | fun Flow.handleSearch(state: UiState) = transform { 32 | emit(state.apply { loading = true }) 33 | log("call search api, query: ${it.value}") 34 | delay(200) 35 | emit(state.apply { query = it.value; loading = false }) 36 | } 37 | 38 | fun Flow.test2() = transform { 39 | log("in test2 SearchQueryTransformer") 40 | emit(UiState().apply { query = it.value }) 41 | } 42 | 43 | @ExperimentalCoroutinesApi 44 | suspend fun main() { 45 | val refresh = MutableSharedFlow() 46 | val like = MutableSharedFlow() 47 | val search = MutableSharedFlow() 48 | 49 | coroutineScope { 50 | launch { 51 | delay(500); like.emit(UiAction.LikeArticle(1)) 52 | delay(500); search.emit(UiAction.SearchQuery("foo")) 53 | delay(500); like.emit(UiAction.LikeArticle(2)) 54 | delay(500); search.emit(UiAction.SearchQuery("bar")) 55 | } 56 | 57 | val flow = merge(refresh, like, search).shareIn(GlobalScope, SharingStarted.Eagerly) 58 | 59 | 60 | val flow2 = flow.scan(UiState()) { state, action -> 61 | when (action) { 62 | is UiAction.Refresh -> state 63 | is UiAction.LikeArticle -> state.apply { likedPosition = action.position } 64 | is UiAction.SearchQuery -> state.apply { query = action.value } 65 | } 66 | } 67 | 68 | val flow3: Flow = flow.flatMapLatest { 69 | log("in flatMapLatest: $it") 70 | merge( 71 | flow.map { log("1 here is $it"); it }.filterIsInstance().test1(), 72 | flow.map { log("2 here is $it");it }.filterIsInstance().map { UiState(query = "in map") }, 73 | flow.map { log("3 here is $it");it }.filterIsInstance().map { log("in map 3");it } 74 | .test2(), 75 | // flowOf(UiState(query = "static")) 76 | ).onEach { 77 | log("in onEach $it") 78 | } 79 | } 80 | 81 | 82 | val flow4 = merge( 83 | flow.filterIsInstance().test1(), 84 | flow.filterIsInstance().test2() 85 | ) 86 | 87 | val flow5 = flow.transform { 88 | log("transforming $it") 89 | emitAll(merge( 90 | flow.filterIsInstance().test1(), 91 | flow.filterIsInstance().test2() 92 | )) 93 | // emitAll() 94 | } 95 | 96 | val flow6 = flow { 97 | var last = UiState().also { emit(it) } 98 | val merged = merge( 99 | flow.filterIsInstance().handleLike(last), 100 | flow.filterIsInstance().handleSearch(last) 101 | ) 102 | merged.onEach { last = it } 103 | emitAll(merged) 104 | } 105 | 106 | log("starting to collect") 107 | 108 | flow6.collect { 109 | val a= "🥰".trimIndent() 110 | val b = "❤" 111 | log("💡----------------------------$it") 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/bank/twopc/BankService.kt: -------------------------------------------------------------------------------- 1 | package concurrency.bank.twopc 2 | 3 | import java.util.concurrent.* 4 | import java.util.concurrent.atomic.AtomicBoolean 5 | import kotlin.concurrent.thread 6 | 7 | /** 8 | * Simulates performing two-phase commits as described in 9 | * https://docs.mongodb.com/v3.4/tutorial/perform-two-phase-commits/ 10 | */ 11 | 12 | data class Transaction( 13 | val id: Int, 14 | val from: Account, 15 | val to: Account, 16 | val amount: Int, 17 | val state: State 18 | ) { 19 | enum class State { Initial, Pending, Applied, Done } 20 | } 21 | 22 | interface Account { 23 | val id: Int 24 | val balance: Int 25 | 26 | /** 27 | * [credit] and [debit] are helper methods mainly used for initializing accounts. 28 | */ 29 | fun credit(amount: Int) 30 | fun debit(amount: Int) 31 | 32 | /** 33 | * @param id: transaction id 34 | */ 35 | fun apply(id: Int, amount: Int) 36 | 37 | /** 38 | * @param id: transaction id 39 | */ 40 | fun commit(id: Int) 41 | } 42 | 43 | interface BankService { 44 | fun transfer(from: Account, to: Account, amount: Int): Result 45 | } 46 | 47 | class RemoteAccount(private val delegate: Account) : Account { 48 | private val disconnected = AtomicBoolean(false) 49 | override val id: Int get() = throwIfDisconnected { delegate.id } 50 | override val balance: Int get() = throwIfDisconnected { delegate.balance } 51 | override fun credit(amount: Int) = throwIfDisconnected { delegate.credit(amount) } 52 | override fun debit(amount: Int) = throwIfDisconnected { delegate.debit(amount) } 53 | override fun apply(id: Int, amount: Int): Unit = throwIfDisconnected { delegate.apply(id, amount) } 54 | override fun commit(id: Int): Unit = throwIfDisconnected { delegate.commit(id) } 55 | fun disconnect() = disconnected.set(true) 56 | fun reconnect() = disconnected.set(false) 57 | private fun throwIfDisconnected(block: () -> T): T = 58 | if (disconnected.get()) throw error("Account ${delegate.id} offline") 59 | else block() 60 | } 61 | 62 | fun main() { 63 | val account1 = RemoteAccount(AccountImpl(1).apply { credit(100) }) 64 | val account2 = RemoteAccount(AccountImpl(2).apply { credit(100) }) 65 | val executor = Executors.newFixedThreadPool(500) 66 | val bank = BankServiceImpl(executor) 67 | 68 | val times = 5000 69 | val startGate = CountDownLatch(1) 70 | repeat(times) { 71 | executor.submit { 72 | startGate.await() 73 | val r = ThreadLocalRandom.current() 74 | val from = if (r.nextBoolean()) account1 else account2 75 | val to = if (from == account1) account2 else account1 76 | runCatching { bank.transfer(from, to, r.nextInt(10, 50)) } 77 | } 78 | } 79 | 80 | // Randomly disconnects accounts. (Simulates network errors.) 81 | repeat(20) { 82 | executor.submit { 83 | startGate.await() 84 | account1.disconnect() 85 | Thread.sleep(ThreadLocalRandom.current().nextInt(10, 20).toLong()) 86 | account1.reconnect() 87 | } 88 | executor.submit { 89 | startGate.await() 90 | account2.disconnect() 91 | Thread.sleep(ThreadLocalRandom.current().nextInt(10, 20).toLong()) 92 | account2.reconnect() 93 | } 94 | } 95 | 96 | val observer = thread { 97 | startGate.await() 98 | try { 99 | while (Thread.currentThread().isAlive) { 100 | runCatching { 101 | val b1: Int = account1.balance 102 | val b2: Int = account2.balance 103 | if (b1 + b2 != 200) { 104 | println("inconsistency observed: account1 $b1, account2 $b2, sum ${b1 + b2}") 105 | } 106 | }.onFailure { println(it.message) } 107 | Thread.sleep(1) 108 | } 109 | } catch (e: InterruptedException) { 110 | } 111 | } 112 | 113 | startGate.countDown() 114 | 115 | executor.shutdown() 116 | executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS) 117 | observer.interrupt() 118 | 119 | require(account1.balance + account2.balance == 200) 120 | } -------------------------------------------------------------------------------- /src/main/kotlin/compose/ComposeToy.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("FunctionName") 2 | 3 | package compose 4 | 5 | /** 6 | * http://intelligiblebabble.com/compose-from-first-principles/ 7 | */ 8 | 9 | interface Composer { 10 | fun emit(node: Node, content: () -> Unit = {}) 11 | } 12 | 13 | class ComposerImpl(root: Node) : Composer { 14 | private var current: Node = root 15 | 16 | override fun emit(node: Node, content: () -> Unit) { 17 | // store current parent to restore later 18 | val parent = current 19 | parent.children.add(node) 20 | current = node 21 | // with `current` set to `node`, we execute the passed in lambda 22 | // in the "scope" of it, so that emitted nodes inside of this 23 | // lambda end up as children to `node`. 24 | content() 25 | // restore current 26 | current = parent 27 | } 28 | } 29 | 30 | class CachingComposerImpl { 31 | private var cache = mutableListOf() 32 | private var index = 0 33 | private val inserting get() = index == cache.size 34 | private fun get(): Any? = cache[index++] 35 | private fun set(value: Any?) { 36 | if (inserting) { index++; cache.add(value); } 37 | else cache[index++] = value 38 | } 39 | 40 | private fun changed(value: T): Boolean { 41 | // if we are inserting, we have nothing to compare against, 42 | // so just store it and return 43 | return if (inserting) { 44 | set(value) 45 | false 46 | } else { 47 | // get current item, increment index. always store new 48 | // value, but return true only if they don't compare 49 | val index = index++ 50 | val item = cache[index] 51 | cache[index] = value 52 | item != value 53 | } 54 | } 55 | 56 | private fun cache(update: Boolean, factory: () -> T): T { 57 | // if we are asked to update the value, or if it is the first time 58 | // the cache is consulted, we need to execute the factory, and save 59 | // the result 60 | return if (inserting || update) factory().also { set(it) } 61 | // otherwise, just return the value in the cache 62 | else get() as T 63 | } 64 | 65 | // override fun Composer.memo(vararg inputs: Any?, factory: () -> T): T { 66 | // var valid = true 67 | // // we need to make sure we check every input, every time. no short-circuiting. 68 | // for (input in inputs) { 69 | // // it is not valid if any of the inputs have changed from last time 70 | // valid = !changed(input) && valid 71 | // } 72 | // return cache(!valid) { factory() } 73 | // } 74 | } 75 | 76 | abstract class Node { 77 | val children = mutableListOf() 78 | override fun toString(): String { 79 | return this::class.simpleName!! 80 | } 81 | } 82 | 83 | enum class Orientation { Vertical, Horizontal } 84 | 85 | class Stack(var orientation: Orientation) : Node() { 86 | override fun toString(): String { 87 | return "${this::class.simpleName}($orientation)" 88 | } 89 | } 90 | 91 | class Text(var text: String) : Node() { 92 | override fun toString(): String { 93 | return "Text($text)" 94 | } 95 | } 96 | 97 | fun renderNodeToScreen(node: Node, indent: Int = 0) { /* ... */ 98 | repeat(indent) { print(" ") } 99 | println(node) 100 | node.children.forEach { 101 | renderNodeToScreen(it, indent + 4) 102 | } 103 | } 104 | 105 | class TodoItem(val title: String) 106 | 107 | fun TodoApp(items: List): Node { 108 | return Stack(Orientation.Vertical).apply { 109 | for (item in items) { 110 | children.add( 111 | Stack(Orientation.Horizontal).apply { 112 | children.add(Text(item.title)) 113 | } 114 | ) 115 | } 116 | } 117 | } 118 | 119 | fun Composer.TodoApp(items: List) { 120 | emit(Stack(Orientation.Vertical)) { 121 | for (item in items) { 122 | emit(Stack(Orientation.Horizontal)) { 123 | emit(Text(item.title)) 124 | } 125 | } 126 | } 127 | } 128 | 129 | fun compose(content: Composer.() -> Unit): Node { 130 | return Stack(Orientation.Vertical).also { 131 | ComposerImpl(it).content() 132 | } 133 | } 134 | 135 | 136 | fun main() { 137 | val items = listOf(TodoItem("Homework"), TodoItem("Reading")) 138 | 139 | renderNodeToScreen(compose { TodoApp(items) }) 140 | } -------------------------------------------------------------------------------- /src/main/kotlin/concurrency/bank/optimistic/BankService.kt: -------------------------------------------------------------------------------- 1 | package concurrency.bank.optimistic 2 | 3 | import java.util.concurrent.Executor 4 | import java.util.concurrent.atomic.AtomicInteger 5 | 6 | 7 | /** 8 | * Reference JCIP p.207 9 | */ 10 | 11 | interface HasId { 12 | val id: Int 13 | } 14 | 15 | data class Transaction( 16 | override val id: Int, 17 | val from: Account, 18 | val to: Account, 19 | val amount: Int, 20 | val status: Status, 21 | ) : HasId { 22 | enum class Status(val value: Int) { 23 | New(0), 24 | DebitSuccess(1), 25 | TxSuccess(3) 26 | } 27 | } 28 | 29 | interface Account : HasId { 30 | val balance: Int 31 | fun compareAndSet(old: Int, new: Int): Boolean 32 | } 33 | 34 | open class AccountImpl( 35 | override val id: Int 36 | ) : Comparable, HasId, Account { 37 | private val _balance = AtomicInteger(0) 38 | 39 | override fun compareAndSet(old: Int, new: Int): Boolean { 40 | beforeService() 41 | return _balance.compareAndSet(old, new) 42 | } 43 | 44 | protected open fun beforeService() { 45 | // Implement by children. 46 | } 47 | 48 | fun credit(amount: Int) { 49 | _balance.getAndUpdate { it + amount } 50 | } 51 | 52 | override val balance get() = _balance.get() 53 | 54 | override fun compareTo(other: Account): Int = id - other.id 55 | 56 | override fun toString(): String { 57 | return "Account(id=${id}, balance=${_balance.get()})" 58 | } 59 | } 60 | 61 | class RemoteAccount(id: Int) : AccountImpl(id) { 62 | @Volatile 63 | private var isDisconnected = false 64 | 65 | fun disconnect() { 66 | isDisconnected = true 67 | } 68 | 69 | fun reconnect() { 70 | isDisconnected = false 71 | } 72 | 73 | override fun beforeService() { 74 | if (isDisconnected) { 75 | error("whoops, account $id is down") 76 | } 77 | } 78 | } 79 | 80 | open class AbstractRepository : Iterable { 81 | private var curId = 0 82 | private val data = mutableMapOf() 83 | 84 | @Synchronized 85 | fun findById(id: Int): T? { 86 | return data[id] 87 | } 88 | 89 | @Synchronized 90 | fun nextId(): Int = curId++ 91 | 92 | @Synchronized 93 | fun save(item: T) { 94 | data[item.id] = item 95 | } 96 | 97 | override fun iterator(): Iterator = data.values.iterator() 98 | } 99 | 100 | class TransactionRepository : AbstractRepository() 101 | 102 | class BankService( 103 | private val transactions: TransactionRepository, 104 | private val executor: Executor 105 | ) { 106 | abstract class TransferException(val tx: Transaction, msg: String?) : RuntimeException(msg) 107 | class InsufficientFund(tx: Transaction) : TransferException(tx, null) 108 | class RemoteException(tx: Transaction, msg: String?) : TransferException(tx, msg) 109 | 110 | private val retryTimes = 5 111 | 112 | fun transfer(from: Account, to: Account, amount: Int): Result { 113 | val tx = Transaction(transactions.nextId(), from, to, amount, Transaction.Status.New) 114 | transactions.save(tx) 115 | return runCatching { 116 | transfer(tx) 117 | }.onFailure { 118 | if (it !is InsufficientFund) { 119 | executor.execute { retry(tx) } 120 | } 121 | if (it !is TransferException) { 122 | return Result.failure(RemoteException(tx, it.message)) 123 | } 124 | } 125 | } 126 | 127 | private fun transfer(tx: Transaction): Transaction { 128 | val from = tx.from 129 | val to = tx.to 130 | val amount = tx.amount 131 | when (tx.status) { 132 | Transaction.Status.New -> { 133 | val nTx = tx.copy(status = Transaction.Status.DebitSuccess).also { 134 | transactions.save(it) 135 | } 136 | for (i in 1..retryTimes + 1) { 137 | if (i == retryTimes + 1) throw RemoteException(tx, "debit failed") 138 | val fromBalance = from.balance 139 | val nBalance = fromBalance - amount 140 | if (nBalance < 0) throw InsufficientFund(tx) 141 | if (from.compareAndSet(fromBalance, fromBalance - amount)) break 142 | } 143 | return transfer(nTx) 144 | } 145 | Transaction.Status.DebitSuccess -> { 146 | val nTx = tx.copy(status = Transaction.Status.TxSuccess).also { 147 | transactions.save(it) 148 | } 149 | for (i in 1..retryTimes + 1) { 150 | if (i == retryTimes + 1) throw RemoteException(tx, "credit failed") 151 | val toBalance = to.balance 152 | if (to.compareAndSet(toBalance, toBalance + amount)) break 153 | } 154 | return transfer(nTx) 155 | } 156 | Transaction.Status.TxSuccess -> return tx 157 | } 158 | } 159 | 160 | private fun retry(tx: Transaction) { 161 | var t = tx 162 | while (t.status != Transaction.Status.TxSuccess) { 163 | t = try { 164 | transfer(tx) 165 | } catch (e: InsufficientFund) { 166 | return 167 | } catch (e: TransferException) { 168 | e.tx 169 | } catch (e: Throwable) { 170 | t 171 | } 172 | Thread.sleep(100) 173 | } 174 | } 175 | } 176 | 177 | -------------------------------------------------------------------------------- /src/main/kotlin/TransferMoneyDemo.kt: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ConcurrentHashMap 2 | import java.util.concurrent.CountDownLatch 3 | import java.util.concurrent.ThreadLocalRandom 4 | import java.util.concurrent.atomic.AtomicInteger 5 | import kotlin.concurrent.thread 6 | 7 | /** 8 | * Reference JCIP p.207 9 | */ 10 | 11 | interface HasId { 12 | val id: Int 13 | } 14 | 15 | data class Transaction( 16 | override val id: Int, 17 | val fromId: Int, 18 | val toId: Int, 19 | val amount: Int, 20 | val status: Status, 21 | ) : HasId { 22 | enum class Status(val value: Int) { 23 | New(0), 24 | DebitSuccess(1), 25 | TxSuccess(3) 26 | } 27 | } 28 | 29 | class Account( 30 | override val id: Int 31 | ) : Comparable, HasId { 32 | private val balance = AtomicInteger(0) 33 | 34 | fun compareAndSet(old: Int, new: Int): Boolean { 35 | return balance.compareAndSet(old, new) 36 | } 37 | 38 | fun credit(amount: Int) { 39 | balance.getAndUpdate { it + amount } 40 | } 41 | 42 | fun getBalance(): Int { 43 | return balance.get() 44 | } 45 | 46 | override fun compareTo(other: Account): Int = id - other.id 47 | 48 | override fun toString(): String { 49 | return "Account(id=${id}, balance=${balance.get()})" 50 | } 51 | } 52 | 53 | open class AbstractRepository { 54 | private var curId = 0 55 | private val data = mutableMapOf() 56 | 57 | @Synchronized 58 | fun findById(id: Int): T? { 59 | return data[id] 60 | } 61 | 62 | @Synchronized 63 | fun nextId(): Int = curId++ 64 | 65 | @Synchronized 66 | fun save(item: T) { 67 | data[item.id] = item 68 | } 69 | } 70 | 71 | class TransactionRepository : AbstractRepository() 72 | class AccountRepository : AbstractRepository() { 73 | fun compareAndUpdate(id: Int, expect: Int, update: Int): Boolean { 74 | val account = findById(id)!! 75 | return account.compareAndSet(expect, update) 76 | } 77 | } 78 | 79 | interface AccountRepositoryRouter { 80 | fun routeById(): AccountRepository 81 | } 82 | 83 | 84 | class BankService( 85 | private val accounts: AccountRepository, 86 | private val transactions: TransactionRepository 87 | ) { 88 | 89 | data class TransferRequest( 90 | val fromId: Int, 91 | val toId: Int, 92 | val amount: Int 93 | ) 94 | 95 | class TransferException(msg: String) : RuntimeException(msg) 96 | class InsufficientFund() : RuntimeException() 97 | 98 | private val retryTimes = 5 99 | 100 | fun transfer(request: TransferRequest): Result { 101 | val from = accounts.findById(request.fromId) 102 | ?: return Result.failure(TransferException("from account id ${request.fromId} not found")) 103 | val to = accounts.findById(request.toId) 104 | ?: return Result.failure(TransferException("to account id ${request.toId} not found")) 105 | 106 | val tx = Transaction( 107 | transactions.nextId(), 108 | from.id, to.id, 109 | request.amount, Transaction.Status.New 110 | ) 111 | transactions.save(tx) 112 | 113 | 114 | for (i in 1..retryTimes + 1) { 115 | if (i > 1) println("debit retry $i") 116 | if (i == retryTimes + 1) return Result.failure(TransferException("debit failed")) 117 | val fromBalance = from.getBalance() 118 | val nBalance = fromBalance - request.amount 119 | if (nBalance < 0) return Result.failure(InsufficientFund()) 120 | if (from.compareAndSet(fromBalance, fromBalance - request.amount)) break 121 | } 122 | 123 | transactions.save(tx.copy(status = Transaction.Status.DebitSuccess)) 124 | 125 | 126 | for (i in 1..retryTimes + 1) { 127 | if (i > 1) println("credit retry $i") 128 | if (i == retryTimes + 1) return Result.failure(TransferException("credit failed")) 129 | val toBalance = to.getBalance() 130 | if (to.compareAndSet(toBalance, toBalance + request.amount)) break 131 | } 132 | 133 | transactions.save(tx.copy(status = Transaction.Status.TxSuccess)) 134 | 135 | return Result.success(Unit) 136 | } 137 | } 138 | 139 | 140 | fun main() { 141 | val transactions = TransactionRepository() 142 | val accounts = AccountRepository() 143 | val bank = BankService(accounts, transactions) 144 | 145 | val account1 = Account(1).apply { credit(100) }.also { accounts.save(it) } 146 | val account2 = Account(2).apply { credit(100) }.also { accounts.save(it) } 147 | 148 | // after tx1 - 1: 50, 2: 150 149 | // after tx2 - 1: 60, 2: 140 150 | val t1 = thread { 151 | bank.transfer(BankService.TransferRequest(1, 2, 50)).getOrThrow() 152 | } 153 | val t2 = thread { 154 | bank.transfer(BankService.TransferRequest(2, 1, 10)).getOrThrow() 155 | } 156 | 157 | t1.join() 158 | t2.join() 159 | println(account1) 160 | println(account2) 161 | 162 | val times = 1000 163 | val startGate = CountDownLatch(1) 164 | val endGate = CountDownLatch(1000) 165 | val failedStats = ConcurrentHashMap() 166 | 167 | repeat(times) { 168 | thread { 169 | startGate.await() 170 | val r = ThreadLocalRandom.current() 171 | val from = if (r.nextBoolean()) account1 else account2 172 | val to = if (from == account1) account2 else account1 173 | bank.transfer(BankService.TransferRequest(from.id, to.id, r.nextInt(10, 50))).getOrElse { 174 | failedStats.compute(it.toString()) { _, v: Int? -> (v ?: 0) + 1 } 175 | } 176 | endGate.countDown() 177 | } 178 | } 179 | 180 | startGate.countDown() 181 | endGate.await() 182 | 183 | println(account1) 184 | println(account2) 185 | assert(account1.getBalance() + account2.getBalance() == 200) 186 | println(failedStats) 187 | } 188 | --------------------------------------------------------------------------------