Java 到 Kotlin:集合与字符串迁移¶
集合和字符串是 Java 迁移 Kotlin 时最常改写的代码。Kotlin 标准库提供了更直接的 API,但迁移时不要只追求“短”,还要保持语义清楚、空安全明确、性能可接受。
集合:只读接口与可变接口¶
Java 的 List 同时包含读取和修改方法:
List<String> names = new ArrayList<>();
names.add("Ada");
Kotlin 区分只读接口和可变接口:
val names: List<String> = listOf("Ada")
val mutableNames: MutableList<String> = mutableListOf("Ada")
只读 List 不能调用 add(),但它不保证底层对象绝对不可变。对外暴露 API 时,List<T> 表达“调用方不应修改”,MutableList<T> 表达“调用方可以修改”。
创建集合¶
Java:
List<String> names = List.of("Ada", "Grace");
Set<Integer> numbers = Set.of(1, 2, 3);
Map<String, Integer> ages = Map.of("Ada", 36);
Kotlin:
val names = listOf("Ada", "Grace")
val numbers = setOf(1, 2, 3)
val ages = mapOf("Ada" to 36)
可变版本:
val names = mutableListOf("Ada")
val numbers = mutableSetOf(1, 2)
val ages = mutableMapOf("Ada" to 36)
遍历集合¶
Java:
for (String name : names) {
System.out.println(name);
}
Kotlin:
for (name in names) {
println(name)
}
需要索引:
for ((index, name) in names.withIndex()) {
println("$index: $name")
}
过滤、映射与排序¶
Java Stream:
List<String> result = names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.sorted()
.toList();
Kotlin:
val result = names
.filter { it.length > 3 }
.map { it.uppercase() }
.sorted()
Kotlin 集合操作默认会产生中间集合。大量数据或长链条处理时,可以使用 asSequence() 延迟计算:
val result = names.asSequence()
.filter { it.length > 3 }
.map { it.uppercase() }
.sorted()
.toList()
查找元素¶
Java:
Optional<String> first = names.stream()
.filter(name -> name.startsWith("A"))
.findFirst();
Kotlin 常用可空值表达“可能没有”:
val first: String? = names.firstOrNull { it.startsWith("A") }
如果你确信一定存在:
val first = names.first { it.startsWith("A") }
找不到时会抛 NoSuchElementException。业务上可能不存在时,优先使用 firstOrNull()。
分组与关联¶
val usersByRole: Map<String, List<User>> =
users.groupBy { it.role }
val userById: Map<Long, User> =
users.associateBy { it.id }
这类操作在 Java Stream 中也能实现,但 Kotlin 标准库命名更贴近集合领域。
Map 访问¶
val age: Int? = ages["Ada"]
map[key] 返回可空值,因为 key 可能不存在。需要默认值:
val age = ages["Ada"] ?: 0
可变 Map 更新:
val ages = mutableMapOf("Ada" to 36)
ages["Grace"] = 85
字符串模板¶
Java:
String message = "Hello, " + name + "!";
Kotlin:
val message = "Hello, $name!"
表达式:
val message = "Name length: ${name.length}"
简单变量用 $name,复杂表达式用 ${...}。
多行字符串¶
val sql = """
SELECT id, name
FROM users
WHERE active = true
""".trimIndent()
这比 Java 早期字符串拼接更清晰。Java 15+ 也有 text block,但 Kotlin 多行字符串和字符串模板结合得更自然。
字符串判空¶
Kotlin 标准库提供常用函数:
text.isEmpty()
text.isBlank()
text.isNullOrEmpty()
text.isNullOrBlank()
区别:
- empty:长度为 0。
- blank:全是空白字符也算空白。
- nullOrEmpty/nullOrBlank:接收可空字符串。
示例:
fun normalize(input: String?): String =
input.takeUnless { it.isNullOrBlank() } ?: "默认值"
字符串转换数字¶
Java:
int value = Integer.parseInt(input);
Kotlin:
val value = input.toInt()
可能失败时:
val value: Int? = input.toIntOrNull()
toIntOrNull() 避免用异常表达正常的解析失败,特别适合用户输入、配置、CSV、命令行参数等场景。
正则与替换¶
val digits = Regex("\\d+")
val found = digits.findAll("a1 b22 c333")
.map { it.value }
.toList()
简单替换:
val normalized = text.replace("Java", "Kotlin")
复杂匹配时用 Regex,简单字面替换时用 replace 即可。
迁移建议¶
- Java Stream 迁移到 Kotlin 集合链时,先保持语义一致,再考虑
Sequence性能。 - 可能不存在的集合结果用
firstOrNull()、singleOrNull()、getOrNull()。 - 不要把 Java Optional 机械翻译成 Kotlin
Optional;Kotlin 通常用可空类型。 - 字符串拼接优先用模板。
- 用户输入转换数字时优先用
toIntOrNull()等安全函数。 - 对外 API 区分
List和MutableList,表达调用方是否允许修改。
参考¶
- 官方集合迁移指南:https://kotlinlang.org/docs/java-to-kotlin-collections-guide.html
- 官方字符串迁移指南:https://kotlinlang.org/docs/java-to-kotlin-idioms-strings.html