06-区间与迭代

06 - Kotlin 区间与迭代

本章介绍 Kotlin 的区间(Range)操作和迭代(Iteration)方式。


一、区间(Range)

1.1 创建区间

kotlin 复制代码
// 闭区间(包含起始和结束)
val range1 = 1..5        // [1, 2, 3, 4, 5]

// 半开区间(不包含结束)
val range2 = 1 until 5   // [1, 2, 3, 4]

// 降序区间
val range3 = 5 downTo 1  // [5, 4, 3, 2, 1]

// 指定步长
val range4 = 1..10 step 2  // [1, 3, 5, 7, 9]
val range5 = 10 downTo 1 step 2  // [10, 8, 6, 4, 2]

1.2 区间类型

类型 示例 说明
IntRange 1..10 整数区间
LongRange 1L..10L 长整数区间
CharRange 'a'..'z' 字符区间
kotlin 复制代码
val intRange: IntRange = 1..10
val longRange: LongRange = 1L..10L
val charRange: CharRange = 'a'..'z'

1.3 区间判断

kotlin 复制代码
val x = 5

// 检查是否在区间内
if (x in 1..10) {
    println("x 在区间内")
}

// 检查是否不在区间内
if (x !in 1..3) {
    println("x 不在区间内")
}

// 实际应用:数据验证
fun validateAge(age: Int): Boolean {
    return age in 0..150
}

fun validateScore(score: Int): String {
    return when (score) {
        in 90..100 -> "优秀"
        in 80..89 -> "良好"
        in 70..79 -> "中等"
        in 60..69 -> "及格"
        in 0..59 -> "不及格"
        else -> "无效分数"
    }
}

// 字符区间判断
fun isLowerCase(char: Char) = char in 'a'..'z'
fun isUpperCase(char: Char) = char in 'A'..'Z'
fun isDigit(char: Char) = char in '0'..'9'

// 多区间判断
fun isVowel(char: Char): Boolean {
    return char.lowercaseChar() in setOf('a', 'e', 'i', 'o', 'u')
}

1.4 区间遍历

kotlin 复制代码
// 正序遍历
for (i in 1..5) {
    print("$i ")  // 1 2 3 4 5
}

// 倒序遍历
for (i in 5 downTo 1) {
    print("$i ")  // 5 4 3 2 1
}

// 步长遍历
for (i in 1..10 step 2) {
    print("$i ")  // 1 3 5 7 9
}

二、迭代(Iteration)

2.1 遍历集合

kotlin 复制代码
val list = listOf("a", "b", "c")

// 遍历元素
for (item in list) {
    println(item)
}

// 带索引遍历
for ((index, item) in list.withIndex()) {
    println("$index: $item")
}

// 使用索引
for (i in list.indices) {
    println("${i}: ${list[i]}")
}

2.2 遍历数组

kotlin 复制代码
val array = arrayOf(1, 2, 3, 4, 5)

for (item in array) {
    println(item)
}

for ((index, value) in array.withIndex()) {
    println("$index: $value")
}

2.3 遍历字符串

kotlin 复制代码
val text = "Kotlin"

for (char in text) {
    println(char)
}

for ((index, char) in text.withIndex()) {
    println("$index: $char")
}

2.4 遍历 Map

kotlin 复制代码
val map = mapOf("a" to 1, "b" to 2, "c" to 3)

for ((key, value) in map) {
    println("$key: $value")
}

// 只遍历键
for (key in map.keys) {
    println(key)
}

// 只遍历值
for (value in map.values) {
    println(value)
}

三、迭代器(Iterator)

3.1 使用迭代器

kotlin 复制代码
val list = listOf(1, 2, 3, 4, 5)
val iterator = list.iterator()

while (iterator.hasNext()) {
    val item = iterator.next()
    println(item)
}

3.2 可变迭代器

kotlin 复制代码
val mutableList = mutableListOf(1, 2, 3, 4, 5)
val iterator = mutableList.iterator()

while (iterator.hasNext()) {
    val item = iterator.next()
    if (item % 2 == 0) {
        iterator.remove()  // 删除偶数
    }
}
println(mutableList)  // [1, 3, 5]

四、序列(Sequence)

4.1 创建序列

kotlin 复制代码
// 从集合创建
val sequence1 = listOf(1, 2, 3).asSequence()

// 使用 sequenceOf
val sequence2 = sequenceOf(1, 2, 3, 4, 5)

// 使用 generateSequence
val sequence3 = generateSequence(1) { it + 1 }  // 无限序列

4.2 序列操作

kotlin 复制代码
val numbers = (1..1_000_000).asSequence()
    .filter { it % 2 == 0 }
    .map { it * it }
    .take(5)
    .toList()

println(numbers)  // [4, 16, 36, 64, 100]

// 实际应用:处理大文件
fun processLargeFile(filePath: String) {
    File(filePath).useLines { lines ->
        lines.asSequence()
            .filter { it.isNotBlank() }
            .map { it.trim() }
            .take(100)
            .forEach { println(it) }
    }
}

// 无限序列
val fibonacci = generateSequence(0 to 1) { (a, b) -> b to a + b }
    .map { it.first }
    .take(10)
    .toList()
println(fibonacci)  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

4.3 序列 vs 集合

特性 集合 序列
计算方式 立即计算 惰性计算
中间操作 每次创建新集合 延迟到终端操作
适用场景 小数据集 大数据集或无限序列
kotlin 复制代码
// 集合:立即计算
val list = listOf(1, 2, 3, 4, 5)
    .filter { println("filter $it"); it % 2 == 0 }
    .map { println("map $it"); it * 2 }

// 序列:惰性计算
val sequence = listOf(1, 2, 3, 4, 5).asSequence()
    .filter { println("filter $it"); it % 2 == 0 }
    .map { println("map $it"); it * 2 }
    .toList()  // 此时才开始计算

五、高阶函数迭代

5.1 forEach

kotlin 复制代码
listOf(1, 2, 3).forEach { println(it) }

listOf(1, 2, 3).forEachIndexed { index, value ->
    println("$index: $value")
}

5.2 map

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)
val doubled = numbers.map { it * 2 }
println(doubled)  // [2, 4, 6, 8, 10]

5.3 filter

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)
val even = numbers.filter { it % 2 == 0 }
println(even)  // [2, 4]

5.4 reduce / fold

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// reduce:从第一个元素开始累积
val sum = numbers.reduce { acc, i -> acc + i }
println(sum)  // 15

// fold:指定初始值
val product = numbers.fold(1) { acc, i -> acc * i }
println(product)  // 120

// 实际应用:字符串拼接
val words = listOf("Kotlin", "is", "awesome")
val sentence = words.reduce { acc, word -> "$acc $word" }
println(sentence)  // Kotlin is awesome

// 计算最大值
val max = numbers.reduce { acc, i -> if (i > acc) i else acc }
println(max)  // 5

// fold 的优势:可以改变返回类型
val lengths = words.fold(0) { acc, word -> acc + word.length }
println(lengths)  // 15

// foldRight:从右向左累积
val result = listOf("a", "b", "c").foldRight("") { str, acc -> acc + str }
println(result)  // cba

5.5 any / all / none

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// any:是否存在满足条件的元素
val hasEven = numbers.any { it % 2 == 0 }
println(hasEven)  // true

// all:是否所有元素都满足条件
val allPositive = numbers.all { it > 0 }
println(allPositive)  // true

// none:是否没有元素满足条件
val noNegative = numbers.none { it < 0 }
println(noNegative)  // true

// 实际应用:表单验证
data class User(val name: String, val age: Int, val email: String)

val users = listOf(
    User("Alice", 25, "alice@example.com"),
    User("Bob", 17, "bob@example.com"),
    User("Charlie", 30, "charlie@example.com")
)

val allAdults = users.all { it.age >= 18 }  // false
val hasMinor = users.any { it.age < 18 }    // true
val noEmptyName = users.none { it.name.isBlank() }  // true

5.6 find / first / last

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// find:查找第一个满足条件的元素(找不到返回 null)
val firstEven = numbers.find { it % 2 == 0 }
println(firstEven)  // 2

// findLast:查找最后一个满足条件的元素
val lastEven = numbers.findLast { it % 2 == 0 }
println(lastEven)  // 4

// first:获取第一个元素(空集合会抛异常)
val first = numbers.first()
println(first)  // 1

// firstOrNull:安全获取第一个元素
val firstOrNull = emptyList<Int>().firstOrNull()
println(firstOrNull)  // null

// first with predicate:获取第一个满足条件的元素
val firstGreaterThan3 = numbers.first { it > 3 }
println(firstGreaterThan3)  // 4

// last:获取最后一个元素
val last = numbers.last()
println(last)  // 5

六、实用示例

6.1 九九乘法表

kotlin 复制代码
for (i in 1..9) {
    for (j in 1..i) {
        print("$j×$i=${i*j}\t")
    }
    println()
}

6.2 查找素数

kotlin 复制代码
fun isPrime(n: Int): Boolean {
    if (n <= 1) return false
    if (n == 2) return true
    if (n % 2 == 0) return false

    // 优化:只检查到 sqrt(n)
    for (i in 3..Math.sqrt(n.toDouble()).toInt() step 2) {
        if (n % i == 0) return false
    }
    return true
}

// 使用区间查找素数
val primes = (2..100).filter { isPrime(it) }
println(primes)

// 使用序列优化大范围查找
val largePrimes = (2..10000).asSequence()
    .filter { isPrime(it) }
    .take(100)
    .toList()

6.3 反转字符串

kotlin 复制代码
fun reverseString(text: String): String {
    var result = ""
    for (i in text.length - 1 downTo 0) {
        result += text[i]
    }
    return result
}

println(reverseString("Kotlin"))  // niltoK

6.4 斐波那契数列

kotlin 复制代码
// 使用序列生成
val fibonacci = generateSequence(Pair(0, 1)) { Pair(it.second, it.first + it.second) }
    .map { it.first }
    .take(10)
    .toList()

println(fibonacci)  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

// 使用循环生成
fun fibonacciList(n: Int): List<Long> {
    if (n <= 0) return emptyList()
    if (n == 1) return listOf(0)

    val result = mutableListOf(0L, 1L)
    for (i in 2 until n) {
        result.add(result[i - 1] + result[i - 2])
    }
    return result
}

println(fibonacciList(10))  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

6.5 数字金字塔

kotlin 复制代码
// 打印数字金字塔
fun printNumberPyramid(rows: Int) {
    for (i in 1..rows) {
        // 打印空格
        repeat(rows - i) { print(" ") }
        // 打印数字
        for (j in 1..i) {
            print("$j ")
        }
        println()
    }
}

printNumberPyramid(5)
/*
    1
   1 2
  1 2 3
 1 2 3 4
1 2 3 4 5
*/

// 打印星号金字塔
fun printStarPyramid(rows: Int) {
    for (i in 1..rows) {
        repeat(rows - i) { print(" ") }
        repeat(2 * i - 1) { print("*") }
        println()
    }
}

6.6 矩阵操作

kotlin 复制代码
// 创建矩阵
fun createMatrix(rows: Int, cols: Int): Array<IntArray> {
    return Array(rows) { row ->
        IntArray(cols) { col -> row * cols + col + 1 }
    }
}

// 打印矩阵
fun printMatrix(matrix: Array<IntArray>) {
    for (row in matrix) {
        for (value in row) {
            print("$value\t")
        }
        println()
    }
}

// 矩阵转置
fun transpose(matrix: Array<IntArray>): Array<IntArray> {
    val rows = matrix.size
    val cols = matrix[0].size
    return Array(cols) { col ->
        IntArray(rows) { row -> matrix[row][col] }
    }
}

// 使用
val matrix = createMatrix(3, 4)
printMatrix(matrix)
println("转置后:")
printMatrix(transpose(matrix))

6.7 数据统计

kotlin 复制代码
data class Sale(val product: String, val amount: Double, val quantity: Int)

val sales = listOf(
    Sale("Apple", 100.0, 10),
    Sale("Banana", 50.0, 20),
    Sale("Apple", 150.0, 15),
    Sale("Orange", 80.0, 8)
)

// 按产品分组统计
val salesByProduct = sales.groupBy { it.product }
    .mapValues { (_, sales) ->
        sales.sumOf { it.amount }
    }
println(salesByProduct)  // {Apple=250.0, Banana=50.0, Orange=80.0}

// 计算总销售额
val totalAmount = sales.sumOf { it.amount }
println("总销售额: $totalAmount")

// 找出销量最高的产品
val topProduct = sales.groupBy { it.product }
    .mapValues { (_, sales) -> sales.sumOf { it.quantity } }
    .maxByOrNull { it.value }
println("销量最高: $topProduct")

// 计算平均单价
val avgPrice = sales.map { it.amount / it.quantity }
    .average()
println("平均单价: $avgPrice")

七、性能优化技巧

7.1 序列 vs 集合的选择

kotlin 复制代码
// 小数据集:使用集合(立即计算)
val smallList = listOf(1, 2, 3, 4, 5)
    .filter { it % 2 == 0 }
    .map { it * it }

// 大数据集:使用序列(惰性计算)
val largeSequence = (1..1_000_000).asSequence()
    .filter { it % 2 == 0 }
    .map { it * it }
    .take(10)
    .toList()

// 多次操作:序列更高效
val result = (1..1000).asSequence()
    .filter { it % 2 == 0 }
    .map { it * 2 }
    .filter { it > 100 }
    .take(5)
    .toList()

7.2 避免不必要的中间集合

kotlin 复制代码
// ❌ 低效:创建多个中间集合
val result1 = list
    .filter { it > 0 }
    .map { it * 2 }
    .filter { it < 100 }

// ✅ 高效:合并条件
val result2 = list
    .filter { it > 0 && it * 2 < 100 }
    .map { it * 2 }

// ✅ 使用序列
val result3 = list.asSequence()
    .filter { it > 0 }
    .map { it * 2 }
    .filter { it < 100 }
    .toList()

7.3 使用专用函数

kotlin 复制代码
// ❌ 低效
val sum1 = list.map { it * 2 }.reduce { acc, i -> acc + i }

// ✅ 高效
val sum2 = list.sumOf { it * 2 }

// ❌ 低效
val hasEven1 = list.filter { it % 2 == 0 }.isNotEmpty()

// ✅ 高效
val hasEven2 = list.any { it % 2 == 0 }

八、区间与迭代对比

操作 区间 迭代
用途 表示数值范围 遍历元素
创建 1..10 for (i in list)
判断 x in 1..10 -
步长 1..10 step 2 -
倒序 10 downTo 1 reversed()
性能 常量空间 取决于集合大小

集合操作 vs 序列操作

特性 集合操作 序列操作
计算时机 立即计算 惰性计算
中间结果 创建新集合 不创建中间集合
内存占用 较高 较低
适用场景 小数据集、需要多次访问 大数据集、单次遍历
性能 小数据集更快 大数据集更快

九、常见陷阱与最佳实践

9.1 区间边界问题

kotlin 复制代码
// ⚠️ 注意:.. 是闭区间,包含结束值
for (i in 0..array.size) {  // ❌ 会越界
    println(array[i])
}

// ✅ 使用 until 或 indices
for (i in 0 until array.size) {  // ✅ 正确
    println(array[i])
}

for (i in array.indices) {  // ✅ 更好
    println(array[i])
}

9.2 空集合处理

kotlin 复制代码
val emptyList = emptyList<Int>()

// ❌ 会抛异常
// val first = emptyList.first()

// ✅ 安全处理
val first = emptyList.firstOrNull()
val max = emptyList.maxOrNull()
val sum = emptyList.sum()  // 返回 0

9.3 修改正在迭代的集合

kotlin 复制代码
val list = mutableListOf(1, 2, 3, 4, 5)

// ❌ 会抛 ConcurrentModificationException
// for (item in list) {
//     if (item % 2 == 0) {
//         list.remove(item)
//     }
// }

// ✅ 使用迭代器
val iterator = list.iterator()
while (iterator.hasNext()) {
    if (iterator.next() % 2 == 0) {
        iterator.remove()
    }
}

// ✅ 创建新集合
val filtered = list.filter { it % 2 != 0 }

// ✅ 使用 removeIf
list.removeIf { it % 2 == 0 }

9.4 最佳实践总结

kotlin 复制代码
// ✅ 优先使用不可变集合
val list = listOf(1, 2, 3)

// ✅ 使用函数式操作代替循环
val doubled = list.map { it * 2 }  // 而不是手动循环

// ✅ 链式调用保持简洁
val result = list
    .filter { it > 0 }
    .map { it * 2 }
    .take(5)

// ✅ 大数据集使用序列
val largeResult = (1..1_000_000).asSequence()
    .filter { it % 2 == 0 }
    .take(100)
    .toList()

// ✅ 使用专用函数
val sum = list.sum()  // 而不是 reduce
val max = list.maxOrNull()  // 而不是手动比较

十、总结

核心要点

  1. 区间(Range)

    • 使用 .. 创建闭区间,until 创建半开区间
    • 支持 downTo 降序和 step 指定步长
    • 可用于 in 判断和 for 循环
  2. 迭代(Iteration)

    • for 循环遍历集合、数组、字符串
    • withIndex() 获取索引和值
    • indices 获取索引范围
  3. 序列(Sequence)

    • 惰性计算,适合大数据集
    • 不创建中间集合,节省内存
    • 使用 asSequence() 转换
  4. 高阶函数

    • mapfilterreducefold
    • 函数式编程风格,代码简洁
    • 链式调用提高可读性
  5. 性能优化

    • 小数据集用集合,大数据集用序列
    • 避免不必要的中间集合
    • 使用专用函数(如 sumOfany

选择指南

场景 推荐方案
数值范围遍历 区间 1..10
集合遍历 for 循环或高阶函数
大数据处理 序列 asSequence()
数据转换 mapfilter
数据聚合 sumreducefold
条件判断 anyallnone

参考资料

相关推荐
Fate_I_C3 小时前
Kotlin 中的 suspend(挂起函数)
android·开发语言·kotlin
凡小烦4 小时前
看完你就是古希腊掌管Compose输入框的神!!!
android·kotlin
モンキー・D・小菜鸡儿4 小时前
kotlin 斗牛小游戏
kotlin·小游戏
Fate_I_C5 小时前
Kotlin 中 `@JvmField` 注解的使用
android·开发语言·kotlin
大大祥5 小时前
一个kotlin实现的视频播放器
android·开发语言·kotlin·音视频
消失的旧时光-19437 小时前
从 Kotlin 到 Flutter:架构迁移指南
开发语言·flutter·kotlin
来来走走1 天前
Android开发(kotlin) 开发一个简单天气应用
android·kotlin
zhangphil1 天前
Kotlin新式管道Channel融合flow流,协程实现Android废弃的AsyncTaskLoader(B)
kotlin
Yang-Never1 天前
Android 内存泄漏 -> ViewModel持有Activity/Fragment导致的内存泄漏
android·java·开发语言·kotlin·android studio