Appearance
循环控制
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 编程的基础工具,就像是程序员的"瑞士军刀"。通过本文的学习,我们了解到:
核心要点回顾
for循环:适用于遍历集合和已知次数的重复操作while循环:适用于条件驱动的重复操作do-while循环:适用于至少执行一次的场景- 自定义迭代器:让任何对象都能支持
for循环遍历
在 SpringBoot 项目中的价值
- 批量数据处理:订单处理、数据迁移、报表生成
- 重试机制:网络调用、数据库操作的容错处理
- 分页处理:大数据量的分批处理
- 业务流程控制:复杂业务逻辑的步骤控制
IMPORTANT
选择合适的循环类型不仅能让代码更清晰,还能提高程序的性能和可维护性。记住:简洁的代码往往是最好的代码。
下一步学习建议
- 深入学习 Kotlin 的集合操作函数(
map、filter、reduce等) - 了解协程中的循环处理方式
- 学习函数式编程中的递归替代循环的思想
- 掌握 Spring Boot 中的异步处理和批量操作
循环虽然看似简单,但掌握其精髓需要在实践中不断磨练。就像学习骑自行车一样,理论知识只是开始,真正的技能来自于反复的练习和应用。加油,未来的 Kotlin 大师! 🎉