官方好用的buildXXX()扩展API知多少,请看这篇!

306 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

本篇文章主要是介绍buildString{}buildList{}buildSet{}buildMap{}系列API的基本使用,通过了解希望可以给你日常开发带来遍历。

1.buildString{}动态构建String

平常我们构造字符串为了避免通过+的形式频繁创建中间String对象,一般会使用StringBuild来动态字符串:

fun test1() {
    val build = StringBuilder()
    build.append("a")
    
    val tmp = "10".repeat(10)
    build.append(tmp)
}

kotlin.text包下提供了buildString{}扩展,来更优雅的动态创建String

public inline fun buildString(builderAction: StringBuilder.() -> Unit): String {
    contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
    return StringBuilder().apply(builderAction).toString()
}

定义了一个接收者为StringBuild的参数,所以我们就能在lambda中调用StringBuild的public的方法实现插入。最终构建StringBuilder对象并转成String返回。

使用如下:

fun test1() {
    val content: String = buildString {
        append("a")
        append("b".repeat(10))
    }
}

其中官方库还提供了一个重载方法:buildString(capacity: Int, builderAction: StringBuilder.() -> Unit){}可以指定StringBuild初始容量。

下面介绍下官方为StringBuild提供的有用的几个扩展方法“

1.1 append(vararg value: String?)插入多值

fun test1() {
    val content: String = buildString {
        //一次性插入多值
        append("a", "b".repeat(10), "b".repeat(10))
    }
}

1.2 appendLine()插入换行

public inline fun StringBuilder.appendLine(): StringBuilder = append('\n')

上面为源码实现。

1.3 appendLine(value: CharSequence?)插入值后追加换行

public inline fun StringBuilder.appendLine(value: CharSequence?): StringBuilder = append(value).appendLine()

源码中,就是在插入换行之前先插入传递的参数。

1.4 set(index: Int, value: Char)重载[]插入指定位置字符

public expect operator fun StringBuilder.set(index: Int, value: Char)

使用如下:

val content: String = buildString {
    //小心数组越界异常
    this[5] = 'b'
}

2. buildList<T>{}构造List

public inline fun <E> buildList(@BuilderInference builderAction: MutableList<E>.() -> Unit): List<E> {
    contract { callsInPlace(builderAction, InvocationKind.EXACTLY_ONCE) }
    return buildListInternal(builderAction)
}

这个方法需要在kotlin1.6及之上的版本稳定使用,使用如下:

val list = buildList<String> {
    add("haha")
    add("ddd")
}

同样官方库也提供了一个重载方法buildList(capacity: Int, @BuilderInference builderAction: MutableList<E>.() -> Unit)可以指定集合初始化的容量。

下面介绍几个有用的集合扩展方法/属性:

2.1 Collection<*>.indices获取集合索引集合

public val Collection<*>.indices: IntRange
    get() = 0..size - 1

这个扩展属性在遍历集合的挺有用:

fun test2() {
    val list = mutableListOf<String>()
    for (i in list.indices) {
        println(list[i])
    }
}

2.2 isNullOrEmpty()contract形式集合判空

public inline fun <T> Collection<T>?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || this.isEmpty()
}

这个扩展关键的是contract关键子,当isNullOrEmpty()返回false的时候让我们在if表达式中直接通过list.xxx()操作集合成员,而不需要带list?.xxx()这样操作。

最近看了掘金一篇介绍contract文章,推荐给大家:Kotlin 标准库随处可见的 contract 到底是什么?

2.3 orEmpty()接受者为空默认创建空集合

public inline fun <T> List<T>?.orEmpty(): List<T> = this ?: emptyList()

源码很简单,大家平常也会经常使用?:判断集合为空时,返回一个默认空集合,这里都给大家封装好了,安心食用。

2.4 shuffled(random: Random)打乱集合元素顺序

public fun <T> Iterable<T>.shuffled(random: Random): List<T> = toMutableList().apply { shuffle(random) }

public fun <T> MutableList<T>.shuffle(random: Random): Unit {
    for (i in lastIndex downTo 1) {
        val j = random.nextInt(i + 1)
        this[j] = this.set(i, this[j])
    }
}

核心实现就是这个shuffle()方法,通过random.nextInt随机下标,和索引下标对应的元素互相交换,最终实现洗牌操作

3. buildSet{}buildMap{}

set构建如下:

val set = buildSet<String> {
    add("20")
}

map构建如下:

val map = buildMap<String, String> {
    this["a"] = "b"
}

一个构造set集合,一个构造哈希表,使用和上面的buildList{}差不多,具体的使用就不再多做描述了,请注意也要在kotlin1.6及以上版本才能稳定使用。

历史文章

之前也写过官方库给我提供的遍历的扩展API,大家感兴趣的可以看下:

你需要了解的官方core-ktx库能对开发带来哪些便利?

官方core-ktx库能对富文本Span开发带来哪些便利?

官方core-ktx库能对SparseArray系列、Pair开发带来哪些便利?

超好用的官方core-ktx库,了解一下~

超好用的官方core-ktx库,了解一下(终)~