1. 概述
在本教程中,我们将学习如何在 Kotlin 中将一个对象列表转换为字符串列表。这种场景在实际开发中非常常见,尤其是在需要从复杂对象中提取某些字段组成简单列表时。
我们将主要介绍 Kotlin 集合的 map()
函数及其变体,以及 for
循环和 flatMap()
的使用方式。
2. 问题描述:将对象列表转换为字符串列表
为了更好地说明问题,我们先定义一个 Employee
数据类,并创建一个 Employee
对象列表。我们的目标是提取这些员工的名字组成一个字符串列表。
2.1 定义 Employee
类
data class Employee (
val empId: Int,
val firstName: String,
val lastName: String,
val monthlySalary: Double
) {
fun name(): String {
return "$firstName $lastName"
}
}
2.2 创建员工列表
private val empList = listOf(
Employee(1, "John", "Doe", Random.nextDouble(1000.0, 5000.0)),
Employee(2, "Jill", "Fowler", Random.nextDouble(1000.0, 5000.0)),
Employee(3, "Jack", "Smith", Random.nextDouble(1000.0, 5000.0)),
Employee(4, "John", "Doe", Random.nextDouble(1000.0, 5000.0))
)
2.3 预期结果
private val expectedEmpNames = listOf("John Doe", "Jill Fowler", "Jack Smith", "John Doe")
接下来我们将使用多种方式来实现 empList
到 expectedEmpNames
的转换。
3. 使用 map()
函数转换列表
map()
是 Kotlin 集合中最常用的转换函数之一。它接收一个 lambda 表达式,用于将每个元素映射为新的值。
3.1 基本使用 map()
val empNameList = empList.map { it.name() }
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
✅ 优点:简洁、语义清晰
❌ 缺点:不处理 null 值(如果元素可能为 null,需使用 mapNotNull()
)
3.2 使用 mapNotNull()
过滤 null 值
当列表中包含 null 时,可以使用 mapNotNull()
自动过滤掉 null 元素:
val empListWithNulls = listOf(*empList.toTypedArray(), null)
val empNameList = empListWithNulls.mapNotNull { it?.name() }
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
⚠️ 注意:使用 it?.name()
是安全调用,防止 null 引发异常。
3.3 使用 mapTo()
转换到已有列表
如果你希望将结果收集到一个已有的可变列表中,可以使用 mapTo()
:
val empNameList = mutableListOf<String>()
empList.mapTo(empNameList) { it.name() }
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
3.4 使用 mapIndexed()
获取索引信息
如果你需要在转换过程中访问元素的索引,可以使用 mapIndexed()
:
val empNameList = empList.mapIndexed { index, item ->
item.name()
}
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
⚠️ 虽然索引在本例中没有使用,但当你需要根据位置进行逻辑处理时非常有用。
4. 使用 for
循环转换列表
虽然 Kotlin 提供了函数式 API,但传统的 for
循环在某些场景下依然适用,尤其是对可读性要求较高的场景。
4.1 普通 for
循环
val empNameList = mutableListOf<String>()
for (emp in empList) {
empNameList.add(emp.name())
}
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
4.2 forEach()
循环
val empNameList = mutableListOf<String>()
empList.forEach { emp ->
empNameList.add(emp.name())
}
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
✅ 优点:直观、易于调试
❌ 缺点:代码冗长,不如 map()
简洁
5. 使用 flatMap()
转换嵌套对象列表
当面对嵌套结构时,比如一个组织包含多个员工,我们可以通过 flatMap()
将嵌套结构“展平”并提取字段。
5.1 定义 Organization
类
data class Organization (
val orgId: Int,
val employees: List<Employee>
)
5.2 创建组织列表
val empByOrgList = listOf(
Organization(
100,
listOf(
Employee(1, "John", "Doe", 1200.0),
Employee(2, "Jill", "Fowler", 1200.0)
)
),
Organization(
200,
listOf(
Employee(3, "Jack", "Smith", 1200.0),
Employee(4, "John", "Doe", 1200.0)
)
)
)
5.3 使用 flatMap()
提取所有员工名字
val empNameList = empByOrgList
.flatMap { it.employees }
.map { it.name() }
assertTrue(empNameList.isNotEmpty())
assertTrue(empNameList == expectedEmpNames)
✅ 优点:适用于嵌套结构,逻辑清晰
⚠️ 注意:flatMap()
会将多个列表合并为一个,避免使用 map().flatten()
等价写法。
6. 总结
在本教程中,我们学习了多种方法将一个自定义对象列表转换为字符串列表:
方法 | 是否推荐 | 说明 |
---|---|---|
map() |
✅ 推荐 | 最常用,语法简洁 |
mapNotNull() |
✅ 推荐 | 处理可能为 null 的元素 |
mapTo() |
✅ 可选 | 转换到已有集合 |
mapIndexed() |
✅ 可选 | 需要索引时使用 |
for / forEach |
⚠️ 一般 | 可读性好,但不够函数式 |
flatMap() |
✅ 推荐 | 处理嵌套结构时非常有效 |
最终选择哪种方式取决于具体业务场景和团队编码风格。函数式写法通常更简洁,但也要注意可维护性。
完整的示例代码可在 GitHub 上找到。