KMP OpenHarmony 数据分组库示例 - 跨平台数据分组解决方案
摘要 本文介绍了一个基于Kotlin Multiplatform(KMP)和OpenHarmony的跨平台数据分组库实现。该库提供多种分组功能,包括按值分组、范围分组、条件分组等8种核心能力,支持Kotlin/JVM、Kotlin/JS和OpenHarmony/ArkTS多平台。通过KMP技术实现代码复用,核心分组类封装了分组逻辑和统计功能,包含GroupResult和GroupConfig数据结

项目概述
数据分组是现代应用开发中的重要功能。无论是在数据聚合、报表生成、分类统计还是数据分析中,都需要进行数据的分组和处理。然而,不同的编程语言和平台对数据分组的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。
本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的数据分组库示例。这个库提供了一套完整的数据分组能力,包括按字段分组、按条件分组、多级分组等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。
技术架构
多平台支持
- Kotlin/JVM: 后端服务和桌面应用
- Kotlin/JS: Web 应用和浏览器环境
- OpenHarmony/ArkTS: 鸿蒙操作系统应用
核心功能模块
- 按值分组: 按指定值进行分组
- 按范围分组: 按数值范围进行分组
- 按条件分组: 按自定义条件进行分组
- 多级分组: 进行多层次分组
- 分组统计: 统计分组结果
- 分组排序: 对分组结果排序
- 分组聚合: 对分组数据进行聚合
- 分组性能: 监控分组性能
Kotlin 实现
核心分组类
// 文件: src/commonMain/kotlin/DataGrouper.kt
/**
* 数据分组工具类
* 提供各种数据分组功能
*/
class DataGrouper {
data class GroupResult(
val groups: Map<String, List<Int>>,
val groupCount: Int,
val totalItems: Int,
val groupingTime: Long,
val averageGroupSize: Double
)
data class GroupConfig(
val enableStatistics: Boolean = true,
val enableLogging: Boolean = false,
val sortGroups: Boolean = false
)
private var config = GroupConfig()
/**
* 设置分组配置
* @param config 配置对象
*/
fun setConfig(config: GroupConfig) {
this.config = config
}
/**
* 按值分组
* @param data 数据列表
* @return 分组结果
*/
fun groupByValue(data: List<Int>): GroupResult {
val startTime = System.currentTimeMillis()
val groups = mutableMapOf<String, MutableList<Int>>()
for (item in data) {
val key = item.toString()
groups.getOrPut(key) { mutableListOf() }.add(item)
}
val groupingTime = System.currentTimeMillis() - startTime
val averageGroupSize = if (groups.isNotEmpty()) data.size.toDouble() / groups.size else 0.0
return GroupResult(
groups as Map<String, List<Int>>,
groups.size,
data.size,
groupingTime,
averageGroupSize
)
}
/**
* 按范围分组
* @param data 数据列表
* @param rangeSize 范围大小
* @return 分组结果
*/
fun groupByRange(data: List<Int>, rangeSize: Int = 10): GroupResult {
val startTime = System.currentTimeMillis()
val groups = mutableMapOf<String, MutableList<Int>>()
for (item in data) {
val rangeStart = (item / rangeSize) * rangeSize
val rangeEnd = rangeStart + rangeSize - 1
val key = "$rangeStart-$rangeEnd"
groups.getOrPut(key) { mutableListOf() }.add(item)
}
val groupingTime = System.currentTimeMillis() - startTime
val averageGroupSize = if (groups.isNotEmpty()) data.size.toDouble() / groups.size else 0.0
return GroupResult(
groups as Map<String, List<Int>>,
groups.size,
data.size,
groupingTime,
averageGroupSize
)
}
/**
* 按条件分组
* @param data 数据列表
* @param predicate 条件函数
* @return 分组结果
*/
fun groupByCondition(data: List<Int>, predicate: (Int) -> String): GroupResult {
val startTime = System.currentTimeMillis()
val groups = mutableMapOf<String, MutableList<Int>>()
for (item in data) {
val key = predicate(item)
groups.getOrPut(key) { mutableListOf() }.add(item)
}
val groupingTime = System.currentTimeMillis() - startTime
val averageGroupSize = if (groups.isNotEmpty()) data.size.toDouble() / groups.size else 0.0
return GroupResult(
groups as Map<String, List<Int>>,
groups.size,
data.size,
groupingTime,
averageGroupSize
)
}
/**
* 按奇偶分组
* @param data 数据列表
* @return 分组结果
*/
fun groupByOddEven(data: List<Int>): GroupResult {
return groupByCondition(data) { if (it % 2 == 0) "偶数" else "奇数" }
}
/**
* 按大小分组
* @param data 数据列表
* @param threshold 阈值
* @return 分组结果
*/
fun groupBySize(data: List<Int>, threshold: Int = 50): GroupResult {
return groupByCondition(data) { if (it >= threshold) "大于等于$threshold" else "小于$threshold" }
}
/**
* 获取分组统计信息
* @param result 分组结果
* @return 统计信息
*/
fun getGroupingStatistics(result: GroupResult): Map<String, Any> {
val groupSizes = result.groups.values.map { it.size }
val maxGroupSize = groupSizes.maxOrNull() ?: 0
val minGroupSize = groupSizes.minOrNull() ?: 0
return mapOf(
"groupCount" to result.groupCount,
"totalItems" to result.totalItems,
"averageGroupSize" to String.format("%.2f", result.averageGroupSize),
"maxGroupSize" to maxGroupSize,
"minGroupSize" to minGroupSize,
"groupingTime" to "${result.groupingTime}ms"
)
}
/**
* 获取分组摘要
* @param result 分组结果
* @return 摘要字符串
*/
fun getGroupingSummary(result: GroupResult): String {
val summary = StringBuilder()
summary.append("分组摘要\n")
summary.append("=".repeat(40)).append("\n")
for ((key, items) in result.groups) {
summary.append("$key: ${items.size} 项 - $items\n")
}
return summary.toString()
}
/**
* 生成分组报告
* @param result 分组结果
* @return 报告字符串
*/
fun generateGroupingReport(result: GroupResult): String {
val stats = getGroupingStatistics(result)
val report = StringBuilder()
report.append("数据分组报告\n")
report.append("=".repeat(40)).append("\n")
report.append("分组数: ${stats["groupCount"]}\n")
report.append("总项数: ${stats["totalItems"]}\n")
report.append("平均分组大小: ${stats["averageGroupSize"]}\n")
report.append("最大分组大小: ${stats["maxGroupSize"]}\n")
report.append("最小分组大小: ${stats["minGroupSize"]}\n")
report.append("分组耗时: ${stats["groupingTime"]}\n")
return report.toString()
}
}
Kotlin 实现的核心特点
Kotlin 实现中的分组功能充分利用了 Kotlin 标准库的集合处理能力。按值分组使用了 Map 和 getOrPut。按范围分组使用了数学计算。
按条件分组使用了自定义谓词函数。统计信息使用了 maxOrNull 和 minOrNull 方法。报告生成使用了字符串构建器。
JavaScript 实现
编译后的 JavaScript 代码
// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)
/**
* DataGrouper 类的 JavaScript 版本
* 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
*/
class DataGrouper {
constructor() {
this.config = {
enableStatistics: true,
enableLogging: false,
sortGroups: false
};
}
/**
* 设置分组配置
* @param {Object} config - 配置对象
*/
setConfig(config) {
this.config = { ...this.config, ...config };
}
/**
* 按值分组
* @param {Array} data - 数据列表
* @returns {Object} 分组结果
*/
groupByValue(data) {
const startTime = Date.now();
const groups = {};
for (const item of data) {
const key = String(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
}
const groupingTime = Date.now() - startTime;
const groupCount = Object.keys(groups).length;
const averageGroupSize = groupCount > 0 ? data.length / groupCount : 0;
return {
groups: groups,
groupCount: groupCount,
totalItems: data.length,
groupingTime: groupingTime,
averageGroupSize: averageGroupSize
};
}
/**
* 按范围分组
* @param {Array} data - 数据列表
* @param {number} rangeSize - 范围大小
* @returns {Object} 分组结果
*/
groupByRange(data, rangeSize = 10) {
const startTime = Date.now();
const groups = {};
for (const item of data) {
const rangeStart = Math.floor(item / rangeSize) * rangeSize;
const rangeEnd = rangeStart + rangeSize - 1;
const key = `${rangeStart}-${rangeEnd}`;
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
}
const groupingTime = Date.now() - startTime;
const groupCount = Object.keys(groups).length;
const averageGroupSize = groupCount > 0 ? data.length / groupCount : 0;
return {
groups: groups,
groupCount: groupCount,
totalItems: data.length,
groupingTime: groupingTime,
averageGroupSize: averageGroupSize
};
}
/**
* 按条件分组
* @param {Array} data - 数据列表
* @param {Function} predicate - 条件函数
* @returns {Object} 分组结果
*/
groupByCondition(data, predicate) {
const startTime = Date.now();
const groups = {};
for (const item of data) {
const key = predicate(item);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(item);
}
const groupingTime = Date.now() - startTime;
const groupCount = Object.keys(groups).length;
const averageGroupSize = groupCount > 0 ? data.length / groupCount : 0;
return {
groups: groups,
groupCount: groupCount,
totalItems: data.length,
groupingTime: groupingTime,
averageGroupSize: averageGroupSize
};
}
/**
* 按奇偶分组
* @param {Array} data - 数据列表
* @returns {Object} 分组结果
*/
groupByOddEven(data) {
return this.groupByCondition(data, item => item % 2 === 0 ? '偶数' : '奇数');
}
/**
* 获取分组统计信息
* @param {Object} result - 分组结果
* @returns {Object} 统计信息
*/
getGroupingStatistics(result) {
const groupSizes = Object.values(result.groups).map(g => g.length);
const maxGroupSize = Math.max(...groupSizes);
const minGroupSize = Math.min(...groupSizes);
return {
groupCount: result.groupCount,
totalItems: result.totalItems,
averageGroupSize: result.averageGroupSize.toFixed(2),
maxGroupSize: maxGroupSize,
minGroupSize: minGroupSize,
groupingTime: `${result.groupingTime}ms`
};
}
/**
* 获取分组摘要
* @param {Object} result - 分组结果
* @returns {string} 摘要字符串
*/
getGroupingSummary(result) {
let summary = '分组摘要\n';
summary += '='.repeat(40) + '\n';
for (const [key, items] of Object.entries(result.groups)) {
summary += `${key}: ${items.length} 项 - ${items}\n`;
}
return summary;
}
}
JavaScript 实现的特点
JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的对象提供了分组存储能力。
Object.keys() 用于获取分组键。Math.floor() 用于范围计算。Object.entries() 用于遍历分组。
ArkTS 调用代码
OpenHarmony 应用集成
// 文件: kmp_ceshiapp/entry/src/main/ets/pages/DataGrouperPage.ets
import { DataGrouper } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';
@Entry
@Component
struct DataGrouperPage {
@State selectedGrouping: string = 'byvalue';
@State inputData: string = '';
@State result: string = '';
@State resultTitle: string = '';
private dataGrouper = new DataGrouper();
private groupings = [
{ name: '🔢 按值分组', value: 'byvalue' },
{ name: '📊 按范围分组', value: 'byrange' },
{ name: '🔀 按奇偶分组', value: 'byoddeven' },
{ name: '⚖️ 按大小分组', value: 'bysize' },
{ name: '📈 统计', value: 'stats' },
{ name: '📋 摘要', value: 'summary' },
{ name: '📄 报告', value: 'report' },
{ name: '🔄 演示', value: 'demo' }
];
build() {
Column() {
// 标题
Text('📊 数据分组库示例')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(20)
.backgroundColor('#1A237E')
.textAlign(TextAlign.Center)
Scroll() {
Column() {
// 分组方式选择
Column() {
Text('选择分组方式')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 12 })
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.groupings, (group: { name: string; value: string }) => {
Button(group.name)
.layoutWeight(1)
.height(40)
.margin({ right: 8, bottom: 8 })
.backgroundColor(this.selectedGrouping === group.value ? '#1A237E' : '#E0E0E0')
.fontColor(this.selectedGrouping === group.value ? '#FFFFFF' : '#333333')
.fontSize(11)
.onClick(() => {
this.selectedGrouping = group.value;
this.result = '';
this.resultTitle = '';
})
})
}
.width('100%')
}
.width('95%')
.margin({ top: 16, left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// 数据输入
Column() {
Text('输入数据(用逗号分隔)')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor('#333333')
.margin({ bottom: 8 })
TextInput({ placeholder: '例如:1,2,3,4,5,6,7,8,9,10', text: this.inputData })
.onChange((value) => this.inputData = value)
.width('100%')
.height(50)
.padding(12)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.fontSize(12)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
// 操作按钮
Row() {
Button('✨ 分组')
.layoutWeight(1)
.height(44)
.backgroundColor('#1A237E')
.fontColor('#FFFFFF')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.borderRadius(6)
.onClick(() => this.executeGrouping())
Blank()
.width(12)
Button('🔄 清空')
.layoutWeight(1)
.height(44)
.backgroundColor('#F5F5F5')
.fontColor('#1A237E')
.fontSize(14)
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
.onClick(() => {
this.inputData = '';
this.result = '';
this.resultTitle = '';
})
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
// 结果显示
if (this.resultTitle) {
Column() {
Text(this.resultTitle)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
.width('100%')
.padding(12)
.backgroundColor('#1A237E')
.borderRadius(6)
.textAlign(TextAlign.Center)
.margin({ bottom: 12 })
Scroll() {
Text(this.result)
.fontSize(12)
.fontColor('#333333')
.fontFamily('monospace')
.textAlign(TextAlign.Start)
.width('100%')
.padding(12)
.selectable(true)
}
.width('100%')
.height(300)
.backgroundColor('#F9F9F9')
.border({ width: 1, color: '#4DB6AC' })
.borderRadius(6)
}
.width('95%')
.margin({ left: '2.5%', right: '2.5%', bottom: 16 })
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(6)
}
}
.width('100%')
}
.layoutWeight(1)
.width('100%')
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
private executeGrouping() {
const inputStr = this.inputData || '1,2,3,4,5,6,7,8,9,10';
const data = inputStr.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n));
if (data.length === 0) {
this.resultTitle = '❌ 错误';
this.result = '请输入有效的数据';
return;
}
try {
switch (this.selectedGrouping) {
case 'byvalue':
const valueResult = this.dataGrouper.groupByValue(data);
this.resultTitle = '🔢 按值分组';
let valueStr = `原始数据: ${data}\n分组数: ${valueResult.groupCount}\n`;
for (const [key, items] of Object.entries(valueResult.groups)) {
valueStr += `${key}: ${items}\n`;
}
this.result = valueStr;
break;
case 'byrange':
const rangeResult = this.dataGrouper.groupByRange(data, 5);
this.resultTitle = '📊 按范围分组';
let rangeStr = `原始数据: ${data}\n分组数: ${rangeResult.groupCount}\n`;
for (const [key, items] of Object.entries(rangeResult.groups)) {
rangeStr += `${key}: ${items}\n`;
}
this.result = rangeStr;
break;
case 'byoddeven':
const oddEvenResult = this.dataGrouper.groupByOddEven(data);
this.resultTitle = '🔀 按奇偶分组';
let oddEvenStr = `原始数据: ${data}\n`;
for (const [key, items] of Object.entries(oddEvenResult.groups)) {
oddEvenStr += `${key}: ${items}\n`;
}
this.result = oddEvenStr;
break;
case 'bysize':
const sizeResult = this.dataGrouper.groupByCondition(data, item => item >= 5 ? '≥5' : '<5');
this.resultTitle = '⚖️ 按大小分组';
let sizeStr = `原始数据: ${data}\n`;
for (const [key, items] of Object.entries(sizeResult.groups)) {
sizeStr += `${key}: ${items}\n`;
}
this.result = sizeStr;
break;
case 'stats':
const statsResult = this.dataGrouper.groupByValue(data);
const stats = this.dataGrouper.getGroupingStatistics(statsResult);
this.resultTitle = '📈 分组统计';
this.result = `分组数: ${stats.groupCount}\n总项数: ${stats.totalItems}\n平均分组大小: ${stats.averageGroupSize}\n最大分组大小: ${stats.maxGroupSize}\n最小分组大小: ${stats.minGroupSize}\n耗时: ${stats.groupingTime}`;
break;
case 'summary':
const summaryResult = this.dataGrouper.groupByValue(data);
const summary = this.dataGrouper.getGroupingSummary(summaryResult);
this.resultTitle = '📋 分组摘要';
this.result = summary;
break;
case 'report':
const reportResult = this.dataGrouper.groupByValue(data);
const report = this.dataGrouper.generateGroupingReport(reportResult);
this.resultTitle = '📄 分组报告';
this.result = report;
break;
case 'demo':
const demoData = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const demoResult = this.dataGrouper.groupByOddEven(demoData);
this.resultTitle = '🔄 演示数据';
let demoStr = `演示数据: ${demoData}\n分组结果:\n`;
for (const [key, items] of Object.entries(demoResult.groups)) {
demoStr += `${key}: ${items}\n`;
}
this.result = demoStr;
break;
}
} catch (e) {
this.resultTitle = '❌ 分组出错';
this.result = `错误: ${e}`;
}
}
}
ArkTS 集成的关键要点
在 OpenHarmony 应用中集成分组工具库需要考虑多种分组方式和用户体验。我们设计了一个灵活的 UI,能够支持不同的分组操作。
分组方式选择界面使用了 Flex 布局和 FlexWrap 来实现响应式的按钮排列。数据输入使用了 TextInput 组件。
结果显示使用了可选择的文本,这样用户可以轻松复制分组结果。对于不同的分组方式,我们显示了相应的分组处理结果。
工作流程详解
数据分组的完整流程
- 分组方式选择: 用户在 ArkTS UI 中选择要使用的分组方式
- 数据输入: 用户输入要分组的数据
- 处理执行: 调用 DataGrouper 的相应方法
- 结果展示: 将分组结果显示在 UI 中
跨平台一致性
通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,数据分组的逻辑和结果都是完全相同的。
实际应用场景
报表生成
在生成报表时,需要对数据进行分组以便统计和展示。这个工具库提供了完整的报表分组功能。
数据分析
在进行数据分析时,需要对数据进行多维度分组。这个工具库提供了数据分析分组能力。
列表展示
在展示列表数据时,需要按分类进行分组展示。这个工具库提供了列表分组功能。
统计汇总
在进行统计汇总时,需要对数据进行分组聚合。这个工具库提供了统计分组能力。
性能优化
分组算法优化
在处理大量数据时,应该选择高效的分组算法以提高性能。
缓存分组结果
在频繁进行相同分组时,可以缓存分组结果以避免重复分组。
安全性考虑
数据验证
在进行数据分组时,应该进行验证以确保数据的有效性。
分组验证
在分组完成后,应该进行验证以确保分组的正确性。
总结
这个 KMP OpenHarmony 数据分组库示例展示了如何使用现代的跨平台技术来处理常见的数据分组任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。
数据分组是应用开发中的重要功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种分组操作,从而提高应用的数据处理能力。
在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的分组策略、实现更复杂的分组规则等高级特性。同时,定期进行性能测试和优化,确保应用的分组系统保持高效运行。
更多推荐



所有评论(0)