Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlin Standard Library Gems

Kotlin Standard Library Gems

Kotlin's standard library has a lot of useful functionality to offer. In this presentation, we will unearth some hidden gems that will come in handy the next time you write Kotlin code. Because if you know how to wield it, the Kotlin standard library is a powerful tool that can help you be more productive in solving your problems and be more expressive in your code.

Anton Arhipov

June 20, 2024
Tweet

More Decks by Anton Arhipov

Other Decks in Programming

Transcript

  1. listOf("#1", "#2", "#3") List(3) { "#${it + 1}" } val

    ml = MutableList(5) { "?" } ml[0] = "a" ml[2] = "c" [#1, #2, #3] [#1, #2, #3] [a, ?, c, ?, ?]
  2. listOf("#1", "#2", "#3") List(3) { "#${it + 1}" } val

    ml = MutableList(5) { "?" } ml[0] = "a" ml[2] = "c" [#1, #2, #3] [#1, #2, #3] [a, ?, c, ?, ?] ml.joinToString("") a?c ? ?
  3. listOf("#1", "#2", "#3") List(3) { "#${it + 1}" } val

    ml = MutableList(5) { "?" } ml[0] = "a" ml[2] = "c" ml.joinToString( separator = "-", prefix = " << ", postfix = " >> ", limit = 3, truncated = "etc." ){ "#$it" } [#1, #2, #3] [#1, #2, #3] [a, ?, c, ?, ?] < < #a- #? -#c-etc. >> ml.joinToString("") a?c ? ?
  4. val students = listOf("Ann", "Joe", "Max", "Anton", "John", "Bo", "Ken").shuffled()

    println(students.chunked(2)) println(students.windowed(3))
  5. val students = listOf("Ann", "Joe", "Max", "Anton", "John", "Bo", "Ken").shuffled()

    println(students.chunked(2)) println(students.windowed(3)) [[John, Ann], [Joe, Bo], [Max, Anton], [Ken]] [[John, Ann, Joe], [Ann, Joe, Bo], [Joe, Bo, Max], [Bo, Max, Anton], [Max, Anton, Ken]]
  6. val students = listOf("Ann", "Joe", "Max", "Anton", "John", "Bo", "Ken").shuffled()

    println(students.chunked(2)) println(students.windowed(3)) [[John, Ann], [Joe, Bo], [Max, Anton], [Ken]] [[John, Ann, Joe], [Ann, Joe, Bo], [Joe, Bo, Max], [Bo, Max, Anton], [Max, Anton, Ken]] listOf(11, 10, 12, 9, 10, 15).windowed(3) { it.average() } [11.0, 12.0, 12.0, 13.0]
  7. val cities = listOf("Amsterdam", "Paris", "New York") val countries =

    listOf("NL", "FR", "US") val citiesToCountries = cities zip countries [(Amsterdam, NL), (Paris, FR), (New York, US)]
  8. val cities = listOf("Amsterdam", "Paris", "New York") val countries =

    listOf("NL", "FR", "US") val citiesToCountries = cities zip countries val (first, second) = citiesToCountries.unzip() [(Amsterdam, NL), (Paris, FR), (New York, US)]
  9. val cities = listOf("Amsterdam", "Paris", "New York") val countries =

    listOf("NL", "FR", "US") val citiesToCountries = cities zip countries val (first, second) = citiesToCountries.unzip() [(Amsterdam, NL), (Paris, FR), (New York, US)] listOf(1, 2, 3, 4, 5).zipWithNext { a, b -> a + b } [3, 5, 7, 9] listOf(1, 2, 3, 4, 5).zipWithNext() [(1, 2), (2, 3), (3, 4), (4, 4)]
  10. abcd bcde cdef defg xya xyb xyc Count the total

    of characters that are present on each line in every group of strings
  11. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings
  12. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: Count the total of characters that are present on each line in every group of strings
  13. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: {d} {xy} Count the total of characters that are present on each line in every group of strings
  14. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: {d} {xy} count = 1 count = 2 Count the total of characters that are present on each line in every group of strings
  15. abcd bcde cdef defg xya xyb xyc Group 1: Group

    2: {d} {xy} count = 1 count = 2 Total = 3 Count the total of characters that are present on each line in every group of strings
  16. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n")
  17. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n")
  18. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  19. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  20. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  21. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() }
  22. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() } Transforming data
  23. val input = """ abcd bcde cdef defg xya xyb

    xyc """.trimIndent() val groups: List<String> = input.split("\n\n") var total = 0 for (group in groups) { val listOfSets: List<Set<Char >> = group.split("\n").map(String :: toSet) var result = listOfSets.first() for (set in listOfSets) { result = result intersect set } total += result.count() } Transforming data Calculating the result
  24. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  25. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  26. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  27. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  28. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } Transforming data
  29. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  30. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  31. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  32. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  33. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result
  34. val groups: List<String> = input.split("\n\n") // List<List<Set<Char >>> val step1

    = groups.map { it.split("\n").map(String :: toSet) } val step2 = step1.sumOf { it.reduce { a, b - > a intersect b }.count() } Transforming data Calculating the result groups.map { group -> group.split(nl).map(String :: toSet) }.sumOf { answerSets -> answerSets.reduce { a, b -> a intersect b }.count() }
  35. listOf(1, 2, 3, 4, 5).fold(10) { a, b - >

    a + b } listOf(1, 2, 3, 4, 5).reduce { a, b - > a + b } listOf("#1", "#2", "#3").fold("#0") { a, b - > a + b } listOf("#1", "#2", "#3").reduce { a, b -> a + b } / / 25 / / 15 / / #0#1#2#3 / / #1#2#3
  36. listOf(1, 2, 3, 4, 5).fold(10) { a, b - >

    a + b } / / 25 How does this work? How do I trace the logic of this operation? What are the results intermediate steps?
  37. listOf(1, 2, 3, 4, 5).fold(10) { a, b - >

    a + b } / / 25 runningFold runningReduce
  38. listOf(1, 2, 3, 4, 5).runningFold(10) { a, b -> a

    + b } / / [10, 11, 13, 16, 20, 25] listOf(1, 2, 3, 4, 5).runningReduce { a, b - > a + b } / / [1, 3, 6, 10, 15] listOf("#1", "#2", "#3").runningReduce { a, b -> a + b } / / [#1, #1#2, #1#2#3]
  39. val result: = x.foo { } Return type Original object

    (x) Result of lambda Access to X it this
  40. val dataSource = BasicDataSource() dataSource.driverClassName = "com.mysql.jdbc.Driver" dataSource.url = "jdbc:mysql://domain:3309/db"

    dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4
  41. val dataSource = BasicDataSource() dataSource.driverClassName = "com.mysql.jdbc.Driver" dataSource.url = "jdbc:mysql://domain:3309/db"

    dataSource.username = "username" dataSource.password = "password" dataSource.maxTotal = 40 dataSource.maxIdle = 40 dataSource.minIdle = 4 val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 }
  42. val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url =

    "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 } public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this }
  43. val dataSource = BasicDataSource().apply { driverClassName = "com.mysql.jdbc.Driver" url =

    "jdbc:mysql://domain:3309/db" username = "username" password = "password" maxTotal = 40 maxIdle = 40 minIdle = 4 } public inline fun <T> T.apply(block: T.() -> Unit): T { block() return this } Lambda with receiver
  44. ? . let val order = retrieveOrder() if (order !=

    null){ processCustomer(order.customer) }
  45. val order = retrieveOrder() if (order != null){ processCustomer(order.customer) }

    retrieveOrder() ?. let { processCustomer(it.customer) } retrieveOrder() ?. customer ?. let( :: processCustomer) or ? . let
  46. let() as a helper for a complex condition if (some.complex.expression.let

    { it is Type && it.has.some.property }) { . .. }
  47. let() as a helper for a complex condition if (some.complex.expression.let

    { it is Type && it.has.some.property }) { . .. }
  48. let() as a helper for a complex condition if (some.complex.expression.let

    { it is Type && it.has.some.property }) { . .. }
  49. if (retrieveOrder().let { it is Subscription && it.customer.name == "Anton"})

    { . .. } let() as a helper for a complex condition if (some.complex.expression.let { it is Type && it.has.some.property }) { . .. }
  50. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs()

    } fun makeDir(path: String) : File { val file = File(path) file.mkdirs() return file } This is simpler! Don’t overuse the scope functions!
  51. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs()

    } fun makeDir(path: String) : File { val file = File(path) file.mkdirs() return file } fun makeDir(path: String) = File(path).also { it.mkdirs() } OK, this one is actually fi ne :) This is simpler! Don’t overuse the scope functions!
  52. buildString //Java String name = "Joe"; StringBuilder sb = new

    StringBuilder(); for (int i = 0; i < 5; i++) { sb.append("Hello, "); sb.append(name); sb.append("!\n"); } System.out.println(sb); //Kotlin val name = "Joe" val s = buildString { repeat(5) { append("Hello, ") append(name) appendLine("!") } } println(s)
  53. Ktor fun main() { embeddedServer(Netty, port = 8080, host =

    "0.0.0.0") { routing { get("/html-dsl") { call.respondHtml { body { h1 { +"HTML" } ul { for (n in 1 .. 10) { li { +"$n" } } } } } } } }.start(wait = true) }
  54. Ktor fun main() { embeddedServer(Netty, port = 8080, host =

    "0.0.0.0") { routing { get("/html-dsl") { call.respondHtml { body { h1 { +"HTML" } ul { for (n in 1 .. 10) { li { +"$n" } } } } } } } }.start(wait = true) } Ktor’s routing
  55. Ktor fun main() { embeddedServer(Netty, port = 8080, host =

    "0.0.0.0") { routing { get("/html-dsl") { call.respondHtml { body { h1 { +"HTML" } ul { for (n in 1 .. 10) { li { +"$n" } } } } } } } }.start(wait = true) } kotlinx.html Ktor’s routing
  56. final ClientBuilder builder = new ClientBuilder(); builder.setFirstName("Anton"); builder.setLastName("Arhipov"); final TwitterBuilder

    twitterBuilder = new TwitterBuilder(); twitterBuilder.setHandle("@antonarhipov"); builder.setTwitter(twitterBuilder.build()); final CompanyBuilder companyBuilder = new CompanyBuilder(); companyBuilder.setName("JetBrains"); companyBuilder.setCity("Tallinn"); builder.setCompany(companyBuilder.build()); final Client client = builder.build(); System.out.println("Created client is: " + client);
  57. val builder = ClientBuilder() builder.firstName = "Anton" builder.lastName = "Arhipov"

    val twitterBuilder = TwitterBuilder() twitterBuilder.handle = "@antonarhipov" builder.twitter = twitterBuilder.build() val companyBuilder = CompanyBuilder() companyBuilder.name = "JetBrains" companyBuilder.city = "Tallinn" builder.company = companyBuilder.build() val client = builder.build() println("Created client is: $client")
  58. val client = ClientBuilder().apply { firstName = "Anton" lastName =

    "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")
  59. val client = ClientBuilder().apply { firstName = "Anton" lastName =

    "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")
  60. val client = ClientBuilder().apply { firstName = "Anton" lastName =

    "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client")
  61. val client = ClientBuilder().apply { firstName = "Anton" lastName =

    "Arhipov" twitter = TwitterBuilder().apply { handle = "@antonarhipov" }.build() company = CompanyBuilder().apply { name = "JetBrains" city = "Tallinn" }.build() }.build() println("Created client is: $client") fun client(c: ClientBuilder.() -> Unit): Client { val builder = ClientBuilder() c(builder) return builder.build() } fun ClientBuilder.company(block: CompanyBuilder.() -> Unit) { company = CompanyBuilder().apply(block).build() } fun ClientBuilder.twitter(block: TwitterBuilder.() -> Unit) { twitter = TwitterBuilder().apply(block).build() }
  62. fun client(c: ClientBuilder.() -> Unit): Client { val builder =

    ClientBuilder() c(builder) return builder.build() } fun ClientBuilder.company(block: CompanyBuilder.() -> Unit) { company = CompanyBuilder().apply(block).build() } fun ClientBuilder.twitter(block: TwitterBuilder.() -> Unit) { twitter = TwitterBuilder().apply(block).build() } val person = person { firstName = "Anton" lastName = "Arhipov" twitter { handle = "@antonarhipov" } company { name = "JetBrains" city = "Tallinn" } } println("Hello, $person!")
  63. Select objects by type with filterIsInstance fun findAllStrings(objects: List<Any>) =

    objects.filter { it is String } fun findAllStrings(objects: List<Any>) = objects.filterIsInstance<String>()
  64. Select objects by type with filterIsInstance fun findAllStrings(objects: List<Any>): List<Any>

    = objects.filter { it is String } fun findAllStrings(objects: List<Any>): List<String> = objects.filterIsInstance<String>()
  65. compareBy compares by multiple keys class Person( val name: String,

    val age: Int ) fun sortPersons(persons: List<Person>) = persons.sortedWith(Comparator<Person> { person1, person2 -> val rc = person1.name.compareTo(person2.name) if (rc != 0) rc else person1.age - person2.age })
  66. class Person( val name: String, val age: Int ) fun

    sortPersons(persons: List<Person>) = persons.sortedWith(Comparator<Person> { person1, person2 -> val rc = person1.name.compareTo(person2.name) if (rc != 0) rc else person1.age - person2.age }) fun sortPersons(persons: List<Person>) = persons.sortedWith(compareBy(Person :: name, Person :: age)) compareBy compares by multiple keys
  67. groupBy to group elements class Request( val url: String, val

    remoteIP: String, val timestamp: Long ) fun analyzeLog(log: List<Request>) { val map = mutableMapOf<String, MutableList<Request >> () for (request in log) { map.getOrPut(request.url) { mutableListOf() } .add(request) } }
  68. class Request( val url: String, val remoteIP: String, val timestamp:

    Long ) fun analyzeLog(log: List<Request>) { val map = mutableMapOf<String, MutableList<Request >> () for (request in log) { map.getOrPut(request.url) { mutableListOf() } .add(request) } } fun analyzeLog(log: List<Request>) { val map = log.groupBy(Request :: url) } groupBy to group elements
  69. Use coerceIn to ensure numbers in range fun updateProgress(value: Int)

    { val actualValue = when { value < 0 - > 0 value > 100 -> 100 else - > value } } fun updateProgress(value: Int) { val actualValue = value.coerceIn(0, 100) }