在这里插入图片描述

目录

  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]

这段代码演示了如何使用 filter() 函数进行基础过滤。创建一个 1 到 10 的整数列表。使用 filter() 筛选偶数,Lambda 表达式 { it % 2 == 0 } 检查每个元素是否是偶数。使用 filter() 筛选大于 5 的数字,结果是 [6, 7, 8, 9, 10]。使用 filter() 筛选小于 3 的数字,结果是 [1, 2]。filter() 是最常用的过滤函数,可以应用于各种条件筛选场景。

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]

这段代码演示了如何使用 filterNot() 函数进行反向过滤。filterNot()filter() 的反面,返回不满足条件的元素。使用 filterNot() 筛选非偶数(即奇数),结果是 [1, 3, 5, 7, 9]。使用 filterNot() 筛选不大于 5 的数字,结果是 [1, 2, 3, 4, 5]。filterNot() 在需要反向筛选时比使用 filter() 并反转条件更简洁。

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]

这段代码演示了如何使用 filterIsInstance() 函数按类型过滤。创建一个混合类型的列表。使用 filterIsInstance<Int>() 筛选整数类型的元素,结果是 [1, 3]。使用 filterIsInstance<String>() 筛选字符串类型的元素,结果是 [hello, world]。使用 filterIsInstance<Double>() 筛选浮点数类型的元素,结果是 [2.5]。filterIsInstance() 是处理混合类型集合的有效方法。

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]

这段代码演示了如何使用 filterIndexed() 函数根据索引进行过滤。创建一个列表。使用 filterIndexed() 筛选索引为偶数的元素,Lambda 表达式接收 index_(元素值不使用),结果是 [10, 30, 50]。使用 filterIndexed() 筛选索引大于 1 且值大于 20 的元素,结果是 [30, 40, 50]。filterIndexed() 在需要根据位置和值的组合条件进行过滤时非常有用。


高级过滤

链式过滤

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

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(", ")}"
}

这是过滤操作的核心实现函数,展示了完整的过滤算法。函数使用 @OptIn(ExperimentalJsExport::class)@JsExport 注解,使其可以被编译成 JavaScript 并在 ArkTS 中调用。函数创建一个 1 到 10 的整数列表。使用 filter() 筛选偶数。使用 filter() 筛选大于 5 的数字。使用链式 filter() 进行多条件过滤,先筛选大于 3 的数字,再筛选小于 8 的数字,实现"大于 3 且小于 8"的条件。使用 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;
}

这段代码是 Kotlin 编译器生成的 JavaScript 代码,展示了 Kotlin 过滤操作如何被转换为 JavaScript。listOf() 函数创建一个列表。ArrayList_init_$Create$() 创建一个临时的 ArrayList 用于存储过滤结果。numbers.b() 获取列表的迭代器。while 循环遍历集合中的每个元素。if 语句检查条件,满足条件的元素被添加到 destination 中。destination.t(element) 将元素添加到结果列表。链式过滤被转换为多个连续的 while 循环,每个循环对应一个 filter() 操作。joinToString() 函数将数组转换为字符串。这个编译过程展示了 KMP 如何将高级的 Kotlin 过滤操作转换为高效的 JavaScript 代码。

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')
  }
}

这段 ArkTS 代码是鸿蒙应用的用户界面实现,展示了如何在 ArkTS 中导入和调用编译后的 Kotlin 函数。首先使用 import 语句从编译后的 JavaScript 模块中导入 filterExample 函数。使用 @State 装饰器定义两个响应式状态变量:message 用于显示状态信息,results 存储过滤操作的结果。aboutToAppear() 生命周期函数在组件加载时自动调用 loadResults() 进行初始化。loadResults() 方法调用 Kotlin 编译的 JavaScript 函数 filterExample(),获取过滤结果,然后更新 resultsmessage。使用 try-catch 块捕获异常。build() 方法构建整个 UI 布局,包含标题、状态信息、可滚动的结果显示区域和刷新按钮。使用 ForEach 遍历 results 数组,为每个结果创建一个 Text 组件。整个界面占满屏幕,背景色为浅灰色。

执行流程说明
  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

这段代码演示了如何使用 any() 函数检查是否存在满足条件的元素。使用 any() 检查是否存在偶数,结果为 true。使用 any() 检查是否存在大于 100 的数字,结果为 false。对于空集合,any() 返回 false,因为没有任何元素。any() 是一个高效的条件检查函数,它可以在找到第一个满足条件的元素后立即返回,不需要遇历整个集合。

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

这段代码演示了如何使用 all() 函数检查是否所有元素都满足条件。使用 all() 检查是否所有元素都是正数,结果为 true。使用 all() 检查是否所有元素都是偶数,结果为 false,因为列表中有奇数。使用 all() 检查是否所有元素都小于 20,结果为 true。all() 是一个全称检查函数,它需要所有元素都满足条件才返回 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

这段代码演示了如何使用 none() 函数检查是否没有元素满足条件。使用 none() 检查是否没有负数,结果为 true。使用 none() 检查是否没有偶数,结果为 false,因为列表中有偶数。使用 none() 检查是否没有大于 100 的数字,结果为 true。none()any() 的反面,它检查是否没有任何元素满足条件。

条件检查示例

@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"
}

这是条件检查的核心实现函数,展示了完整的条件检查算法。函数使用 @OptIn(ExperimentalJsExport::class)@JsExport 注解,使其可以被编译成 JavaScript 并在 ArkTS 中调用。函数创建一个 1 到 10 的整数列表。使用 any() 检查是否存在偶数。使用 all() 检查是否所有元素都是正数。使用 none() 检查是否没有负数。最后使用字符串模板和 joinToString() 格式化输出结果。这个函数展示了如何综合使用条件检查函数。


分组和分割

groupBy - 按条件分组

groupBy 函数用于按条件分组。它接受一个 lambda 表达式作为参数,返回一个 Map,表示按条件分组后的结果。

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]}

这段代码演示了如何使用 groupBy() 函数按条件分组。创建一个 1 到 10 的列表。使用 groupBy() 按奇偶性分组,Lambda 表达式返回 “偶数” 或 “奇数” 作为分组键。结果是一个 Map,键为 “偶数” 和 “奇数”,值为对应的元素列表。使用 groupBy() 按模 3 分组,Lambda 表达式返回 it % 3 作为分组键。结果是一个 Map,键为 0、1、2,值为对应的元素列表。groupBy() 是处理数据分类的有效方法。

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]

这段代码演示了如何使用 partition() 函数将集合分割成两部分。创建一个 1 到 10 的列表。使用 partition() 按奇偶性分割,Lambda 表达式返回 true 的元素进入第一个列表(偶数),返回 false 的元素进入第二个列表(奇数)。使用解构赋值 val (evens, odds) = 获取两个列表。使用 partition() 按大小分割,返回大于 5 的元素和小于等于 5 的元素。partition()filter() 的区别在于它同时返回满足和不满足条件的两个列表,避免了两次遍历。

分组示例

@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")}"
}

这是分组操作的核心实现函数,展示了完整的分组算法。函数使用 @OptIn(ExperimentalJsExport::class)@JsExport 注解,使其可以被编译成 JavaScript 并在 ArkTS 中调用。函数创建一个 1 到 10 的整数列表。使用 groupBy() 按奇偶性分组,返回一个 Map。创建一个可变列表用于存储格式化的结果。使用 forEach() 遍历 Map,对每个分组键值对进行处理。使用解构 (key, values) 获取分组键和对应的元素列表。使用 joinToString() 将元素列表转换为字符串。最后返回格式化的多行结果。这个函数展示了如何综合使用分组操作。


实战案例

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

在上面的"高级过滤"部分已经展示了完整的三层代码示例(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 }

这段代码对比了及早过滤 vs 后期过滤的性能差异。第一种方法先使用 filter() 过滤出大于 5 的数字,然后再使用 map() 转换,这样只需要转换 5 个元素。第二种方法先使用 map() 转换所有 10 个元素,然后再使用 filter() 过滤,这样需要转换所有元素。及早过滤可以减少后续操作的数据量,提高性能。

2. 使用 Sequence 处理大数据

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

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

这段代码对比了使用 Sequence vs 使用列表进行链式操作的性能差异。第一种方法使用 asSequence() 将列表转换为序列,然后链式调用 filter()map()。Sequence 使用延迟计算,不会创建中间列表。第二种方法直接在列表上链式调用 filter()map(),每个操作都会创建一个中间列表。对于大数据集,第一种方法性能更好,因为它避免了创建多个中间集合。

3. 避免重复过滤

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

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

这段代码对比了合并条件 vs 重复过滤的性能差异。第一种方法使用单个 filter() 调用,条件为 it > 5 && it < 20,只需要遍历一次集合。第二种方法使用两个 filter() 调用,第一个过滤大于 5 的数字,第二个过滤小于 20 的数字,需要遍历两次。合并条件可以减少遍历次数,提高性能。

4. 选择合适的操作

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

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

这段代码对比了使用专门函数 vs 使用 filter 进行条件检查的性能差异。第一种方法使用 any() 函数,它可以在找到第一个满足条件的元素后立即返回,不需要遍历整个集合。第二种方法使用 filter() 创建一个中间列表,然后检查是否为空,这需要遍历所有元素。选择合适的操作可以显著提高性能。


常见问题

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 优化大数据处理性能

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐