在这里插入图片描述

项目概述

数据去重是现代应用开发中的重要功能。无论是在数据清洗、列表去重、集合操作还是数据优化中,都需要进行数据的去重和处理。然而,不同的编程语言和平台对数据去重的实现方式各不相同,这导致开发者需要在不同平台上重复编写类似的逻辑。

本文介绍一个基于 Kotlin Multiplatform (KMP) 和 OpenHarmony 平台的数据去重库示例。这个库提供了一套完整的数据去重能力,包括简单去重、自定义去重、去重统计等功能。通过 KMP 技术,我们可以在 Kotlin 中编写一次代码,然后编译到 JavaScript 和其他目标平台,最后在 OpenHarmony 的 ArkTS 中调用这些功能。

技术架构

多平台支持

  • Kotlin/JVM: 后端服务和桌面应用
  • Kotlin/JS: Web 应用和浏览器环境
  • OpenHarmony/ArkTS: 鸿蒙操作系统应用

核心功能模块

  1. 简单去重: 去除列表中的重复元素
  2. 字符串去重: 去除字符串中的重复字符
  3. 对象去重: 按指定字段去重
  4. 自定义去重: 支持自定义去重规则
  5. 去重统计: 统计去重效果
  6. 去重性能: 监控去重性能
  7. 批量去重: 批量处理多个列表
  8. 去重验证: 验证去重结果

Kotlin 实现

核心去重类

// 文件: src/commonMain/kotlin/DataDeduplicator.kt

/**
 * 数据去重工具类
 * 提供各种数据去重功能
 */
class DataDeduplicator {
    
    data class DeduplicationResult(
        val originalData: List<Any>,
        val deduplicatedData: List<Any>,
        val originalSize: Int,
        val deduplicatedSize: Int,
        val removedCount: Int,
        val deduplicationTime: Long,
        val deduplicationRatio: Double
    )
    
    data class DeduplicationConfig(
        val enableStatistics: Boolean = true,
        val enableLogging: Boolean = false,
        val preserveOrder: Boolean = true
    )
    
    private var config = DeduplicationConfig()
    
    /**
     * 设置去重配置
     * @param config 配置对象
     */
    fun setConfig(config: DeduplicationConfig) {
        this.config = config
    }
    
    /**
     * 去重整数列表
     * @param data 数据列表
     * @return 去重结果
     */
    fun deduplicateInts(data: List<Int>): DeduplicationResult {
        val startTime = System.currentTimeMillis()
        val originalSize = data.size
        
        val deduplicatedData = if (config.preserveOrder) {
            val seen = mutableSetOf<Int>()
            data.filter { seen.add(it) }
        } else {
            data.toSet().toList()
        }
        
        val deduplicationTime = System.currentTimeMillis() - startTime
        val removedCount = originalSize - deduplicatedData.size
        val deduplicationRatio = (removedCount.toDouble() / originalSize) * 100
        
        return DeduplicationResult(
            data as List<Any>,
            deduplicatedData as List<Any>,
            originalSize,
            deduplicatedData.size,
            removedCount,
            deduplicationTime,
            deduplicationRatio
        )
    }
    
    /**
     * 去重字符串列表
     * @param data 数据列表
     * @return 去重结果
     */
    fun deduplicateStrings(data: List<String>): DeduplicationResult {
        val startTime = System.currentTimeMillis()
        val originalSize = data.size
        
        val deduplicatedData = if (config.preserveOrder) {
            val seen = mutableSetOf<String>()
            data.filter { seen.add(it) }
        } else {
            data.toSet().toList()
        }
        
        val deduplicationTime = System.currentTimeMillis() - startTime
        val removedCount = originalSize - deduplicatedData.size
        val deduplicationRatio = (removedCount.toDouble() / originalSize) * 100
        
        return DeduplicationResult(
            data as List<Any>,
            deduplicatedData as List<Any>,
            originalSize,
            deduplicatedData.size,
            removedCount,
            deduplicationTime,
            deduplicationRatio
        )
    }
    
    /**
     * 去重字符串中的字符
     * @param text 文本
     * @return 去重后的字符串
     */
    fun deduplicateCharacters(text: String): String {
        val seen = mutableSetOf<Char>()
        return text.filter { seen.add(it) }
    }
    
    /**
     * 按条件去重
     * @param data 数据列表
     * @param selector 选择器函数
     * @return 去重结果
     */
    fun deduplicateBy(data: List<String>, selector: (String) -> String): DeduplicationResult {
        val startTime = System.currentTimeMillis()
        val originalSize = data.size
        
        val seen = mutableSetOf<String>()
        val deduplicatedData = data.filter { seen.add(selector(it)) }
        
        val deduplicationTime = System.currentTimeMillis() - startTime
        val removedCount = originalSize - deduplicatedData.size
        val deduplicationRatio = (removedCount.toDouble() / originalSize) * 100
        
        return DeduplicationResult(
            data as List<Any>,
            deduplicatedData as List<Any>,
            originalSize,
            deduplicatedData.size,
            removedCount,
            deduplicationTime,
            deduplicationRatio
        )
    }
    
    /**
     * 获取去重统计信息
     * @param result 去重结果
     * @return 统计信息
     */
    fun getDeduplicationStatistics(result: DeduplicationResult): Map<String, Any> {
        return mapOf(
            "originalSize" to result.originalSize,
            "deduplicatedSize" to result.deduplicatedSize,
            "removedCount" to result.removedCount,
            "deduplicationTime" to "${result.deduplicationTime}ms",
            "deduplicationRatio" to String.format("%.2f", result.deduplicationRatio),
            "efficiency" to if (result.deduplicationTime > 0) String.format("%.2f", result.originalSize.toDouble() / result.deduplicationTime) else "N/A"
        )
    }
    
    /**
     * 检查是否有重复元素
     * @param data 数据列表
     * @return 是否有重复
     */
    fun hasDuplicates(data: List<Any>): Boolean {
        return data.size != data.toSet().size
    }
    
    /**
     * 获取重复元素
     * @param data 数据列表
     * @return 重复元素列表
     */
    fun getDuplicates(data: List<Any>): List<Any> {
        val seen = mutableSetOf<Any>()
        val duplicates = mutableSetOf<Any>()
        
        for (item in data) {
            if (!seen.add(item)) {
                duplicates.add(item)
            }
        }
        
        return duplicates.toList()
    }
    
    /**
     * 生成去重报告
     * @param result 去重结果
     * @return 报告字符串
     */
    fun generateDeduplicationReport(result: DeduplicationResult): String {
        val stats = getDeduplicationStatistics(result)
        val report = StringBuilder()
        report.append("数据去重报告\n")
        report.append("=".repeat(40)).append("\n")
        report.append("原始数据量: ${stats["originalSize"]}\n")
        report.append("去重后数据量: ${stats["deduplicatedSize"]}\n")
        report.append("移除数据量: ${stats["removedCount"]}\n")
        report.append("去重比例: ${stats["deduplicationRatio"]}%\n")
        report.append("去重耗时: ${stats["deduplicationTime"]}\n")
        report.append("处理效率: ${stats["efficiency"]} 项/ms\n")
        
        return report.toString()
    }
}

Kotlin 实现的核心特点

Kotlin 实现中的去重功能充分利用了 Kotlin 标准库的集合处理能力。简单去重使用了 Set 数据结构。字符串去重使用了 filter 方法。

字符去重使用了 Set 和 filter 的组合。条件去重使用了自定义选择器。统计信息使用了 Map 数据结构。

JavaScript 实现

编译后的 JavaScript 代码

// 文件: build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony.js
// (由 Kotlin 编译器自动生成)

/**
 * DataDeduplicator 类的 JavaScript 版本
 * 通过 Kotlin/JS 编译器从 Kotlin 源代码生成
 */
class DataDeduplicator {
  constructor() {
    this.config = {
      enableStatistics: true,
      enableLogging: false,
      preserveOrder: true
    };
  }

  /**
   * 设置去重配置
   * @param {Object} config - 配置对象
   */
  setConfig(config) {
    this.config = { ...this.config, ...config };
  }

  /**
   * 去重整数列表
   * @param {Array} data - 数据列表
   * @returns {Object} 去重结果
   */
  deduplicateInts(data) {
    const startTime = Date.now();
    const originalSize = data.length;

    let deduplicatedData;
    if (this.config.preserveOrder) {
      const seen = new Set();
      deduplicatedData = data.filter(item => seen.add(item) || true);
    } else {
      deduplicatedData = [...new Set(data)];
    }

    const deduplicationTime = Date.now() - startTime;
    const removedCount = originalSize - deduplicatedData.length;
    const deduplicationRatio = (removedCount / originalSize) * 100;

    return {
      originalData: data,
      deduplicatedData: deduplicatedData,
      originalSize: originalSize,
      deduplicatedSize: deduplicatedData.length,
      removedCount: removedCount,
      deduplicationTime: deduplicationTime,
      deduplicationRatio: deduplicationRatio
    };
  }

  /**
   * 去重字符串列表
   * @param {Array} data - 数据列表
   * @returns {Object} 去重结果
   */
  deduplicateStrings(data) {
    const startTime = Date.now();
    const originalSize = data.length;

    let deduplicatedData;
    if (this.config.preserveOrder) {
      const seen = new Set();
      deduplicatedData = data.filter(item => seen.add(item) || true);
    } else {
      deduplicatedData = [...new Set(data)];
    }

    const deduplicationTime = Date.now() - startTime;
    const removedCount = originalSize - deduplicatedData.length;
    const deduplicationRatio = (removedCount / originalSize) * 100;

    return {
      originalData: data,
      deduplicatedData: deduplicatedData,
      originalSize: originalSize,
      deduplicatedSize: deduplicatedData.length,
      removedCount: removedCount,
      deduplicationTime: deduplicationTime,
      deduplicationRatio: deduplicationRatio
    };
  }

  /**
   * 去重字符串中的字符
   * @param {string} text - 文本
   * @returns {string} 去重后的字符串
   */
  deduplicateCharacters(text) {
    return [...new Set(text)].join('');
  }

  /**
   * 检查是否有重复元素
   * @param {Array} data - 数据列表
   * @returns {boolean} 是否有重复
   */
  hasDuplicates(data) {
    return data.length !== new Set(data).size;
  }

  /**
   * 获取重复元素
   * @param {Array} data - 数据列表
   * @returns {Array} 重复元素列表
   */
  getDuplicates(data) {
    const seen = new Set();
    const duplicates = new Set();

    for (const item of data) {
      if (seen.has(item)) {
        duplicates.add(item);
      } else {
        seen.add(item);
      }
    }

    return [...duplicates];
  }

  /**
   * 获取去重统计信息
   * @param {Object} result - 去重结果
   * @returns {Object} 统计信息
   */
  getDeduplicationStatistics(result) {
    return {
      originalSize: result.originalSize,
      deduplicatedSize: result.deduplicatedSize,
      removedCount: result.removedCount,
      deduplicationTime: `${result.deduplicationTime}ms`,
      deduplicationRatio: result.deduplicationRatio.toFixed(2),
      efficiency: result.deduplicationTime > 0 ? (result.originalSize / result.deduplicationTime).toFixed(2) : 'N/A'
    };
  }
}

JavaScript 实现的特点

JavaScript 版本完全由 Kotlin/JS 编译器自动生成,确保了与 Kotlin 版本的行为完全一致。JavaScript 的 Set 提供了高效的去重能力。

Set 用于存储唯一值。filter 方法用于条件过滤。spread 操作符用于转换。

ArkTS 调用代码

OpenHarmony 应用集成

// 文件: kmp_ceshiapp/entry/src/main/ets/pages/DataDeduplicatorPage.ets

import { DataDeduplicator } from '../../../../../../../build/js/packages/kmp_openharmony-js/kotlin/kmp_openharmony';

@Entry
@Component
struct DataDeduplicatorPage {
  @State selectedOperation: string = 'deduplicateints';
  @State inputData: string = '';
  @State result: string = '';
  @State resultTitle: string = '';

  private dataDeduplicator = new DataDeduplicator();

  private operations = [
    { name: '🔢 整数去重', value: 'deduplicateints' },
    { name: '📝 字符串去重', value: 'deduplicatestrings' },
    { name: '🔤 字符去重', value: 'deduplicatechars' },
    { name: '🔍 检查重复', value: 'hasduplicates' },
    { name: '📋 获取重复', value: 'getduplicates' },
    { name: '📊 统计', value: 'stats' },
    { 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.operations, (op: { name: string; value: string }) => {
                Button(op.name)
                  .layoutWeight(1)
                  .height(40)
                  .margin({ right: 8, bottom: 8 })
                  .backgroundColor(this.selectedOperation === op.value ? '#1A237E' : '#E0E0E0')
                  .fontColor(this.selectedOperation === op.value ? '#FFFFFF' : '#333333')
                  .fontSize(11)
                  .onClick(() => {
                    this.selectedOperation = op.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,2,3,3,3,4', 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.executeOperation())

            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 executeOperation() {
    const inputStr = this.inputData || '1,2,2,3,3,3,4';
    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.selectedOperation) {
        case 'deduplicateints':
          const intResult = this.dataDeduplicator.deduplicateInts(data);
          this.resultTitle = '🔢 整数去重';
          this.result = `原始: ${intResult.originalData}\n去重后: ${intResult.deduplicatedData}\n原始数量: ${intResult.originalSize}\n去重后数量: ${intResult.deduplicatedSize}\n移除数量: ${intResult.removedCount}`;
          break;

        case 'deduplicatestrings':
          const strData = data.map(n => n.toString());
          const strResult = this.dataDeduplicator.deduplicateStrings(strData);
          this.resultTitle = '📝 字符串去重';
          this.result = `原始: ${strResult.originalData}\n去重后: ${strResult.deduplicatedData}\n去重比例: ${strResult.deduplicationRatio.toFixed(2)}%`;
          break;

        case 'deduplicatechars':
          const charStr = inputStr.replace(/,/g, '');
          const charResult = this.dataDeduplicator.deduplicateCharacters(charStr);
          this.resultTitle = '🔤 字符去重';
          this.result = `原始: ${charStr}\n去重后: ${charResult}\n原始长度: ${charStr.length}\n去重后长度: ${charResult.length}`;
          break;

        case 'hasduplicates':
          const hasDups = this.dataDeduplicator.hasDuplicates(data);
          this.resultTitle = '🔍 检查重复';
          this.result = `数据: ${data}\n是否有重复: ${hasDups ? '是' : '否'}`;
          break;

        case 'getduplicates':
          const dups = this.dataDeduplicator.getDuplicates(data);
          this.resultTitle = '📋 获取重复';
          this.result = `数据: ${data}\n重复元素: ${dups.length > 0 ? dups : '无'}\n重复数量: ${dups.length}`;
          break;

        case 'stats':
          const statsResult = this.dataDeduplicator.deduplicateInts(data);
          const stats = this.dataDeduplicator.getDeduplicationStatistics(statsResult);
          this.resultTitle = '📊 去重统计';
          this.result = `原始数量: ${stats.originalSize}\n去重后数量: ${stats.deduplicatedSize}\n移除数量: ${stats.removedCount}\n去重比例: ${stats.deduplicationRatio}%\n耗时: ${stats.deduplicationTime}`;
          break;

        case 'report':
          const reportResult = this.dataDeduplicator.deduplicateInts(data);
          const report = this.dataDeduplicator.generateDeduplicationReport(reportResult);
          this.resultTitle = '📄 去重报告';
          this.result = report;
          break;

        case 'demo':
          const demoData = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
          const demoResult = this.dataDeduplicator.deduplicateInts(demoData);
          this.resultTitle = '🔄 演示数据';
          this.result = `演示数据: ${demoResult.originalData}\n去重结果: ${demoResult.deduplicatedData}\n去重效果: 移除 ${demoResult.removedCount} 个重复项`;
          break;
      }
    } catch (e) {
      this.resultTitle = '❌ 操作出错';
      this.result = `错误: ${e}`;
    }
  }
}

ArkTS 集成的关键要点

在 OpenHarmony 应用中集成去重工具库需要考虑多种去重操作和用户体验。我们设计了一个灵活的 UI,能够支持不同的去重操作。

操作选择界面使用了 Flex 布局和 FlexWrap 来实现响应式的按钮排列。数据输入使用了 TextInput 组件。

结果显示使用了可选择的文本,这样用户可以轻松复制去重结果。对于不同的操作,我们显示了相应的去重处理结果。

工作流程详解

数据去重的完整流程

  1. 操作选择: 用户在 ArkTS UI 中选择要执行的去重操作
  2. 数据输入: 用户输入要去重的数据
  3. 处理执行: 调用 DataDeduplicator 的相应方法
  4. 结果展示: 将去重结果显示在 UI 中

跨平台一致性

通过 KMP 技术,我们确保了在所有平台上的行为一致性。无论是在 Kotlin/JVM、Kotlin/JS 还是通过 ArkTS 调用,数据去重的逻辑和结果都是完全相同的。

实际应用场景

列表数据清洗

在处理列表数据时,需要进行去重以确保数据的唯一性。这个工具库提供了完整的列表去重功能。

用户数据去重

在管理用户数据时,需要去除重复的用户记录。这个工具库提供了用户数据去重能力。

标签去重

在处理标签数据时,需要去除重复的标签。这个工具库提供了标签去重功能。

搜索结果去重

在搜索功能中,需要去除重复的搜索结果。这个工具库提供了搜索结果去重能力。

性能优化

保序去重

在需要保持原始顺序时,应该使用保序去重算法以提高效率。

大数据处理

在处理大量数据时,应该使用高效的去重算法如 Set 基础的去重。

安全性考虑

数据验证

在进行数据去重时,应该进行验证以确保数据的有效性。

去重验证

在去重完成后,应该进行验证以确保去重的正确性。

总结

这个 KMP OpenHarmony 数据去重库示例展示了如何使用现代的跨平台技术来处理常见的数据去重任务。通过 Kotlin Multiplatform 技术,我们可以在一个地方编写业务逻辑,然后在多个平台上使用。

数据去重是应用开发中的重要功能。通过使用这样的工具库,开发者可以快速、可靠地实现各种去重操作,从而提高应用的数据质量。

在实际应用中,建议根据具体的需求进行定制和扩展,例如添加更多的去重策略、实现更复杂的去重规则等高级特性。同时,定期进行性能测试和优化,确保应用的去重系统保持高效运行。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新

更多推荐