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() 返回的是新数组,原数组不变。如果你频繁增删,建议用 ArrayList 或 MutableList。
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]
切片返回的是新数组,不会影响原数组。这在处理大数据集时特别有用——你可以只取一部分数据做处理,不用复制整个数组。
嗯,字符串和数组的内容就这些了。其实说白了,它们都是序列,很多操作套路是一样的。你只要把下标、切片、遍历这三个基本功练熟,后面学列表、集合什么的就轻松多了。