鸿蒙开发中集合与数组的解析与实战

本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

在鸿蒙(HarmonyOS)应用开发中,集合与数组容器是处理数据结构的核心工具,它们提供了高效的数据存储和操作方法。本文将全面介绍鸿蒙开发中的各类容器,包括线性容器、非线性容器以及原生数组API的使用方法。

一、容器类概述与分类

鸿蒙开发框架提供了丰富的容器类API,主要分为线性容器非线性容器两大类,共14种容器类型,每种容器都有其特定的使用场景和性能特点。

1.1 线性容器类

线性容器底层主要通过数组或链表实现,包括:

  • 基于数组实现:ArrayList、Vector、Queue、Deque、Stack
  • 基于链表实现:List、LinkedList

1.2 非线性容器类

非线性容器底层通过哈希表或红黑树实现,包括:

  • 基于哈希表实现:HashMap、HashSet、LightWeightMap、LightWeightSet、PlainArray
  • 基于红黑树实现:TreeMap、TreeSet

1.3 原生数组API

除了专门的容器类,鸿蒙也支持标准的JavaScript/TypeScript数组操作API,如push、pop、slice等方法。

二、线性容器详解

2.1 ArrayList(动态数组)

ArrayList是最常用的线性容器,基于动态数组实现,支持快速随机访问。

核心特性

  • 初始容量为10,动态扩容时每次增加原始容量的1.5倍
  • 内存空间连续,访问效率高(O(1))
  • 插入删除操作可能引起元素移动(O(n)) 

常用API

import ArrayList from '@ohos.util.ArrayList';

// 创建ArrayList
let arrayList = new ArrayList<string>();

// 添加元素
arrayList.add("a");  // 尾部添加
arrayList.insert("b", 0);  // 指定位置插入

// 查询元素
let hasElement = arrayList.has("a");  // 判断是否存在
let index = arrayList.getIndexOf("a");  // 获取首次出现位置
let element = arrayList.get(0);  // 根据索引获取

// 删除元素
let removed = arrayList.removeByIndex(0);  // 根据索引删除
let cleared = arrayList.clear();  // 清空

示例场景:需要频繁读取元素且数据量变化不大的场景,如配置项管理、静态数据缓存等。

2.2 Vector(向量)

Vector与ArrayList类似,但扩容策略不同,每次扩容为原始容量的2倍。

与ArrayList的区别

  • 扩容更快(2倍 vs 1.5倍),适合数据添加频繁的场景
  • 提供更完善的校验和容错机制
  • 线程安全(部分操作)
import Vector from '@ohos.util.Vector';

let vector = new Vector<number>();
vector.add(1);
vector.insert(2, 0);
let element = vector.get(0);

2.3 List(单向链表)

List基于单向链表实现,适合频繁插入删除但随机访问较少的场景。

核心特性

  • 内存空间可以不连续
  • 插入删除效率高(O(1))
  • 随机访问效率低(O(n))
  • 允许元素为null
import { List } from '@kit.ArkTS';

let list = new List<string>();
list.add("first");
list.insert("second", 1);
let hasElement = list.has("first");
let element = list.get(1);
let removed = list.remove("second");

注意事项

  • 避免使用[index]方式直接访问,推荐使用get()方法 
  • 查询操作需要从头遍历,效率较低

2.4 LinkedList(双向链表)

LinkedList是List的双向版本,可以从任意节点向前或向后遍历。

与List的区别

  • 可以双向遍历
  • 头尾操作效率更高
  • 内存占用略高(需要存储前后指针)
import LinkedList from '@ohos.util.LinkedList';

let linkedList = new LinkedList<number>();
linkedList.add(1);
linkedList.addFirst(0);  // 头部添加
linkedList.addLast(2);   // 尾部添加
let first = linkedList.getFirst();
let last = linkedList.getLast();

2.5 Queue(队列)与Deque(双端队列)

Queue是先进先出(FIFO)的队列,Deque支持两端操作

import Queue from '@ohos.util.Queue';
import Deque from '@ohos.util.Deque';

// Queue示例
let queue = new Queue<string>();
queue.add("task1");  // 入队
let task = queue.poll();  // 出队

// Deque示例
let deque = new Deque<number>();
deque.addFirst(1);  // 前端入队
deque.addLast(2);   // 后端入队
let first = deque.popFirst();  // 前端出队
let last = deque.popLast();    // 后端出队

2.6 Stack(栈)

Stack实现后进先出(LIFO)的数据结构 

import Stack from '@ohos.util.Stack';

let stack = new Stack<string>();
stack.push("page1");  // 入栈
stack.push("page2");
let page = stack.pop();  // 出栈("page2")
let top = stack.peek();  // 查看栈顶("page1")

应用场景:页面导航、撤销操作、表达式求值等。

三、非线性容器详解

3.1 HashMap(哈希映射)

HashMap存储键值对,基于哈希表实现,查找效率高(O(1))。

核心特性

  • 初始容量16,扩容为2倍
  • 键唯一,允许null键值
  • 冲突解决:链地址法
import HashMap from '@ohos.util.HashMap';

let map = new HashMap<string, number>();
map.set("apple", 5);
map.set("banana", 3);
let count = map.get("apple");  // 5
let hasKey = map.hasKey("banana");
let removed = map.remove("apple");
let keys = map.keys();  // 获取所有键
let values = map.values();  // 获取所有值

注意事项

  • 键对象应正确实现hashCode和equals方法
  • 遍历顺序不确定

3.2 HashSet(哈希集合)

HashSet基于HashMap实现,存储唯一值

import { HashSet } from '@kit.ArkTS';

let set = new HashSet<string>();
set.add("a");
set.add("b");
let hasElement = set.has("a");
let removed = set.remove("b");
let size = set.size;
set.clear();

应用场景:去重、集合运算、存在性检查等。

3.3 TreeMap(树映射)与TreeSet(树集合)

基于红黑树实现,元素保持有序

import TreeMap from '@ohos.util.TreeMap';
import TreeSet from '@ohos.util.TreeSet';

// TreeMap示例
let treeMap = new TreeMap<string, number>();
treeMap.set("a", 1);
treeMap.set("b", 2);
let firstKey = treeMap.getFirstKey();  // "a"
let lastValue = treeMap.getLastValue();  // 2

// TreeSet示例
let treeSet = new TreeSet<number>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
let first = treeSet.getFirst();  // 1

特点

  • 元素自动排序(基于自然顺序或Comparator)
  • 查询效率O(log n)
  • 不支持null键(可能影响排序)

3.4 LightWeightMap与LightWeightSet

轻量级容器,内存占用更少

import LightWeightMap from '@ohos.util.LightWeightMap';
import LightWeightSet from '@ohos.util.LightWeightSet';

// LightWeightMap示例
let lwMap = new LightWeightMap();
lwMap.set("key", "value");
let value = lwMap.get("key");

// LightWeightSet示例
let lwSet = new LightWeightSet();
lwSet.add("item");
let has = lwSet.has("item");

适用场景:内存敏感的环境,存储简单数据类型。

四、原生数组API详解

鸿蒙开发中也可以使用标准的JavaScript/TypeScript数组操作。

4.1 基本操作

// 创建数组
let arr: number[] = [1, 2, 3];
let arr2: Array<string> = new Array("a", "b");

// 访问元素
let first = arr[0];  // 1
arr[1] = 4;  // [1, 4, 3]

// 长度属性
let len = arr.length;  // 3

4.2 常用方法

添加/删除元素
// 尾部操作
arr.push(5);  // [1, 4, 3, 5]
let last = arr.pop();  // 5, [1, 4, 3]

// 头部操作
arr.unshift(0);  // [0, 1, 4, 3]
let first = arr.shift();  // 0, [1, 4, 3]

// 任意位置操作
arr.splice(1, 1, 2, 2);  // [1, 2, 2, 3] (从索引1开始删除1个元素,插入2,2)

查找与遍历

// 查找元素
let index = arr.indexOf(2);  // 1
let lastIndex = arr.lastIndexOf(2);  // 2

// 遍历数组
arr.forEach((item, idx) => {
    console.log(`arr[${idx}] = ${item}`);
});

// 映射新数组
let doubled = arr.map(x => x * 2);  // [2, 4, 4, 6]

// 过滤数组
let filtered = arr.filter(x => x > 2);  // [3]

排序与转换

// 排序
arr.sort((a, b) => a - b);  // [1, 2, 2, 3]
arr.reverse();  // [3, 2, 2, 1]

// 数组转字符串
let str = arr.join("-");  // "3-2-2-1"

// 数组合并
let newArr = arr.concat([4, 5]);  // [3, 2, 2, 1, 4, 5]

4.3 高级方法

// 测试所有元素是否满足条件
let allPass = arr.every(x => x > 0);  // true

// 测试是否有元素满足条件
let anyPass = arr.some(x => x > 2);  // true

// 查找元素
let found = arr.find(x => x > 1);  // 3
let foundIndex = arr.findIndex(x => x > 1);  // 0

// 归约计算
let sum = arr.reduce((acc, cur) => acc + cur, 0);  // 8

五、ArkTS容器与原生容器的区别

在HarmonyOS Next系统中,ArkTS提供了自己的容器实现,与JavaScript原生容器存在行为差异:

特性ArkTS容器原生容器
遍历时修改不允许允许
计算属性名不支持支持
线程安全非线程安全,需异步锁非线程安全
初始化需要构造函数直接字面量
并发传递可安全传递需序列化

六、性能优化

6.1 容器选择

  1. 频繁读取:ArrayList/HashMap
  2. 频繁插入删除:LinkedList
  3. 需要排序:TreeMap/TreeSet
  4. 内存敏感:LightWeightMap/LightWeightSet
  5. 简单数据:原生数组
  6. 唯一性要求:HashSet/TreeSet
  7. 键值对存储:HashMap/TreeMap

6.2 性能优化建议

  1. 预估容量:初始化时设置合理容量避免频繁扩容

let list = new ArrayList<string>(100);  // 初始容量100

     2. 批量操作:优先使用addAll/setAll等方法 

let data = [/* 大量数据 */];
list.addAll(data);

      3. 遍历优化

// 不好的做法
for (let i = 0; i < list.length; i++) {
    let item = list.get(i);
    // ...
}

// 推荐做法
let iterator = list[Symbol.iterator]();
let next = iterator.next();
while (!next.done) {
    let item = next.value;
    // ...
    next = iterator.next();
}

    4.避免装箱拆箱:为数值类型使用专用容器

import LightWeightMap from '@ohos.util.LightWeightMap';
let intMap = new LightWeightMap();  // 比HashMap更节省内存

6.3 线程安全策略

ArkTS容器采用fail-fast机制,并发环境下需要自行保证线程安全:

import { TaskPool } from '@kit.ArkTS';

let map = new collections.Map<string, number>();
let mutex = new TaskPool.Mutex();

async function safeUpdate() {
    await mutex.lock();
    try {
        map.set("key", map.get("key") + 1);
    } finally {
        mutex.unlock();
    }
}

七、常见问题与解决

  1. 容器选择困难

    • 问题:不确定该用ArrayList还是LinkedList
    • 方案:根据操作类型选择 - 频繁随机访问用ArrayList,频繁插入删除用LinkedList
  2. 性能问题

    • 问题:大数据量时操作变慢
    • 方案:检查是否频繁扩容,初始化时设置合理容量;考虑使用更高效的容器如LightWeight系列
  3. 并发问题

    • 问题:多线程环境下容器操作异常
    • 方案:使用TaskPool.Mutex等同步机制
  4. 内存泄漏

    • 问题:容器持有对象导致无法释放
    • 方案:及时清理不再使用的容器;WeakMap/WeakSet等弱引用容器
  5. 类型安全问题

    • 问题:运行时类型错误
    • 方案:严格使用泛型类型约束;必要时进行类型检查
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值