5、字符串和数组:从基础操作到实战技巧

字符串和数组,可以说是 Kotlin 里最常用的两种数据结构了。我做了这么多年开发,几乎每个项目都离不开它们。今天咱们就好好聊聊这两个家伙,从最基础的输入输出,到那些能让你写代码更爽的骚操作。

5.1 字符串输入与输出

先说说字符串的输入。在 Kotlin 里,从控制台读字符串其实很简单:

println("请输入你的名字:")
val name = readLine() ?: "默认值"
println("你好,$name")

这里有个坑,readLine() 返回的是可空类型 String?。我刚开始用 Kotlin 时就被这个坑过——用户直接按回车,结果程序崩了。所以记得用 ?: 给个默认值,或者用 !! 强制非空(但我不建议这么干)。

输出方面,Kotlin 的字符串模板是真的好用:

val age = 28
println("我今年 ${age} 岁,明年就 ${age + 1} 岁了")

你看,直接在字符串里写表达式,比 Java 那种拼接舒服多了。

5.2 下标和切片

字符串本质上就是字符的序列,所以可以用下标访问:

val str = "Kotlin"
println(str[0])  // K
println(str[3])  // l

不过要注意,下标从 0 开始,别越界了。我见过太多新手写 str[str.length] 然后一脸懵逼。

切片操作就更灵活了:

val str = "Hello Kotlin"
println(str.substring(0, 5))   // Hello
println(str.slice(6..11))      // Kotlin
println(str.slice(0 until 5))  // Hello

我个人习惯用 slice 配合区间,写起来更直观。你想想看,6..11 一眼就知道取哪几个字符。

5.3 字符串常见函数

Kotlin 给字符串加了不少好用的函数,我挑几个常用的说说:

函数 作用 示例
trim() 去除首尾空格 " abc ".trim()"abc"
split() 按分隔符拆分 "a,b,c".split(",")[a, b, c]
replace() 替换字符/字符串 "hello".replace("l", "x")"hexxo"
contains() 是否包含子串 "Kotlin".contains("ot")true
startsWith() 是否以某前缀开头 "Kotlin".startsWith("Ko")true
小技巧: 处理用户输入时,我习惯先 trim() 再判断,不然用户多打个空格就匹配不上了。

5.4 数组(Array)

数组在 Kotlin 里有两种创建方式:

// 方式一:用 arrayOf
val arr1 = arrayOf(1, 2, 3, 4, 5)

// 方式二:指定类型和大小
val arr2 = Array(5) { i -> i * 2 }  // [0, 2, 4, 6, 8]

// 基本类型数组(性能更好)
val intArr = intArrayOf(1, 2, 3)
val charArr = charArrayOf('a', 'b', 'c')

这里有个细节:Array<Int>IntArray 不是一回事。前者是装箱的,后者是原始类型。我在做性能敏感的计算时,会用 IntArray 来避免装箱开销。

5.5 数组的循环遍历

遍历数组的方式有很多,我按常用程度排个序:

val arr = arrayOf("苹果", "香蕉", "橘子")

// 最常用:for-in
for (item in arr) {
    println(item)
}

// 需要下标时
for (i in arr.indices) {
    println("$i: ${arr[i]}")
}

// 同时获取下标和值
for ((index, value) in arr.withIndex()) {
    println("$index -> $value")
}

// 用 forEach(函数式风格)
arr.forEach { println(it) }

我个人最喜欢 withIndex(),既拿到了下标又拿到了值,不用手动维护索引变量。

5.6 数组的增删改查

数组的长度是固定的,所以「增删」其实是通过创建新数组来实现的。不过 Kotlin 提供了一些好用的扩展函数:

val arr = arrayOf(1, 2, 3)

// 查
println(arr.contains(2))   // true
println(arr.indexOf(3))    // 2

// 改
arr[1] = 99  // 直接修改

// 增(返回新数组)
val newArr = arr.plus(4)   // [1, 99, 3, 4]

// 删(返回新数组)
val filtered = arr.filter { it != 99 }  // [1, 3]
注意: plus()filter() 返回的是新数组,原数组不变。如果你频繁增删,建议用 ArrayListMutableList

5.7 数组的嵌套

二维数组其实就是数组的数组。我做个简单的矩阵示例:

// 创建一个 3x3 的矩阵
val matrix = Array(3) { Array(3) { 0 } }

// 赋值
matrix[0][0] = 1
matrix[1][1] = 2
matrix[2][2] = 3

// 遍历
for (row in matrix) {
    for (value in row) {
        print("$value ")
    }
    println()
}

嵌套数组在游戏开发、图像处理里很常见。我记得有次做扫雷游戏,就是用二维数组来存储地雷位置的。

5.8 数组的切片

数组也支持切片操作,和字符串类似:

val arr = arrayOf(10, 20, 30, 40, 50)

// 取前三个
val slice1 = arr.sliceArray(0..2)  // [10, 20, 30]

// 取指定下标
val slice2 = arr.sliceArray(listOf(0, 2, 4))  // [10, 30, 50]

// 用区间
val slice3 = arr.sliceArray(1 until 4)  // [20, 30, 40]

切片返回的是新数组,不会影响原数组。这在处理大数据集时特别有用——你可以只取一部分数据做处理,不用复制整个数组。

核心总结: 字符串和数组都是序列,所以很多操作是相通的。下标从0开始、切片用区间、遍历用for-in,这些套路记住了,写起来就顺手了。
字符串与数组知识体系 字符串 String 输入输出 下标与切片 常见函数(trim/split等) 数组 Array 创建与遍历 增删改查 嵌套与切片 共同点:下标从0开始 · 支持切片 · 可用for-in遍历

嗯,字符串和数组的内容就这些了。其实说白了,它们都是序列,很多操作套路是一样的。你只要把下标、切片、遍历这三个基本功练熟,后面学列表、集合什么的就轻松多了。

公众号:蓝海资料掘金营,微信deep3321