目录

  1. 概述

  2. 基础过滤

  3. 高级过滤

  4. 条件检查

  5. 分组和分割

  6. 实战案例

  7. 性能优化

  8. 常见问题


概述

本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中进行过滤操作和条件筛选。过滤是数据处理中最常见的操作之一。Kotlin 提供了强大而灵活的过滤 API,允许我们根据各种条件从集合中筛选出需要的元素。通过 KMP,这些过滤操作可以无缝编译到 JavaScript,在 OpenHarmony 应用中高效运行。

为什么需要学习过滤操作?

  • 数据清洗:从原始数据中提取有效数据,支持鸿蒙应用的数据处理

  • 业务逻辑:根据条件筛选符合要求的数据,实现复杂的业务需求

  • 性能优化:及早过滤可以减少后续处理的数据量,提高应用性能

  • 代码简洁:使用函数式过滤比循环更清晰,提高代码可维护性

  • 链式操作:可以组合多个过滤条件,实现复杂的数据处理流程

  • 跨端兼容:过滤操作在编译到 JavaScript 时表现出色,完美支持 OpenHarmony 应用


基础过滤

filter - 基础过滤

filter 函数根据条件筛选元素,返回满足条件的元素列表。

// 基础用法
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
​
// 筛选偶数
val evens = numbers.filter { it % 2 == 0 }
println(evens)  // [2, 4, 6, 8, 10]
​
// 筛选大于 5 的数字
val greaterThan5 = numbers.filter { it > 5 }
println(greaterThan5)  // [6, 7, 8, 9, 10]
​
// 筛选小于 3 的数字
val lessThan3 = numbers.filter { it < 3 }
println(lessThan3)  // [1, 2]

filterNot - 反向过滤

filterNot 返回不满足条件的元素。

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
​
// 筛选非偶数(奇数)
val odds = numbers.filterNot { it % 2 == 0 }
println(odds)  // [1, 3, 5, 7, 9]
​
// 筛选不大于 5 的数字
val notGreaterThan5 = numbers.filterNot { it > 5 }
println(notGreaterThan5)  // [1, 2, 3, 4, 5]

filterIsInstance - 类型过滤

按类型过滤集合中的元素。

val mixed = listOf(1, "hello", 2.5, "world", 3, true)
​
// 筛选整数
val integers = mixed.filterIsInstance<Int>()
println(integers)  // [1, 3]
​
// 筛选字符串
val strings = mixed.filterIsInstance<String>()
println(strings)  // [hello, world]
​
// 筛选浮点数
val doubles = mixed.filterIsInstance<Double>()
println(doubles)  // [2.5]

filterIndexed - 带索引的过滤

根据元素索引进行过滤。

val numbers = listOf(10, 20, 30, 40, 50)
​
// 筛选索引为偶数的元素
val evenIndices = numbers.filterIndexed { index, _ -> index % 2 == 0 }
println(evenIndices)  // [10, 30, 50]
​
// 筛选索引大于 1 且值大于 20 的元素
val filtered = numbers.filterIndexed { index, value -> index > 1 && value > 20 }
println(filtered)  // [30, 40, 50]

高级过滤

链式过滤

将多个过滤条件组合使用。

Kotlin 源代码
@OptIn(ExperimentalJsExport::class)
@JsExport
fun filterExample(): String {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    // 筛选偶数
    val evens = numbers.filter { it % 2 == 0 }
    
    // 筛选大于 5 的数字
    val greaterThan5 = numbers.filter { it > 5 }
    
    // 链式过滤:大于 3 且小于 8
    val result = numbers
        .filter { it > 3 }
        .filter { it < 8 }
    
    return "原始数据: ${numbers.joinToString(", ")}\n" +
           "偶数: ${evens.joinToString(", ")}\n" +
           "大于5: ${greaterThan5.joinToString(", ")}\n" +
           "3到8之间: ${result.joinToString(", ")}"
}
编译后的 JavaScript 代码
function filterExample() {
  var numbers = listOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  
  // 筛选偶数
  var destination = ArrayList_init_$Create$();
  var _iterator__ex2g4s = numbers.b();
  while (_iterator__ex2g4s.c()) {
    var element = _iterator__ex2g4s.d();
    if ((element % 2 | 0) === 0) {
      destination.t(element);
    }
  }
  var evens = destination;
  
  // 筛选大于 5 的数字
  var destination_0 = ArrayList_init_$Create$();
  var _iterator__ex2g4s_0 = numbers.b();
  while (_iterator__ex2g4s_0.c()) {
    var element_0 = _iterator__ex2g4s_0.d();
    if (element_0 > 5) {
      destination_0.t(element_0);
    }
  }
  var greaterThan5 = destination_0;
  
  // 链式过滤:大于 3 且小于 8
  var destination_1 = ArrayList_init_$Create$();
  var _iterator__ex2g4s_1 = numbers.b();
  while (_iterator__ex2g4s_1.c()) {
    var element_1 = _iterator__ex2g4s_1.d();
    if (element_1 > 3) {
      destination_1.t(element_1);
    }
  }
  var temp = destination_1;
  
  var destination_2 = ArrayList_init_$Create$();
  var _iterator__ex2g4s_2 = temp.b();
  while (_iterator__ex2g4s_2.c()) {
    var element_2 = _iterator__ex2g4s_2.d();
    if (element_2 < 8) {
      destination_2.t(element_2);
    }
  }
  var result = destination_2;
  
  var result_str = "原始数据: " + joinToString(numbers, ", ") + "\n" +
                   "偶数: " + joinToString(evens, ", ") + "\n" +
                   "大于5: " + joinToString(greaterThan5, ", ") + "\n" +
                   "3到8之间: " + joinToString(result, ", ");
  println(result_str);
  return result_str;
}
ArkTS 调用代码
import { filterExample } from './hellokjs';
​
@Entry
@Component
struct Index {
  @State message: string = '加载中...';
  @State results: string[] = [];
​
  aboutToAppear(): void {
    this.loadResults();
  }
​
  loadResults(): void {
    try {
      // 调用 Kotlin 编译的 JavaScript 函数
      const filter = filterExample();
      this.results = [filter];
      this.message = '案例已加载';
    } catch (error) {
      this.message = `错误: ${error}`;
    }
  }
​
  build() {
    Column() {
      Text('Kotlin 过滤操作案例演示')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 20 })
​
      Text(this.message)
        .fontSize(14)
        .fontColor(Color.Gray)
        .margin({ bottom: 15 })
​
      Scroll() {
        Column() {
          ForEach(this.results, (result: string) => {
            Text(result)
              .fontSize(12)
              .fontFamily('monospace')
              .padding(12)
              .width('100%')
              .backgroundColor(Color.White)
              .border({ width: 1, color: Color.Gray })
              .borderRadius(8)
          })
        }
        .width('100%')
        .padding({ left: 15, right: 15 })
      }
      .layoutWeight(1)
      .width('100%')
​
      Button('刷新结果')
        .width('80%')
        .height(40)
        .margin({ bottom: 20 })
        .onClick(() => {
          this.loadResults();
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
  }
}
执行流程说明
  1. Kotlin 源代码:定义 filterExample() 函数,使用 Kotlin 的 filter 操作符

  2. 编译过程:Gradle 使用 KMP 编译器将 Kotlin 代码编译成 JavaScript

  3. JavaScript 输出:编译器生成优化的 JavaScript 代码,使用循环和条件判断实现过滤逻辑

  4. ArkTS 调用:在 OpenHarmony 应用中导入并调用编译后的 JavaScript 函数

  5. 结果展示:在 UI 中显示过滤结果

过滤后转换

结合 filtermap 进行数据转换。在实际项目中,可以参考上面的 filterExample() 案例进行扩展。

使用自定义条件

定义复杂的过滤条件。在实际项目中,可以根据业务需求自定义过滤条件,参考上面的 filterExample() 案例的实现方式。


条件检查

any - 是否存在满足条件的元素

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
​
// 是否存在偶数
val hasEven = numbers.any { it % 2 == 0 }
println(hasEven)  // true
​
// 是否存在大于 100 的数字
val hasLarge = numbers.any { it > 100 }
println(hasLarge)  // false
​
// 空集合
val empty = emptyList<Int>()
println(empty.any { it > 0 })  // false

all - 是否所有元素都满足条件

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 是否所有元素都是正数
val allPositive = numbers.all { it > 0 }
println(allPositive)  // true

// 是否所有元素都是偶数
val allEven = numbers.all { it % 2 == 0 }
println(allEven)  // false

// 是否所有元素都小于 20
val allSmall = numbers.all { it < 20 }
println(allSmall)  // true

none - 是否没有元素满足条件

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 是否没有负数
val noNegative = numbers.none { it < 0 }
println(noNegative)  // true

// 是否没有偶数
val noEven = numbers.none { it % 2 == 0 }
println(noEven)  // false

// 是否没有大于 100 的数字
val noLarge = numbers.none { it > 100 }
println(noLarge)  // true

条件检查示例

@OptIn(ExperimentalJsExport::class)
@JsExport
fun conditionCheckExample(): String {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    val hasEven = numbers.any { it % 2 == 0 }
    val allPositive = numbers.all { it > 0 }
    val noNegative = numbers.none { it < 0 }
    
    return "数字: ${numbers.joinToString(", ")}\n" +
           "存在偶数: $hasEven\n" +
           "全部为正数: $allPositive\n" +
           "没有负数: $noNegative"
}

分组和分割

groupBy - 按条件分组

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 按奇偶性分组
val grouped = numbers.groupBy { if (it % 2 == 0) "偶数" else "奇数" }
println(grouped)
// {偶数=[2, 4, 6, 8, 10], 奇数=[1, 3, 5, 7, 9]}

// 按模 3 分组
val groupedBy3 = numbers.groupBy { it % 3 }
println(groupedBy3)
// {1=[1, 4, 7, 10], 2=[2, 5, 8], 0=[3, 6, 9]}

partition - 分割成两部分

val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 分割成偶数和奇数
val (evens, odds) = numbers.partition { it % 2 == 0 }
println("偶数: $evens")  // [2, 4, 6, 8, 10]
println("奇数: $odds")   // [1, 3, 5, 7, 9]

// 分割成大于 5 和小于等于 5
val (large, small) = numbers.partition { it > 5 }
println("大于5: $large")  // [6, 7, 8, 9, 10]
println("≤5: $small")     // [1, 2, 3, 4, 5]

分组示例

@OptIn(ExperimentalJsExport::class)
@JsExport
fun groupByExample(): String {
    val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    
    // 按奇偶性分组
    val grouped = numbers.groupBy { if (it % 2 == 0) "偶数" else "奇数" }
    
    val result = mutableListOf<String>()
    grouped.forEach { (key, values) ->
        result.add("$key: ${values.joinToString(", ")}")
    }
    
    return "按奇偶分组:\n${result.joinToString("\n")}"
}

实战案例

案例:过滤操作的实际应用

在上面的"高级过滤"部分已经展示了完整的三层代码示例(Kotlin、JavaScript、ArkTS)。这个 filterExample() 案例演示了:

  1. 基础过滤:筛选偶数和大于 5 的数字

  2. 链式过滤:组合多个过滤条件(大于 3 且小于 8)

  3. 编译过程:展示了 Kotlin 代码如何编译成 JavaScript

  4. 实际调用:展示了如何在 ArkTS 中调用编译后的函数

扩展应用场景

在实际项目中,可以基于 filterExample() 的模式进行扩展:

  • 数据清洗:过滤无效数据(如年龄 >= 18 的用户)

  • 商品筛选:按类别、价格范围、评分等条件筛选产品

  • 订单统计:筛选特定状态的订单并进行统计

  • 日志分析:过滤特定级别或时间范围的日志

所有这些应用都遵循相同的 Kotlin → JavaScript → ArkTS 的编译和调用流程。


性能优化

1. 及早过滤

// ✅ 好:先过滤再转换
val result = numbers
    .filter { it > 5 }
    .map { it * 2 }

// ❌ 不好:先转换再过滤
val result = numbers
    .map { it * 2 }
    .filter { it > 10 }

2. 使用 Sequence 处理大数据

// ✅ 好:使用 Sequence 延迟计算
val result = numbers
    .asSequence()
    .filter { it > 5 }
    .map { it * 2 }
    .toList()

// ❌ 不好:创建多个中间列表
val result = numbers
    .filter { it > 5 }
    .map { it * 2 }

3. 避免重复过滤

// ✅ 好:合并条件
val result = numbers.filter { it > 5 && it < 20 }

// ❌ 不好:重复过滤
val result = numbers
    .filter { it > 5 }
    .filter { it < 20 }

4. 选择合适的操作

// ✅ 好:使用专门的函数
val hasEven = numbers.any { it % 2 == 0 }

// ❌ 不好:使用 filter 再检查
val hasEven = numbers.filter { it % 2 == 0 }.isNotEmpty()

常见问题

Q1: filter 和 filterNot 有什么区别?

A:

  • filter:返回满足条件的元素

  • filterNot:返回不满足条件的元素

val numbers = listOf(1, 2, 3, 4, 5)

val evens = numbers.filter { it % 2 == 0 }      // [2, 4]
val odds = numbers.filterNot { it % 2 == 0 }    // [1, 3, 5]

Q2: 什么时候使用 any/all/none?

A:

  • any:检查是否存在满足条件的元素

  • all:检查是否所有元素都满足条件

  • none:检查是否没有元素满足条件

val numbers = listOf(1, 2, 3, 4, 5)

numbers.any { it > 3 }    // true(存在 4, 5)
numbers.all { it > 0 }    // true(全部为正数)
numbers.none { it < 0 }   // true(没有负数)

Q3: filter 和 partition 有什么区别?

A:

  • filter:返回满足条件的元素

  • partition:返回满足和不满足条件的两个列表

val numbers = listOf(1, 2, 3, 4, 5)

val evens = numbers.filter { it % 2 == 0 }           // [2, 4]
val (evens2, odds) = numbers.partition { it % 2 == 0 }  // [2, 4], [1, 3, 5]

Q4: 如何处理空集合的过滤?

A: 使用安全操作符和默认值

val list: List<Int>? = null

// 安全处理
val filtered = list?.filter { it > 5 } ?: emptyList()

// 或使用 orEmpty()
val filtered = list.orEmpty().filter { it > 5 }

Q5: 过滤大数据集时如何提高性能?

A: 使用 Sequence 避免创建中间集合

// ✅ 好:使用 Sequence
val result = largeList
    .asSequence()
    .filter { it > 5 }
    .map { it * 2 }
    .filter { it < 100 }
    .toList()

// ❌ 不好:创建多个中间列表
val result = largeList
    .filter { it > 5 }
    .map { it * 2 }
    .filter { it < 100 }

总结

关键要点

  • filter 是最常用的过滤操作

  • any/all/none 用于条件检查

  • ✅ 链式过滤可以组合多个条件

  • partition 可以同时获得两部分数据

  • ✅ 使用 Sequence 优化大数据处理性能

下一步

  1. 学习更多高级过滤技巧

  2. 实践复杂的数据筛选场景

  3. 优化过滤操作的性能

  4. 探索自定义过滤扩展函数


参考资源

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐