Skip to content

循环控制

Kotlin 的三种循环方式

1. for 循环:遍历的艺术家 🎨

for 循环就像是一个贴心的服务员,能够逐一为你处理集合中的每个元素。在 Kotlin 中,for 循环的设计哲学是"简洁而强大"。

基础语法与原理

kotlin
// SpringBoot 服务中的订单处理示例
@Service
class OrderService {

    fun processOrders(orders: List<Order>) {
        for (order in orders) {                    
            println("Processing order: ${order.id}")
            validateOrder(order)
            calculateTotal(order)
            updateInventory(order)
        }
    }

    private fun validateOrder(order: Order) {
        // 订单验证逻辑
    }

    private fun calculateTotal(order: Order) {
        // 计算订单总价
    }

    private fun updateInventory(order: Order) {
        // 更新库存
    }
}

data class Order(val id: String, val items: List<String>)

TIP

for 循环在 Kotlin 中不仅仅是语法糖,它背后使用了迭代器模式,这意味着任何实现了 Iterable 接口的对象都可以被遍历。

实际业务场景:批量数据处理

在微服务架构中,我们经常需要批量处理数据。以下是一个电商系统中批量更新商品价格的例子:

kotlin
@RestController
@RequestMapping("/api/products")
class ProductController(
    private val productService: ProductService
) {

    @PostMapping("/batch-update-prices")
    fun batchUpdatePrices(@RequestBody priceUpdates: List<PriceUpdateRequest>): ResponseEntity<BatchUpdateResponse> {
        val results = mutableListOf<UpdateResult>()
        var successCount = 0
        var failureCount = 0

        for (update in priceUpdates) {                           
            try {
                productService.updatePrice(update.productId, update.newPrice)
                results.add(UpdateResult(update.productId, "SUCCESS", null))
                successCount++
            } catch (e: Exception) {
                results.add(UpdateResult(update.productId, "FAILED", e.message))
                failureCount++
            }
        }

        return ResponseEntity.ok(
            BatchUpdateResponse(
                totalProcessed = priceUpdates.size,
                successCount = successCount,
                failureCount = failureCount,
                details = results
            )
        )
    }
}

data class PriceUpdateRequest(val productId: String, val newPrice: BigDecimal)
data class UpdateResult(val productId: String, val status: String, val error: String?)
data class BatchUpdateResponse(
    val totalProcessed: Int,
    val successCount: Int,
    val failureCount: Int,
    val details: List<UpdateResult>
)

2. while 循环:条件守护者 🛡️

while 循环就像是一个尽职的门卫,只要条件满足,就会一直执行下去。它特别适合处理"不知道确切次数,但知道停止条件"的场景。

业务场景:分页数据处理

在处理大量数据时,我们通常使用分页来避免内存溢出。while 循环在这种场景下表现出色:

kotlin
@Service
class DataMigrationService(
    private val sourceRepository: SourceDataRepository,
    private val targetRepository: TargetDataRepository
) {

    private val logger = LoggerFactory.getLogger(DataMigrationService::class.java)

    fun migrateAllData() {
        var currentPage = 0
        val pageSize = 1000
        var hasMoreData = true

        while (hasMoreData) {                                    
            val dataPage = sourceRepository.findAll(
                PageRequest.of(currentPage, pageSize)
            )

            if (dataPage.content.isEmpty()) {
                hasMoreData = false
                logger.info("数据迁移完成,共处理 ${currentPage * pageSize} 条记录")
            } else {
                // 处理当前页的数据
                val transformedData = dataPage.content.map { transformData(it) }
                targetRepository.saveAll(transformedData)

                logger.info("已处理第 ${currentPage + 1} 页,共 ${dataPage.content.size} 条记录")
                currentPage++
            }
        }
    }

    private fun transformData(source: SourceData): TargetData {
        // 数据转换逻辑
        return TargetData(
            id = source.id,
            name = source.name.uppercase(),
            processedAt = LocalDateTime.now()
        )
    }
}

WARNING

使用 while 循环时要特别注意避免无限循环!确保循环体内有能够改变条件的代码,否则程序会一直运行下去。

3. do-while 循环:先行动后思考 🏃‍♂️

do-while 循环遵循"先做再问"的哲学,至少会执行一次循环体。这在某些业务场景中非常有用,比如用户输入验证或重试机制。

业务场景:API 重试机制

在微服务架构中,服务间调用可能因为网络问题而失败。使用 do-while 循环实现重试机制是一个经典应用:

kotlin
@Service
class ExternalApiService {

    private val logger = LoggerFactory.getLogger(ExternalApiService::class.java)
    private val restTemplate = RestTemplate()

    fun callExternalApiWithRetry(url: String, maxRetries: Int = 3): String? {
        var attempt = 0
        var response: String? = null

        do {                                                     
            attempt++
            try {
                logger.info("尝试调用外部API,第 $attempt 次尝试")
                response = restTemplate.getForObject(url, String::class.java)
                logger.info("API调用成功")
                break // 成功后跳出循环
            } catch (e: Exception) {
                logger.warn("API调用失败,第 $attempt 次尝试: ${e.message}")
                if (attempt < maxRetries) {
                    Thread.sleep(1000 * attempt) // 指数退避
                }
            }
        } while (attempt < maxRetries)                           

        if (response == null) {
            logger.error("API调用最终失败,已重试 $maxRetries 次")
        }

        return response
    }
}

高级特性:自定义迭代器 🔧

Kotlin 的循环系统最强大的特性之一是支持自定义迭代器。这让我们可以为任何类添加 for 循环支持,就像给对象装上了"遍历引擎"。

实战案例:订单管理系统

假设我们有一个订单管理系统,需要能够遍历订单中的所有商品:

kotlin
// 商品数据类
data class Product(
    val id: String,
    val name: String,
    val price: BigDecimal,
    val category: String
)

// 订单类,实现自定义迭代器
class Order(
    val id: String,
    val customerId: String,
    private val products: MutableList<Product> = mutableListOf()
) {

    fun addProduct(product: Product) {
        products.add(product)
    }

    // 自定义迭代器的核心:operator fun iterator()
    operator fun iterator(): Iterator<Product> {                
        return products.iterator()                              
    }                                                           

    fun getTotalAmount(): BigDecimal {
        return products.sumOf { it.price }
    }
}

// 在 SpringBoot 服务中使用
@Service
class OrderProcessingService {

    fun generateOrderReport(order: Order): OrderReport {
        val productDetails = mutableListOf<String>()
        var totalAmount = BigDecimal.ZERO

        // 现在可以直接遍历 Order 对象了!
        for (product in order) {                                
            productDetails.add("${product.name}: ${product.price}")
            totalAmount = totalAmount.add(product.price)
        }

        return OrderReport(
            orderId = order.id,
            productCount = productDetails.size,
            productDetails = productDetails,
            totalAmount = totalAmount
        )
    }
}

data class OrderReport(
    val orderId: String,
    val productCount: Int,
    val productDetails: List<String>,
    val totalAmount: BigDecimal
)

更复杂的迭代器:带过滤功能的商品目录

让我们创建一个更高级的例子,实现一个可以按类别过滤的商品目录:

Details

点击查看完整的商品目录迭代器实现

kotlin
// 商品目录类,支持按类别过滤的迭代
class ProductCatalog(
    private val products: List<Product>
) {

    // 默认迭代器:遍历所有商品
    operator fun iterator(): Iterator<Product> {
        return products.iterator()
    }

    // 按类别过滤的迭代器
    fun byCategory(category: String): CategoryFilteredCatalog {
        return CategoryFilteredCatalog(products, category)
    }

    // 按价格范围过滤的迭代器
    fun byPriceRange(minPrice: BigDecimal, maxPrice: BigDecimal): PriceFilteredCatalog {
        return PriceFilteredCatalog(products, minPrice, maxPrice)
    }
}

// 按类别过滤的包装类
class CategoryFilteredCatalog(
    private val products: List<Product>,
    private val targetCategory: String
) {
    operator fun iterator(): Iterator<Product> {
        return products.filter { it.category == targetCategory }.iterator()
    }
}

// 按价格过滤的包装类
class PriceFilteredCatalog(
    private val products: List<Product>,
    private val minPrice: BigDecimal,
    private val maxPrice: BigDecimal
) {
    operator fun iterator(): Iterator<Product> {
        return products.filter {
            it.price >= minPrice && it.price <= maxPrice
        }.iterator()
    }
}

// 在控制器中使用
@RestController
@RequestMapping("/api/products")
class ProductController(
    private val productService: ProductService
) {

    @GetMapping("/category/{category}")
    fun getProductsByCategory(@PathVariable category: String): List<ProductDto> {
        val catalog = ProductCatalog(productService.getAllProducts())
        val results = mutableListOf<ProductDto>()

        // 使用自定义迭代器遍历特定类别的商品
        for (product in catalog.byCategory(category)) {          
            results.add(ProductDto(
                id = product.id,
                name = product.name,
                price = product.price,
                category = product.category
            ))
        }

        return results
    }

    @GetMapping("/price-range")
    fun getProductsByPriceRange(
        @RequestParam minPrice: BigDecimal,
        @RequestParam maxPrice: BigDecimal
    ): List<ProductDto> {
        val catalog = ProductCatalog(productService.getAllProducts())
        val results = mutableListOf<ProductDto>()

        // 使用价格范围迭代器
        for (product in catalog.byPriceRange(minPrice, maxPrice)) {  
            results.add(ProductDto(
                id = product.id,
                name = product.name,
                price = product.price,
                category = product.category
            ))
        }

        return results
    }
}

data class ProductDto(
    val id: String,
    val name: String,
    val price: BigDecimal,
    val category: String
)

循环的执行流程图

让我们通过时序图来理解不同循环的执行流程:

最佳实践与常见陷阱 ⚠️

1. 选择合适的循环类型

kotlin
// 使用 while 循环遍历集合(不推荐)
val products = listOf("apple", "banana", "orange")
var index = 0
while (index < products.size) {                    
    println(products[index])
    index++
}
kotlin
// 使用 for 循环遍历集合(推荐)
val products = listOf("apple", "banana", "orange")
for (product in products) {                        
    println(product)
}

// 如果需要索引,使用 withIndex()
for ((index, product) in products.withIndex()) {   
    println("$index: $product")
}

2. 避免无限循环

CAUTION

无限循环是程序员的噩梦,会导致程序崩溃或服务器资源耗尽。

kotlin
@Service
class SafeLoopService {

    fun processDataSafely() {
        var attempts = 0
        val maxAttempts = 10

        while (someCondition() && attempts < maxAttempts) {     
            // 处理逻辑
            attempts++

            // 添加适当的延迟,避免CPU占用过高
            Thread.sleep(100)
        }

        if (attempts >= maxAttempts) {
            throw RuntimeException("处理失败:超过最大尝试次数")
        }
    }

    private fun someCondition(): Boolean {
        // 某些业务条件
        return false
    }
}

3. 性能优化技巧

TIP

在处理大量数据时,考虑使用 Kotlin 的序列(Sequence)来实现惰性求值,可以显著提高性能。

kotlin
@Service
class OptimizedDataProcessor {

    // 传统方式:立即处理所有数据
    fun processDataTraditional(data: List<String>): List<String> {
        return data
            .filter { it.length > 5 }                          
            .map { it.uppercase() }                            
            .filter { it.startsWith("A") }                     
    }

    // 优化方式:使用序列进行惰性处理
    fun processDataOptimized(data: List<String>): List<String> {
        return data.asSequence()                               
            .filter { it.length > 5 }                         
            .map { it.uppercase() }                            
            .filter { it.startsWith("A") }                     
            .toList()                                          
    }
}

实际项目中的综合应用 🚀

让我们通过一个完整的电商订单处理系统来展示循环的综合应用:

kotlin
@Service
@Transactional
class OrderProcessingService(
    private val orderRepository: OrderRepository,
    private val inventoryService: InventoryService,
    private val paymentService: PaymentService,
    private val notificationService: NotificationService
) {

    private val logger = LoggerFactory.getLogger(OrderProcessingService::class.java)

    /**
     * 批量处理订单 - 展示多种循环的组合使用
     */
    fun processPendingOrders(): BatchProcessResult {
        val pendingOrders = orderRepository.findByStatus(OrderStatus.PENDING)
        var processedCount = 0
        var failedCount = 0
        val failedOrders = mutableListOf<String>()

        // 使用 for 循环处理每个订单
        for (order in pendingOrders) {                          
            try {
                if (processOrder(order)) {
                    processedCount++
                } else {
                    failedCount++
                    failedOrders.add(order.id)
                }
            } catch (e: Exception) {
                logger.error("处理订单 ${order.id} 时发生错误", e)
                failedCount++
                failedOrders.add(order.id)
            }
        }

        return BatchProcessResult(
            totalOrders = pendingOrders.size,
            processedCount = processedCount,
            failedCount = failedCount,
            failedOrderIds = failedOrders
        )
    }

    /**
     * 处理单个订单 - 展示 while 循环在重试机制中的应用
     */
    private fun processOrder(order: Order): Boolean {
        // 1. 检查库存
        if (!checkInventory(order)) {
            return false
        }

        // 2. 处理支付 - 使用 while 循环实现重试
        var paymentAttempts = 0
        val maxPaymentAttempts = 3
        var paymentSuccessful = false

        while (!paymentSuccessful && paymentAttempts < maxPaymentAttempts) {  
            try {
                paymentService.processPayment(order.paymentInfo)
                paymentSuccessful = true
            } catch (e: PaymentException) {
                paymentAttempts++
                logger.warn("支付失败,第 $paymentAttempts 次尝试: ${e.message}")
                Thread.sleep(1000) // 等待1秒后重试
            }
        }

        if (!paymentSuccessful) {
            return false
        }

        // 3. 更新库存
        updateInventory(order)

        // 4. 更新订单状态
        order.status = OrderStatus.PROCESSED
        orderRepository.save(order)

        // 5. 发送通知
        notificationService.sendOrderConfirmation(order)

        return true
    }

    /**
     * 检查库存 - 展示自定义迭代器的使用
     */
    private fun checkInventory(order: Order): Boolean {
        // 假设 Order 实现了自定义迭代器,可以遍历订单项
        for (item in order) {                                   
            val availableStock = inventoryService.getAvailableStock(item.productId)
            if (availableStock < item.quantity) {
                logger.warn("商品 ${item.productId} 库存不足,需要 ${item.quantity},可用 $availableStock")
                return false
            }
        }
        return true
    }

    /**
     * 更新库存 - 展示 do-while 循环在数据一致性中的应用
     */
    private fun updateInventory(order: Order) {
        for (item in order) {
            var updateSuccessful = false
            var attempts = 0
            val maxAttempts = 5

            // 使用 do-while 确保至少尝试一次,并在并发冲突时重试
            do {                                                
                attempts++
                try {
                    inventoryService.decreaseStock(item.productId, item.quantity)
                    updateSuccessful = true
                } catch (e: OptimisticLockingFailureException) {
                    logger.warn("库存更新冲突,第 $attempts 次重试")
                    Thread.sleep(50) // 短暂等待后重试
                }
            } while (!updateSuccessful && attempts < maxAttempts)  

            if (!updateSuccessful) {
                throw RuntimeException("库存更新失败,商品ID: ${item.productId}")
            }
        }
    }
}

// 相关数据类
data class BatchProcessResult(
    val totalOrders: Int,
    val processedCount: Int,
    val failedCount: Int,
    val failedOrderIds: List<String>
)

enum class OrderStatus {
    PENDING, PROCESSED, CANCELLED, SHIPPED
}

总结与展望 🎯

循环控制结构是 Kotlin 编程的基础工具,就像是程序员的"瑞士军刀"。通过本文的学习,我们了解到:

核心要点回顾

  1. for 循环:适用于遍历集合和已知次数的重复操作
  2. while 循环:适用于条件驱动的重复操作
  3. do-while 循环:适用于至少执行一次的场景
  4. 自定义迭代器:让任何对象都能支持 for 循环遍历

在 SpringBoot 项目中的价值

  • 批量数据处理:订单处理、数据迁移、报表生成
  • 重试机制:网络调用、数据库操作的容错处理
  • 分页处理:大数据量的分批处理
  • 业务流程控制:复杂业务逻辑的步骤控制

IMPORTANT

选择合适的循环类型不仅能让代码更清晰,还能提高程序的性能和可维护性。记住:简洁的代码往往是最好的代码。

下一步学习建议

  1. 深入学习 Kotlin 的集合操作函数(mapfilterreduce 等)
  2. 了解协程中的循环处理方式
  3. 学习函数式编程中的递归替代循环的思想
  4. 掌握 Spring Boot 中的异步处理和批量操作

循环虽然看似简单,但掌握其精髓需要在实践中不断磨练。就像学习骑自行车一样,理论知识只是开始,真正的技能来自于反复的练习和应用。加油,未来的 Kotlin 大师! 🎉