在这里插入图片描述

目录

  1. 概述
  2. 基础排序
  3. 高级排序
  4. 自定义排序
  5. 实战案例
  6. 性能优化
  7. 常见问题

概述

本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中进行排序操作和数据排列。排序是数据处理中最常见的操作之一。通过 KMP,这些排序操作可以无缝编译到 JavaScript,在 OpenHarmony 应用中高效运行。

为什么需要学习排序操作?

  • 数据展示:按特定顺序展示数据,提升用户体验
  • 数据分析:排序后的数据便于分析和查找
  • 业务逻辑:实现排序功能是很多应用的核心需求
  • 代码简洁:使用函数式排序比手写排序算法更简洁
  • 跨端兼容:排序操作在编译到 JavaScript 时表现出色,完美支持 OpenHarmony
  • 代码复用:一份 Kotlin 代码可同时服务多个平台

基础排序

sorted - 升序排序

对集合进行升序排序。

val numbers = listOf(5, 2, 8, 1, 9, 3)
val sorted = numbers.sorted()
println(sorted)  // [1, 2, 3, 5, 8, 9]

val words = listOf("Zebra", "Apple", "Mango")
val sortedWords = words.sorted()
println(sortedWords)  // [Apple, Mango, Zebra]

代码说明:

这段代码演示了 sorted() 函数的升序排序功能。对于数字列表,sorted() 将元素按从小到大的顺序排列。对于字符串列表,sorted() 按字母顺序排列(字典序)。sorted() 函数返回一个新的排序后的列表,原列表保持不变。

sortedDescending - 降序排序

对集合进行降序排序。

val numbers = listOf(5, 2, 8, 1, 9, 3)
val sorted = numbers.sortedDescending()
println(sorted)  // [9, 8, 5, 3, 2, 1]

val words = listOf("Zebra", "Apple", "Mango")
val sortedWords = words.sortedDescending()
println(sortedWords)  // [Zebra, Mango, Apple]

代码说明:

这段代码演示了 sortedDescending() 函数的降序排序功能。对于数字列表,sortedDescending() 将元素按从大到小的顺序排列。对于字符串列表,sortedDescending() 按反向字母顺序排列。sortedDescending() 函数返回一个新的排序后的列表,原列表保持不变。

reverse - 反转

反转集合中元素的顺序。

val numbers = listOf(1, 2, 3, 4, 5)
val reversed = numbers.reversed()
println(reversed)  // [5, 4, 3, 2, 1]

// 原集合不变
println(numbers)  // [1, 2, 3, 4, 5]

代码说明:

这段代码演示了 reversed() 函数的反转功能。reversed() 将列表中的元素顺序完全反转,第一个元素变成最后一个,最后一个元素变成第一个。reversed() 函数返回一个新的反转后的列表,原列表保持不变。这个函数对于需要逆序遍历数据的场景非常有用。


高级排序

sortedBy - 按条件排序

Kotlin 源代码
@OptIn(ExperimentalJsExport::class)
@JsExport
fun sortedExample(): String {
    val numbers = listOf(5, 2, 8, 1, 9, 3, 7, 4, 6)
    val words = listOf("Zebra", "Apple", "Mango", "Banana", "Cherry")
    
    // 升序排序
    val sortedNumbers = numbers.sorted()
    
    // 降序排序
    val reverseSorted = numbers.sortedDescending()
    
    // 按长度排序
    val sortedByLength = words.sortedBy { it.length }
    
    return "原始数字: ${numbers.joinToString(", ")}\n" +
           "升序: ${sortedNumbers.joinToString(", ")}\n" +
           "降序: ${reverseSorted.joinToString(", ")}\n" +
           "按长度排序: ${sortedByLength.joinToString(", ")}"
}

代码说明:

这是排序操作的完整 Kotlin 实现。函数使用 @JsExport 装饰器将其导出为 JavaScript 可调用的函数。首先创建数字列表和字符串列表。使用 sorted() 进行升序排序。使用 sortedDescending() 进行降序排序。使用 sortedBy { it.length } 按字符串长度升序排序,Lambda 表达式 { it.length } 指定排序的关键字。最后使用 joinToString() 将列表转换为逗号分隔的字符串,并格式化输出。

编译后的 JavaScript 代码
function sortedExample() {
  var numbers = listOf_0([5, 2, 8, 1, 9, 3, 7, 4, 6]);
  var words = listOf_0(['Zebra', 'Apple', 'Mango', 'Banana', 'Cherry']);
  
  // 升序排序
  var sortedNumbers = sorted(numbers);
  
  // 降序排序
  var reverseSorted = sortedDescending(numbers);
  
  // 按长度排序
  // Inline function 'kotlin.collections.sortedBy' call
  // Inline function 'kotlin.comparisons.compareBy' call
  var tmp = sortedExample$lambda;
  var tmp$ret$0 = new sam$kotlin_Comparator$0(tmp);
  var sortedByLength = sortedWith(words, tmp$ret$0);
  
  return '原始数字: ' + joinToString_0(numbers, ', ') + '\n' + 
         ('升序: ' + joinToString_0(sortedNumbers, ', ') + '\n') + 
         ('降序: ' + joinToString_0(reverseSorted, ', ') + '\n') + 
         ('按长度排序: ' + joinToString_0(sortedByLength, ', '));
}

function sortedExample$lambda(a, b) {
  // Inline function 'kotlin.comparisons.compareValuesBy' call
  // Inline function 'sortedExample.<anonymous>' call
  var tmp = a.length;
  var tmp_0 = b.length;
  return tmp - tmp_0 | 0;
}

代码说明:

这是 Kotlin 代码编译到 JavaScript 后的结果。可以看到 Kotlin 的排序函数被转换为 JavaScript 等价物:sorted() 变成 sorted() 函数调用,sortedDescending() 变成 sortedDescending() 函数调用。sortedBy 中的 Lambda 表达式被编译成一个单独的函数 sortedExample$lambda,该函数实现了比较逻辑。joinToString() 被转换为 joinToString_0() 函数。虽然编译后的代码看起来复杂,但它保留了原始 Kotlin 代码的逻辑和功能。

ArkTS 调用代码
import { sortedExample } from './hellokjs';

@Entry
@Component
struct Index {
  @State message: string = '加载中...';
  @State results: string[] = [];

  aboutToAppear(): void {
    this.loadResults();
  }

  loadResults(): void {
    try {
      // 调用 Kotlin 编译的 JavaScript 函数
      const sortedResult = sortedExample();
      this.results = [sortedResult];
      this.message = '案例已加载';
    } catch (error) {
      this.message = `错误: ${error}`;
    }
  }

  build() {
    Column() {
      Text('Kotlin Sorted 排序操作演示')
        .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')
  }
}

代码说明:

这是 OpenHarmony ArkTS 页面的完整实现。首先导入 Kotlin 编译生成的 sortedExample 函数。定义三个状态变量:message 显示操作状态,results 存储排序结果。aboutToAppear() 生命周期钩子在页面加载时自动调用 loadResults() 进行初始排序。loadResults() 方法调用 Kotlin 函数获取排序结果,更新状态信息,使用 try-catch 捕获异常。build() 方法定义 UI 布局:顶部显示标题和状态,中间是可滚动的结果显示区域,下方是刷新按钮。使用等宽字体显示排序结果,保持格式对齐。

执行流程说明
  1. Kotlin 源代码:定义 sortedExample() 函数,使用 sorted、sortedDescending、sortedBy 进行排序
  2. 编译过程:Gradle 使用 KMP 编译器将 Kotlin 代码编译成 JavaScript
  3. JavaScript 输出:编译器生成优化的 JavaScript 代码,使用内置排序函数实现排序逻辑
  4. ArkTS 调用:在 OpenHarmony 应用中导入并调用编译后的 JavaScript 函数
  5. 结果展示:在 UI 中显示排序结果

sortedByDescending - 按条件降序排序

val words = listOf("Zebra", "Apple", "Mango", "Banana")

// 按长度降序排序
val sortedByLength = words.sortedByDescending { it.length }
println(sortedByLength)  // [Zebra, Mango, Apple, Banana]

代码说明:

这段代码演示了 sortedByDescending() 函数的按条件降序排序功能。sortedByDescending { it.length } 按字符串长度从长到短排序。Lambda 表达式 { it.length } 指定排序的关键字。结果中长度为 5 的单词(Zebra、Mango)排在前面,长度为 4 的单词(Apple、Banana)排在后面。这个函数对于需要按某个属性降序排列的场景非常有用。


自定义排序

sortedWith - 使用自定义比较器

data class Person(val name: String, val age: Int)

val people = listOf(
    Person("Alice", 30),
    Person("Bob", 25),
    Person("Charlie", 35)
)

// 按年龄排序
val byAge = people.sortedWith(compareBy { it.age })
println(byAge)
// [Person(Bob, 25), Person(Alice, 30), Person(Charlie, 35)]

// 按名字排序
val byName = people.sortedWith(compareBy { it.name })
println(byName)
// [Person(Alice, 30), Person(Bob, 25), Person(Charlie, 35)]

// 多条件排序:先按年龄,再按名字
val byAgeAndName = people.sortedWith(compareBy({ it.age }, { it.name }))

代码说明:

这段代码演示了 sortedWith() 函数的自定义排序功能。首先定义一个数据类 Person 包含名字和年龄。使用 sortedWith(compareBy { it.age }) 按年龄升序排序。使用 sortedWith(compareBy { it.name }) 按名字升序排序。使用 sortedWith(compareBy({ it.age }, { it.name })) 进行多条件排序:先按年龄升序,如果年龄相同则按名字升序。compareBy() 函数创建一个比较器,sortedWith() 使用这个比较器进行排序。这个方法对于复杂的排序需求非常灵活。


实战案例

案例:排序操作的实际应用

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

  1. 基础排序:升序排序、降序排序
  2. 高级排序:按条件排序(按长度)
  3. 编译过程:展示了 Kotlin 代码如何编译成 JavaScript
  4. 实际调用:展示了如何在 ArkTS 中调用编译后的函数
扩展应用场景

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

  • 用户列表排序:按名字、年龄、注册时间等排序
  • 商品列表排序:按价格、销量、评分等排序
  • 订单列表排序:按日期、金额、状态等排序
  • 搜索结果排序:按相关性、时间、热度等排序

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


性能优化

1. 选择合适的排序方式

// ✅ 好:使用 sorted()
val sorted = numbers.sorted()

// ❌ 不好:使用 sortedBy()
val sorted = numbers.sortedBy { it }

代码说明:

这个示例对比了两种排序方法的性能。第一种方法使用 sorted() 直接排序,是最优的选择,因为它针对可比较类型进行了优化。第二种方法使用 sortedBy { it } 虽然功能相同,但需要额外的 Lambda 表达式处理,性能略低。最佳实践是:对于简单的排序,使用 sorted()sortedDescending();对于复杂的排序条件,才使用 sortedBy()sortedWith()

2. 避免多次排序

// ❌ 不好:多次排序
val sorted1 = numbers.sorted()
val sorted2 = numbers.sortedDescending()

// ✅ 好:一次排序后反转
val sorted = numbers.sorted()
val reversed = sorted.reversed()

代码说明:

这个示例对比了排序的性能优化。第一种方法对同一个列表进行两次完整的排序操作,时间复杂度为 O(2n log n)。第二种方法先进行一次排序,然后使用 reversed() 反转结果,时间复杂度为 O(n log n + n),效率更高。最佳实践是:如果需要升序和降序两个版本,应该先排序再反转,而不是分别排序。

3. 使用 Sequence 处理大数据

// ✅ 好:使用 Sequence
val result = numbers
    .asSequence()
    .filter { it > 5 }
    .sortedBy { it }
    .toList()

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

代码说明:

这个示例对比了处理大数据集的方法。第一种方法使用 asSequence() 将列表转换为序列,序列是惰性求值的,只在最后 toList() 时才执行所有操作。这样可以避免创建中间列表,节省内存。第二种方法使用集合操作,filter() 会创建一个中间列表,然后 sortedBy() 再创建另一个中间列表,浪费内存。最佳实践是:处理大数据集时,使用 Sequence 进行链式操作。

4. 缓存排序结果

// ✅ 好:缓存排序结果
val sorted = numbers.sorted()
val first = sorted.first()
val last = sorted.last()

// ❌ 不好:多次排序
val first = numbers.sorted().first()
val last = numbers.sorted().last()

代码说明:

这个示例对比了排序结果的缓存方法。第一种方法先进行一次排序并缓存结果,然后多次使用这个结果,只需要一次 O(n log n) 的排序操作。第二种方法每次获取数据时都进行一次排序,总共需要两次 O(n log n) 的排序操作,浪费计算资源。最佳实践是:如果需要多次使用排序结果,应该先排序并缓存,然后重复使用。


常见问题

Q1: sorted 和 sortedBy 有什么区别?

A:

  • sorted:直接排序,用于可比较的类型
  • sortedBy:按指定属性排序,用于复杂对象
val numbers = listOf(5, 2, 8, 1, 9)
val sorted = numbers.sorted()  // [1, 2, 5, 8, 9]

val words = listOf("Zebra", "Apple", "Mango")
val byLength = words.sortedBy { it.length }  // [Apple, Zebra, Mango]

代码说明:

这个示例对比了 sorted()sortedBy() 的区别。sorted() 直接对可比较的类型(如数字、字符串)进行排序,使用类型的自然顺序。sortedBy() 接收一个 Lambda 表达式,指定排序的关键字,适用于复杂对象或需要自定义排序逻辑的场景。在这个例子中,sortedBy { it.length } 按字符串长度排序,而不是字母顺序。

Q2: 如何进行多条件排序?

A: 使用 sortedWith 和 compareBy

data class Person(val name: String, val age: Int)

val people = listOf(
    Person("Alice", 30),
    Person("Bob", 25),
    Person("Alice", 25)
)

// 先按名字,再按年龄
val sorted = people.sortedWith(compareBy({ it.name }, { it.age }))
// [Person(Alice, 25), Person(Alice, 30), Person(Bob, 25)]

代码说明:

这段代码演示了多条件排序。使用 compareBy() 创建一个比较器,接收多个 Lambda 表达式作为排序条件。第一个条件是名字,第二个条件是年龄。排序时先按名字升序排列,如果名字相同则按年龄升序排列。结果中两个 Alice 按年龄排序(25 在 30 之前),Bob 排在最后。这个方法对于需要按多个属性排序的场景非常有用。

Q3: 如何进行不区分大小写的排序?

A: 使用 sortedBy 和 lowercase()

val words = listOf("Zebra", "apple", "Mango", "BANANA")

// 不区分大小写排序
val sorted = words.sortedBy { it.lowercase() }
println(sorted)  // [apple, BANANA, Mango, Zebra]

代码说明:

这段代码演示了不区分大小写的排序。使用 sortedBy { it.lowercase() } 将每个字符串转换为小写后进行排序。这样无论原字符串是大写、小写还是混合大小写,都会按照小写字母的顺序排列。结果中所有单词都按字母顺序排列,不受大小写影响。这个方法对于需要忽略大小写的排序场景非常有用。

Q4: 如何对可变集合进行原地排序?

A: 使用 sort() 或 sortDescending()

val numbers = mutableListOf(5, 2, 8, 1, 9)

// 原地升序排序
numbers.sort()
println(numbers)  // [1, 2, 5, 8, 9]

// 原地降序排序
numbers.sortDescending()
println(numbers)  // [9, 8, 5, 2, 1]

代码说明:

这段代码演示了原地排序。sort()sortDescending() 直接修改可变列表,不返回新列表。与 sorted()sortedDescending() 不同,这两个函数不创建新的列表对象,而是在原列表上进行排序。这种方法对于处理大型数据集时更节省内存。注意:这些函数只能用于可变列表(MutableList),不能用于不可变列表(List)。

Q5: 排序的时间复杂度是多少?

A: Kotlin 的排序使用快速排序或合并排序,时间复杂度为 O(n log n)

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

// 时间复杂度:O(n log n)
val sorted = numbers.sorted()

// 时间复杂度:O(n log n)
val byLength = words.sortedBy { it.length }

代码说明:

这段代码说明了排序的时间复杂度。Kotlin 的 sorted()sortedBy() 函数使用高效的排序算法(通常是快速排序或合并排序),时间复杂度为 O(n log n),其中 n 是列表的元素个数。这意味着即使列表很大,排序的性能也是可以接受的。空间复杂度通常为 O(n),因为需要创建新的排序后的列表。了解时间复杂度对于编写高效的代码非常重要。


总结

关键要点

  • sorted()sortedDescending() 是最常用的排序函数
  • sortedBy() 用于按属性排序
  • sortedWith() 用于自定义排序逻辑
  • reversed() 用于反转顺序
  • ✅ 对可变集合使用 sort() 进行原地排序
Logo

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

更多推荐