数组和集合
概述
Kotlin提供了丰富的数组和集合类型来存储和操作数据。本章将详细介绍数组、列表、集合、映射等数据结构,以及它们的创建、操作和最佳实践。
数组 (Arrays)
基本数组操作
kotlin
fun main() {
// 创建数组的不同方式
val numbers1 = arrayOf(1, 2, 3, 4, 5)
val numbers2 = Array(5) { i -> i * 2 } // [0, 2, 4, 6, 8]
val numbers3 = Array(5) { 0 } // [0, 0, 0, 0, 0]
// 指定类型的数组
val strings: Array<String> = arrayOf("apple", "banana", "cherry")
val nullableStrings: Array<String?> = arrayOfNulls(3)
println("数组创建:")
println("numbers1: ${numbers1.contentToString()}")
println("numbers2: ${numbers2.contentToString()}")
println("strings: ${strings.contentToString()}")
// 数组访问和修改
println("\n数组访问:")
println("第一个元素: ${numbers1[0]}")
println("最后一个元素: ${numbers1[numbers1.size - 1]}")
println("数组长度: ${numbers1.size}")
// 修改数组元素
numbers1[0] = 10
println("修改后: ${numbers1.contentToString()}")
// 安全访问
fun safeGet(array: Array<Int>, index: Int): Int? {
return if (index in array.indices) array[index] else null
}
println("安全访问索引5: ${safeGet(numbers1, 5)}")
println("安全访问索引2: ${safeGet(numbers1, 2)}")
}原始类型数组
kotlin
fun main() {
// 原始类型数组(避免装箱开销)
val intArray = intArrayOf(1, 2, 3, 4, 5)
val doubleArray = doubleArrayOf(1.1, 2.2, 3.3)
val booleanArray = booleanArrayOf(true, false, true)
val charArray = charArrayOf('a', 'b', 'c')
val longArray = LongArray(5) { it.toLong() * 10 }
println("原始类型数组:")
println("IntArray: ${intArray.contentToString()}")
println("DoubleArray: ${doubleArray.contentToString()}")
println("BooleanArray: ${booleanArray.contentToString()}")
println("CharArray: ${charArray.contentToString()}")
println("LongArray: ${longArray.contentToString()}")
// 数组操作
println("\n数组操作:")
println("求和: ${intArray.sum()}")
println("平均值: ${intArray.average()}")
println("最大值: ${intArray.maxOrNull()}")
println("最小值: ${intArray.minOrNull()}")
println("排序后: ${intArray.sorted()}")
// 数组转换
val doubled = intArray.map { it * 2 }
val filtered = intArray.filter { it > 2 }
val exists = intArray.any { it > 4 }
val allPositive = intArray.all { it > 0 }
println("翻倍: $doubled")
println("过滤>2: $filtered")
println("存在>4: $exists")
println("全部正数: $allPositive")
}多维数组
kotlin
fun main() {
// 二维数组
val matrix = Array(3) { Array(3) { 0 } }
val matrix2 = arrayOf(
arrayOf(1, 2, 3),
arrayOf(4, 5, 6),
arrayOf(7, 8, 9)
)
// 初始化矩阵
for (i in matrix.indices) {
for (j in matrix[i].indices) {
matrix[i][j] = i * 3 + j + 1
}
}
println("矩阵操作:")
println("矩阵1:")
printMatrix(matrix)
println("矩阵2:")
printMatrix(matrix2)
// 矩阵运算
val sum = addMatrices(matrix, matrix2)
println("矩阵相加:")
printMatrix(sum)
// 转置矩阵
val transposed = transposeMatrix(matrix2)
println("转置矩阵:")
printMatrix(transposed)
}
fun printMatrix(matrix: Array<Array<Int>>) {
for (row in matrix) {
println(row.joinToString(" ") { "%3d".format(it) })
}
}
fun addMatrices(a: Array<Array<Int>>, b: Array<Array<Int>>): Array<Array<Int>> {
require(a.size == b.size && a[0].size == b[0].size) { "矩阵尺寸不匹配" }
return Array(a.size) { i ->
Array(a[i].size) { j ->
a[i][j] + b[i][j]
}
}
}
fun transposeMatrix(matrix: Array<Array<Int>>): Array<Array<Int>> {
val rows = matrix.size
val cols = matrix[0].size
return Array(cols) { j ->
Array(rows) { i ->
matrix[i][j]
}
}
}列表 (Lists)
不可变列表
kotlin
fun main() {
// 创建不可变列表
val fruits = listOf("apple", "banana", "cherry", "date")
val numbers = listOf(1, 2, 3, 4, 5)
val mixed = listOf("hello", 42, 3.14, true)
val emptyList = emptyList<String>()
println("列表创建:")
println("水果: $fruits")
println("数字: $numbers")
println("混合: $mixed")
// 列表访问
println("\n列表访问:")
println("第一个水果: ${fruits[0]}")
println("第一个水果(first): ${fruits.first()}")
println("最后一个水果: ${fruits.last()}")
println("第二个水果(getOrNull): ${fruits.getOrNull(1)}")
println("索引10(getOrNull): ${fruits.getOrNull(10)}")
// 列表属性
println("\n列表属性:")
println("大小: ${fruits.size}")
println("是否为空: ${fruits.isEmpty()}")
println("包含'apple': ${"apple" in fruits}")
println("包含'grape': ${"grape" in fruits}")
// 列表切片
println("\n列表切片:")
println("前3个: ${fruits.take(3)}")
println("跳过2个: ${fruits.drop(2)}")
println("子列表[1,3): ${fruits.subList(1, 3)}")
// 列表查找
println("\n列表查找:")
println("'cherry'的索引: ${fruits.indexOf("cherry")}")
println("'grape'的索引: ${fruits.indexOf("grape")}") // -1表示未找到
println("以'a'开头的第一个: ${fruits.find { it.startsWith('a') }}")
println("长度>5的第一个: ${fruits.find { it.length > 5 }}")
}可变列表
kotlin
fun main() {
// 创建可变列表
val mutableFruits = mutableListOf("apple", "banana")
val arrayList = arrayListOf<String>()
println("可变列表操作:")
println("初始列表: $mutableFruits")
// 添加元素
mutableFruits.add("cherry")
mutableFruits.add(1, "orange") // 在指定位置插入
mutableFruits.addAll(listOf("grape", "kiwi"))
println("添加后: $mutableFruits")
// 删除元素
mutableFruits.remove("banana")
mutableFruits.removeAt(0) // 删除指定位置
mutableFruits.removeAll(listOf("grape", "kiwi"))
println("删除后: $mutableFruits")
// 修改元素
mutableFruits[0] = "pineapple"
println("修改后: $mutableFruits")
// 列表操作
mutableFruits.sort()
println("排序后: $mutableFruits")
mutableFruits.reverse()
println("反转后: $mutableFruits")
mutableFruits.shuffle()
println("打乱后: $mutableFruits")
// 批量操作
val numbers = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
numbers.removeIf { it % 2 == 0 } // 删除偶数
println("删除偶数后: $numbers")
numbers.replaceAll { it * 2 } // 所有元素翻倍
println("翻倍后: $numbers")
}列表的函数式操作
kotlin
data class Person(val name: String, val age: Int, val city: String)
fun main() {
val people = listOf(
Person("Alice", 25, "New York"),
Person("Bob", 30, "London"),
Person("Charlie", 35, "Tokyo"),
Person("Diana", 28, "New York"),
Person("Eve", 32, "London")
)
println("函数式列表操作:")
// map - 转换
val names = people.map { it.name }
val ages = people.map { it.age }
val nameAgeMap = people.map { "${it.name} (${it.age})" }
println("姓名: $names")
println("年龄: $ages")
println("姓名年龄: $nameAgeMap")
// filter - 过滤
val adults = people.filter { it.age >= 30 }
val newYorkers = people.filter { it.city == "New York" }
println("30岁以上: ${adults.map { it.name }}")
println("纽约人: ${newYorkers.map { it.name }}")
// groupBy - 分组
val peopleByCity = people.groupBy { it.city }
val peopleByAgeGroup = people.groupBy {
when {
it.age < 30 -> "年轻"
it.age < 35 -> "中年"
else -> "成熟"
}
}
println("按城市分组:")
peopleByCity.forEach { (city, cityPeople) ->
println(" $city: ${cityPeople.map { it.name }}")
}
println("按年龄组分组:")
peopleByAgeGroup.forEach { (ageGroup, agePeople) ->
println(" $ageGroup: ${agePeople.map { it.name }}")
}
// 聚合操作
val totalAge = people.sumOf { it.age }
val averageAge = people.map { it.age }.average()
val oldestPerson = people.maxByOrNull { it.age }
val youngestPerson = people.minByOrNull { it.age }
println("总年龄: $totalAge")
println("平均年龄: ${"%.1f".format(averageAge)}")
println("最年长: ${oldestPerson?.name}")
println("最年轻: ${youngestPerson?.name}")
// 链式操作
val result = people
.filter { it.age >= 28 }
.sortedBy { it.age }
.groupBy { it.city }
.mapValues { (_, cityPeople) -> cityPeople.map { it.name } }
println("链式操作结果: $result")
}集合 (Sets)
不可变集合
kotlin
fun main() {
// 创建不可变集合
val numbers = setOf(1, 2, 3, 4, 5, 3, 2, 1) // 重复元素会被去除
val fruits = setOf("apple", "banana", "cherry")
val emptySet = emptySet<String>()
println("集合创建:")
println("数字集合: $numbers") // [1, 2, 3, 4, 5]
println("水果集合: $fruits")
println("集合大小: ${numbers.size}")
// 集合检查
println("\n集合检查:")
println("包含3: ${3 in numbers}")
println("包含6: ${6 in numbers}")
println("是否为空: ${numbers.isEmpty()}")
// 集合运算
val set1 = setOf(1, 2, 3, 4, 5)
val set2 = setOf(4, 5, 6, 7, 8)
println("\n集合运算:")
println("set1: $set1")
println("set2: $set2")
println("并集 (union): ${set1 union set2}")
println("交集 (intersect): ${set1 intersect set2}")
println("差集 (subtract): ${set1 - set2}")
println("对称差集: ${(set1 - set2) + (set2 - set1)}")
// 子集检查
val subset = setOf(2, 3)
println("$subset 是 $set1 的子集: ${subset.all { it in set1 }}")
// 集合转换
val doubled = numbers.map { it * 2 }.toSet()
val filtered = numbers.filter { it > 2 }.toSet()
println("\n集合转换:")
println("翻倍: $doubled")
println("过滤>2: $filtered")
}可变集合
kotlin
fun main() {
// 创建可变集合
val mutableNumbers = mutableSetOf(1, 2, 3)
val hashSet = hashSetOf<String>()
val linkedHashSet = linkedSetOf<String>() // 保持插入顺序
println("可变集合操作:")
println("初始集合: $mutableNumbers")
// 添加元素
mutableNumbers.add(4)
mutableNumbers.add(2) // 重复元素不会被添加
mutableNumbers.addAll(setOf(5, 6, 7))
println("添加后: $mutableNumbers")
// 删除元素
mutableNumbers.remove(3)
mutableNumbers.removeAll(setOf(6, 7))
println("删除后: $mutableNumbers")
// 保留操作
mutableNumbers.retainAll(setOf(1, 2, 4, 8)) // 只保留指定元素
println("保留后: $mutableNumbers")
// LinkedHashSet示例(保持插入顺序)
linkedHashSet.addAll(listOf("third", "first", "second"))
println("LinkedHashSet: $linkedHashSet") // 保持插入顺序
// HashSet示例(不保证顺序)
hashSet.addAll(listOf("third", "first", "second"))
println("HashSet: $hashSet") // 顺序可能不同
}映射 (Maps)
不可变映射
kotlin
fun main() {
// 创建不可变映射
val capitals = mapOf(
"USA" to "Washington",
"UK" to "London",
"Japan" to "Tokyo",
"France" to "Paris"
)
val numbers = mapOf(1 to "one", 2 to "two", 3 to "three")
val emptyMap = emptyMap<String, String>()
println("映射创建:")
println("首都: $capitals")
println("数字: $numbers")
// 映射访问
println("\n映射访问:")
println("USA的首都: ${capitals["USA"]}")
println("China的首都: ${capitals["China"]}") // null
println("UK的首都(getValue): ${capitals.getValue("UK")}")
println("Germany的首都(getOrDefault): ${capitals.getOrDefault("Germany", "Unknown")}")
// 映射属性
println("\n映射属性:")
println("大小: ${capitals.size}")
println("是否为空: ${capitals.isEmpty()}")
println("包含键'Japan': ${"Japan" in capitals}")
println("包含值'Berlin': ${"Berlin" in capitals.values}")
// 键和值
println("\n键和值:")
println("所有键: ${capitals.keys}")
println("所有值: ${capitals.values}")
println("所有条目: ${capitals.entries}")
// 映射遍历
println("\n映射遍历:")
for ((country, capital) in capitals) {
println("$country 的首都是 $capital")
}
capitals.forEach { (country, capital) ->
println("$country -> $capital")
}
}可变映射
kotlin
fun main() {
// 创建可变映射
val mutableScores = mutableMapOf(
"Alice" to 95,
"Bob" to 87,
"Charlie" to 92
)
val hashMap = hashMapOf<String, Int>()
val linkedHashMap = linkedMapOf<String, Int>() // 保持插入顺序
println("可变映射操作:")
println("初始分数: $mutableScores")
// 添加和修改
mutableScores["Diana"] = 89 // 添加新条目
mutableScores["Alice"] = 98 // 修改现有条目
mutableScores.put("Eve", 91)
mutableScores.putAll(mapOf("Frank" to 85, "Grace" to 94))
println("添加后: $mutableScores")
// 删除
mutableScores.remove("Bob")
val removedScore = mutableScores.remove("Charlie")
println("删除Charlie的分数: $removedScore")
println("删除后: $mutableScores")
// 条件操作
mutableScores.putIfAbsent("Alice", 100) // 如果不存在才添加
mutableScores.replace("Diana", 89, 90) // 条件替换
println("条件操作后: $mutableScores")
// 计算操作
mutableScores.compute("Alice") { _, oldValue -> (oldValue ?: 0) + 2 }
mutableScores.computeIfAbsent("Henry") { 88 }
mutableScores.computeIfPresent("Eve") { _, oldValue -> oldValue + 5 }
println("计算操作后: $mutableScores")
// 合并操作
mutableScores.merge("Grace", 10) { oldValue, newValue -> oldValue + newValue }
println("合并操作后: $mutableScores")
}映射的高级操作
kotlin
data class Student(val name: String, val grade: Int, val subject: String)
fun main() {
val students = listOf(
Student("Alice", 95, "Math"),
Student("Bob", 87, "Math"),
Student("Charlie", 92, "Science"),
Student("Diana", 89, "Math"),
Student("Eve", 91, "Science"),
Student("Frank", 85, "English"),
Student("Grace", 94, "English")
)
println("映射高级操作:")
// 按科目分组
val studentsBySubject = students.groupBy { it.subject }
println("按科目分组:")
studentsBySubject.forEach { (subject, subjectStudents) ->
println(" $subject: ${subjectStudents.map { it.name }}")
}
// 创建姓名到成绩的映射
val nameToGrade = students.associate { it.name to it.grade }
println("\n姓名到成绩映射: $nameToGrade")
// 按科目统计平均分
val averageBySubject = students
.groupBy { it.subject }
.mapValues { (_, subjectStudents) ->
subjectStudents.map { it.grade }.average()
}
println("各科目平均分:")
averageBySubject.forEach { (subject, average) ->
println(" $subject: ${"%.1f".format(average)}")
}
// 映射转换
val gradeDistribution = students
.groupingBy {
when {
it.grade >= 90 -> "A"
it.grade >= 80 -> "B"
it.grade >= 70 -> "C"
else -> "D"
}
}
.eachCount()
println("成绩分布: $gradeDistribution")
// 映射过滤
val highPerformers = nameToGrade.filter { (_, grade) -> grade >= 90 }
val mathStudents = studentsBySubject["Math"]?.associate { it.name to it.grade } ?: emptyMap()
println("高分学生: $highPerformers")
println("数学学生: $mathStudents")
// 映射合并
val bonusPoints = mapOf("Alice" to 5, "Bob" to 3, "Charlie" to 2)
val finalGrades = nameToGrade.toMutableMap()
bonusPoints.forEach { (name, bonus) ->
finalGrades.merge(name, bonus) { oldGrade, bonusPoint -> oldGrade + bonusPoint }
}
println("加分后成绩: $finalGrades")
}序列 (Sequences)
延迟计算
kotlin
fun main() {
// 序列 vs 集合的性能比较
val largeList = (1..1_000_000).toList()
println("序列操作:")
// 使用集合(立即计算)
val listResult = largeList
.filter { it % 2 == 0 }
.map { it * it }
.take(10)
println("集合操作结果: $listResult")
// 使用序列(延迟计算)
val sequenceResult = largeList.asSequence()
.filter { it % 2 == 0 }
.map { it * it }
.take(10)
.toList()
println("序列操作结果: $sequenceResult")
// 创建序列
val sequence1 = sequenceOf(1, 2, 3, 4, 5)
val sequence2 = generateSequence(1) { it + 1 } // 无限序列
val sequence3 = generateSequence { kotlin.random.Random.nextInt(100) }
println("序列1前5个: ${sequence1.take(5).toList()}")
println("序列2前10个: ${sequence2.take(10).toList()}")
println("序列3前5个: ${sequence3.take(5).toList()}")
// 斐波那契序列
val fibonacci = generateSequence(Pair(0, 1)) { (a, b) -> Pair(b, a + b) }
.map { it.first }
println("斐波那契前15个: ${fibonacci.take(15).toList()}")
// 文件处理示例(模拟)
fun processLargeDataset(): Sequence<String> = sequence {
repeat(1000) { index ->
yield("数据行 $index")
// 模拟从文件或数据库读取数据
}
}
val processedData = processLargeDataset()
.filter { it.contains("5") }
.map { it.uppercase() }
.take(5)
.toList()
println("处理的数据: $processedData")
}集合的实际应用
数据分析示例
kotlin
data class SalesRecord(
val date: String,
val product: String,
val category: String,
val amount: Double,
val quantity: Int,
val region: String
)
class SalesAnalyzer {
fun analyzeSales(records: List<SalesRecord>): Map<String, Any> {
val totalSales = records.sumOf { it.amount }
val totalQuantity = records.sumOf { it.quantity }
val averageOrderValue = totalSales / records.size
// 按产品分析
val salesByProduct = records
.groupBy { it.product }
.mapValues { (_, productRecords) ->
productRecords.sumOf { it.amount }
}
.toList()
.sortedByDescending { it.second }
// 按类别分析
val salesByCategory = records
.groupBy { it.category }
.mapValues { (_, categoryRecords) ->
mapOf(
"totalSales" to categoryRecords.sumOf { it.amount },
"totalQuantity" to categoryRecords.sumOf { it.quantity },
"averagePrice" to categoryRecords.map { it.amount / it.quantity }.average()
)
}
// 按地区分析
val salesByRegion = records
.groupBy { it.region }
.mapValues { (_, regionRecords) -> regionRecords.sumOf { it.amount } }
// 趋势分析
val salesByDate = records
.groupBy { it.date }
.mapValues { (_, dateRecords) -> dateRecords.sumOf { it.amount } }
.toSortedMap()
return mapOf(
"totalSales" to totalSales,
"totalQuantity" to totalQuantity,
"averageOrderValue" to averageOrderValue,
"topProducts" to salesByProduct.take(5),
"categoryAnalysis" to salesByCategory,
"regionAnalysis" to salesByRegion,
"dailyTrend" to salesByDate
)
}
}
fun main() {
val salesRecords = listOf(
SalesRecord("2023-01-01", "笔记本电脑", "电子产品", 999.99, 1, "北京"),
SalesRecord("2023-01-01", "鼠标", "电子产品", 29.99, 2, "上海"),
SalesRecord("2023-01-02", "键盘", "电子产品", 79.99, 1, "广州"),
SalesRecord("2023-01-02", "显示器", "电子产品", 299.99, 1, "深圳"),
SalesRecord("2023-01-03", "咖啡", "食品", 15.99, 3, "北京"),
SalesRecord("2023-01-03", "茶叶", "食品", 25.99, 2, "上海"),
SalesRecord("2023-01-04", "笔记本电脑", "电子产品", 1199.99, 1, "广州"),
SalesRecord("2023-01-04", "手机", "电子产品", 699.99, 1, "深圳")
)
val analyzer = SalesAnalyzer()
val analysis = analyzer.analyzeSales(salesRecords)
println("销售数据分析报告:")
println("总销售额: ${"%.2f".format(analysis["totalSales"])}")
println("总销量: ${analysis["totalQuantity"]}")
println("平均订单价值: ${"%.2f".format(analysis["averageOrderValue"])}")
println("\n热销产品TOP5:")
@Suppress("UNCHECKED_CAST")
val topProducts = analysis["topProducts"] as List<Pair<String, Double>>
topProducts.forEach { (product, sales) ->
println(" $product: ${"%.2f".format(sales)}")
}
println("\n按类别分析:")
@Suppress("UNCHECKED_CAST")
val categoryAnalysis = analysis["categoryAnalysis"] as Map<String, Map<String, Any>>
categoryAnalysis.forEach { (category, stats) ->
println(" $category:")
println(" 总销售额: ${"%.2f".format(stats["totalSales"])}")
println(" 总销量: ${stats["totalQuantity"]}")
println(" 平均单价: ${"%.2f".format(stats["averagePrice"])}")
}
}性能优化
集合选择指南
kotlin
fun main() {
println("集合性能指南:")
// 1. 根据使用场景选择合适的集合类型
// 需要保持插入顺序且允许重复 -> List
val orderHistory = mutableListOf<String>()
// 需要唯一性且不关心顺序 -> HashSet
val uniqueVisitors = hashSetOf<String>()
// 需要唯一性且保持插入顺序 -> LinkedHashSet
val orderedCategories = linkedSetOf<String>()
// 需要键值映射且不关心顺序 -> HashMap
val userCache = hashMapOf<String, Any>()
// 需要键值映射且保持插入顺序 -> LinkedHashMap
val orderedConfig = linkedMapOf<String, String>()
// 2. 大数据集处理使用序列
fun processLargeDataEfficiently(data: List<Int>): List<Int> {
return data.asSequence()
.filter { it > 100 }
.map { it * 2 }
.take(1000)
.toList()
}
// 3. 避免不必要的中间集合
val numbers = (1..1000).toList()
// 不好:创建多个中间集合
val result1 = numbers
.filter { it % 2 == 0 }
.map { it * it }
.filter { it > 100 }
.sorted()
// 好:使用序列减少中间集合
val result2 = numbers.asSequence()
.filter { it % 2 == 0 }
.map { it * it }
.filter { it > 100 }
.sorted()
.toList()
println("优化后的结果大小: ${result2.size}")
}最佳实践
1. 选择合适的集合类型
kotlin
// 根据需求选择集合类型
class BestPractices {
// 只读数据使用不可变集合
val supportedLanguages = listOf("Kotlin", "Java", "Python", "JavaScript")
// 需要修改的数据使用可变集合
private val _activeUsers = mutableSetOf<String>()
val activeUsers: Set<String> = _activeUsers // 暴露只读视图
// 大数据集使用序列
fun processLargeDataset(data: List<String>): List<String> {
return data.asSequence()
.filter { it.isNotBlank() }
.map { it.trim().lowercase() }
.distinct()
.sorted()
.toList()
}
// 使用适当的集合操作
fun findUsersByCity(users: List<Person>, city: String): List<Person> {
return users.filter { it.city == city } // 而不是手动循环
}
}2. 空安全和错误处理
kotlin
fun safeCollectionOperations() {
val numbers: List<Int>? = null
val emptyList = emptyList<Int>()
val normalList = listOf(1, 2, 3, 4, 5)
// 安全访问
println("空列表第一个元素: ${numbers?.firstOrNull()}")
println("空列表大小: ${numbers?.size ?: 0}")
// 使用安全操作
val result = normalList
.takeIf { it.isNotEmpty() }
?.filter { it > 2 }
?.map { it * 2 }
?: emptyList()
println("安全操作结果: $result")
}下一步
掌握了数组和集合的使用后,让我们学习Kotlin中的接口和抽象类。
下一章: 接口和抽象类
练习题
- 实现一个学生成绩管理系统,使用不同的集合类型存储数据
- 创建一个图书馆管理系统,支持图书的增删改查操作
- 编写一个数据分析工具,处理大量销售数据并生成报告
- 实现一个缓存系统,使用映射存储键值对并支持过期时间
- 设计一个推荐系统,基于用户行为数据推荐相关内容