
【学习目标】理解数组的本质掌握数组的创建、读取、修改和常用方法理解元组的特性掌握元组的创建与使用理解枚举的作用掌握枚举的定义、使用场景与核心优势能清晰区分数组、元组与枚举的差异根据业务场景选择使用了解ArkTS线性容器类LinkedList/ArrayList掌握其与原生数组的区别和使用场景【学习重点】数组创建优先使用type[]字面量方式下标从0开始且需避免越界元组是固定长度、固定类型的有序集合与数组的可变特性形成对比数组常用方法中区分“修改原数组”和“返回新数组”的类型枚举用于定义命名常量集合解决“魔法数字/字符串”问题提升代码可读性LinkedList双向链表和ArrayList动态数组是鸿蒙封装的容器类需根据性能需求选择一、工程结构本节我们将创建名为ArrayTupleEnumDemo的工程基于**鸿蒙5.0API12开发使用DevEco Studio 6.0**工具项目结构目录如下ArrayTupleEnumDemo ├── AppScope # 应用全局配置 ├── entry # 主模块目录 │ ├── src │ │ ├── main │ │ │ ├── ets # ArkTS核心代码目录 │ │ │ │ ├── entryability # 应用入口能力 │ │ │ │ ├── pages # 页面组件目录 │ │ │ │ │ └── Index.ets # 核心页面 │ │ │ │ └── utils # 工具函数目录 │ │ │ │ └── ArrayTupleEnumTest.ets # 本节所有学习内容 │ │ │ ├── resources # 静态资源图片/字符串等 │ │ │ └── module.json5 # 模块配置文件 │ │ ├── ohosTest # 测试目录 │ │ └── test # 单元测试目录 │ ├── build-profile.json5 # 构建配置 │ ├── hvigorfile.ts # 构建脚本 │ └── oh-package.json5 # 依赖配置 ├── hvigor # 构建工具依赖 └── oh_modules # 第三方依赖包开发准备步骤打开DevEco Studio创建ArrayTupleEnumDemo项目选择Empty Ability模板API12/鸿蒙5.0在src/main/ets目录下右键创建utils文件夹选中utils文件夹右键创建ArrayTupleEnumTest.ets文件封装所有学习函数所有学习函数在Index.ets的aboutToAppear生命周期中统一调用。二、数组Array有序的同类型数据容器2.1 什么是数组数组是存储多个同类型数据的有序容器可以把它想象成一排编号的储物柜每个储物柜有唯一的下标索引从0开始不是1每个储物柜里只能放同一类型的物品储物柜的数量可以通过push/pop等方法动态调整底层实际是重新创建数组并复制数据2.2 数组的创建两种创建数组的方式字面量方式推荐new Array()仅在特定场景使用。创建方式语法示例特点说明T[]const arr: number[] [1,2,3]语法简洁、无歧义支持类型标注日常开发首选new Array()const arr: Arraynumber new Array(1,2,3)语法繁琐单个数字参数表示数组长度易踩坑代码示例数组的创建/** * 测试数组的创建方式字面量 vs new Array() * 核心对比字面量创建更直观new Array()易产生歧义单数字参数为长度 */ export function testArrayCreation(): void { console.log(\n 数组的创建 ); // 1. 字面量方式创建推荐语义清晰无歧义 const numArr: number[] [10, 20, 30, 40]; // 数字数组显式类型标注 const strArr [苹果, 香蕉, 橙子]; // 字符串数组类型自动推断 const emptyArr: number[] []; // 空数组后续可动态添加元素 const nestedArr: number[][] [[85, 92], [78, 95]]; // 嵌套数组二维数组数组的数组 // 输出验证创建结果 console.log(数字数组, numArr); console.log(字符串数组, strArr); console.log(空数组, emptyArr); console.log(嵌套数组, nestedArr); // 2. new Array()方式仅作对比日常开发不推荐使用 const arr1: Arraynumber new Array(1, 2, 3); // 多参数参数直接作为数组元素 const arr2: Arraynumber new Array(3); // 单数字参数表示数组长度空占位符数组 console.log(new Array(1,2,3), arr1); console.log(new Array(3), arr2); // 输出[]长度为3的空数组控制台仅显示空数组标识 console.log(访问arr2[0], arr2[0]); // 输出undefined // 避坑提示创建单个数字元素的数组必须使用字面量 const singleNumArr: number[] [3]; // 正确数组元素为数字3长度1 const wrongArr: Arraynumber new Array(3); // 错误长度为3的空数组非元素3 }运行效果 数组的创建 数字数组 [10,20,30,40] 字符串数组 [苹果,香蕉,橙子] 空数组 [] 嵌套数组 [[85,92],[78,95]] new Array(1,2,3) [1,2,3] new Array(3) [] 访问arr2[0] undefined2.3 数组的读取通过下标访问读取数组元素的核心是数组名[下标]下标从0开始最后一个元素的下标是数组长度-1。代码示例数组的读取/** * 测试数组的读取操作单层/嵌套数组 越界安全处理 * 核心要点下标从0开始嵌套数组需多层下标越界访问需前置判断 */ export function testArrayRead(): void { console.log(\n 数组的读取 ); // 1. 单层数组读取一维数组 const fruits: string[] [苹果, 香蕉, 橙子]; const firstFruit fruits[0]; // 读取第一个元素下标0 const secondFruit fruits[1]; // 读取第二个元素下标1 const lastFruit fruits[fruits.length - 1]; // 读取最后一个元素通用写法适配数组长度变化 // 输出读取结果 console.log(第一个水果, firstFruit); // 输出苹果 console.log(第二个水果, secondFruit); // 输出香蕉 console.log(最后一个水果, lastFruit); // 输出橙子 // 2. 嵌套数组读取二维数组 const classScores: number[][] [[85, 92], [78, 95]]; // 外层数组班级内层数组学生分数 const score1 classScores[0][1]; // 外层下标0第一个班级→ 内层下标1第二个学生分数→ 92 const score2 classScores[1][0]; // 外层下标1第二个班级→ 内层下标0第一个学生分数→ 78 console.log(外层0内层1的分数, score1); // 输出92 console.log(外层1内层0的分数, score2); // 输出78 // 3. 安全处理数组越界访问下标超出最大索引时的防护 const targetIndex 5; // fruits最大下标为25属于越界 // 推荐写法先判断下标有效性再读取避免返回undefined const fruit targetIndex fruits.length ? fruits[targetIndex] : 默认值下标越界; console.log(读取结果越界处理, fruit); // 输出默认值下标越界 }运行效果 数组的读取 第一个水果 苹果 第二个水果 香蕉 最后一个水果 橙子 外层0内层1的分数 92 外层1内层0的分数 78 读取结果越界处理 默认值下标越界2.4 数组的修改通过下标赋值修改数组元素的格式为数组名[下标] 新值单层和嵌套数组的修改逻辑一致。代码示例数组的修改/** * 测试数组的修改操作单层元素修改 嵌套数组增改 * 核心要点数组为引用类型直接通过下标修改元素嵌套数组需定位到内层下标 */ export function testArrayModify(): void { console.log(\n 数组的修改 ); const fruits: string[] [苹果, 香蕉, 橙子]; const classScores: number[][] [[85, 92], [78, 95]]; // 1. 单层数组元素修改直接通过下标赋值 fruits[1] 葡萄; // 将下标1的香蕉修改为葡萄 console.log(修改后的水果数组, fruits); // 输出[苹果,葡萄,橙子] // 2. 嵌套数组修改先添加内层数组再修改内层元素 classScores.push([60, 88]); // 向嵌套数组添加新的内层数组第三个班级分数 classScores[2][1] 90; // 修改新添加的内层数组下标1的元素88→90 console.log(修改后的分数数组, classScores); // 输出[[85,92],[78,95],[60,90]] }运行效果 数组的修改 修改后的水果数组 [苹果,葡萄,橙子] 修改后的分数数组 [[85,92],[78,95],[60,90]]2.5 数组的常用方法增删改查数组提供了常用方法来操作元素分为修改原数组和返回新数组两类新手需注意区分。方法名作用说明是否修改原数组push(item)在数组末尾添加一个/多个元素返回新长度是pop()删除最后一个元素返回被删除的元素是splice(start, delNum, ...add)指定位置增删改返回被删元素是slice(start, end)截取元素返回新数组否代码示例数组常用方法/** * 测试数组的常用方法修改原数组 vs 返回新数组 * 核心区分push/pop/splice修改原数组slice返回新数组原数组不变 */ export function testArrayMethods(): void { console.log(\n 数组的常用方法 ); // 1. 修改原数组的方法会改变原数组内容/长度 let nums: number[] [1, 2, 3]; console.log(原数组, nums); // 输出[1,2,3] // push向数组末尾添加一个/多个元素返回新数组长度 const newLength nums.push(4, 5); console.log(push后, nums); // 输出[1,2,3,4,5] console.log(新长度, newLength); // 输出5 // pop删除数组最后一个元素返回被删除的元素 const popItem nums.pop(); console.log(pop后, nums); // 输出[1,2,3,4] console.log(被删除的元素, popItem); // 输出5 // splice指定位置增/删/改元素返回被删除的元素数组 // 参数说明splice(起始下标, 删除数量, 新增元素1, 新增元素2...) const spliceItems nums.splice(1, 2, 6, 7); // 下标1开始删除2个元素添加6、7 console.log(splice后, nums); // 输出[1,6,7,4] console.log(被删除的元素, spliceItems); // 输出[2,3] // 2. 不修改原数组的方法返回新数组原数组保持不变 const nums2: number[] [10, 20, 30, 40, 50]; // slice截取指定范围元素返回新数组左闭右开不包含结束下标 const slice1 nums2.slice(1, 3); // 截取下标1到3不含3→ [20,30] const slice2 nums2.slice(2); // 截取下标2到末尾 → [30,40,50] console.log(原数组slice不修改, nums2); // 输出[10,20,30,40,50] console.log(slice(1,3), slice1); // 输出[20,30] console.log(slice(2), slice2); // 输出[30,40,50] }运行效果 数组的常用方法 原数组 [1,2,3] push后 [1,2,3,4,5] 新长度 5 pop后 [1,2,3,4] 被删除的元素 5 splice后 [1,6,7,4] 被删除的元素 [2,3] 原数组slice不修改 [10,20,30,40,50] slice(1,3) [20,30] slice(2) [30,40,50]2.6 线性容器类LinkedList与ArrayList除了原生数组ArkTS还提供了两个高频使用的容器类底层分别基于双向链表和动态数组实现适用于不同性能场景。代码示例LinkedList与ArrayList的使用// 线性容器类必须在文件顶部导入 import { ArrayList, LinkedList } from kit.ArkTS; /** * 线性容器类LinkedList双向链表、ArrayList动态数组 * 核心差异LinkedList适合频繁增删ArrayList适合频繁查询 */ export function testHarmonyContainers(): void { console.log(\n LinkedList/ArrayList使用 ); // 1. LinkedList双向链表底层链表实现头尾操作高效 const linkedList: LinkedListnumber new LinkedListnumber(); try { // 基础增删查 linkedList.add(100); // 向链表尾部添加元素 linkedList.add(200); // 继续向尾部添加 linkedList.insert(1, 300); // 在下标1的位置插入元素300 console.log(LinkedList首元素, linkedList.getFirst()); // 获取链表第一个元素 → 100 console.log(LinkedList尾元素, linkedList.getLast()); // 获取链表最后一个元素 → 200 console.log(LinkedList下标1元素, linkedList.get(1)); // 获取下标1元素 → 300需遍历链表 // 链表头尾高效操作核心优势 linkedList.addFirst(50); // 向链表头部添加元素O(1)无需遍历 linkedList.add(250); // 向链表尾部添加元素底层遍历到尾部效率低于addFirst console.log(头尾添加后, linkedList.convertToArray()); // 输出[50,100,300,200,250] // 链表删除操作 linkedList.removeFirst(); // 删除链表头部元素O(1) const lastValue linkedList.getLast(); // 获取尾部元素 linkedList.remove(lastValue); // 删除尾部元素需先获取值再删除 console.log(删除头尾后, linkedList.convertToArray()); // 输出[100,300,200] console.log(LinkedList转原生数组, linkedList.convertToArray()); // 输出[100,300,200] } catch (error) { console.error(LinkedList操作异常, error instanceof Error ? error.message : error); } // 2. ArrayList动态数组底层数组实现查询高效支持容量管理 const dynamicArr: ArrayListnumber new ArrayListnumber(); try { dynamicArr.add(10); // 添加元素到数组尾部 dynamicArr.add(20); dynamicArr.add(30); console.log(ArrayList初始元素, dynamicArr.convertToArray()); // 输出[10,20,30] // 元素删除按值删除、按下标删除 dynamicArr.remove(10); // 移除值为10的元素 → 剩余[20,30] console.log(移除元素10后, dynamicArr.convertToArray()); // 输出[20,30] dynamicArr.removeByIndex(1); // 移除下标1的元素30 → 剩余[20] console.log(移除下标1元素后, dynamicArr.convertToArray()); // 输出[20] // 安全访问先确认元素存在再通过下标访问 dynamicArr.add(40); dynamicArr.add(50); // 补充元素便于观察容量变化 console.log(ArrayList下标1元素, dynamicArr[1]); // 输出40原生支持下标访问 console.log(ArrayList当前容量, dynamicArr.getCapacity()); // 输出默认初始容量如10 // 容量管理动态数组专属能力 dynamicArr.increaseCapacityTo(20); // 手动扩容到20提前预留空间减少频繁扩容 console.log(扩容后容量, dynamicArr.getCapacity()); // 输出20 dynamicArr.trimToCurrentLength(); // 释放冗余容量容量当前元素数量节省内存 console.log(优化后容量, dynamicArr.getCapacity()); // 输出3 // 转换为原生数组便于使用标准数组方法 const nativeArr dynamicArr.convertToArray(); console.log(ArrayList转原生数组, nativeArr); // 输出[20,40,50] } catch (error) { console.error(ArrayList操作异常, error instanceof Error ? error.message : error); } }运行效果 LinkedList/ArrayList使用 LinkedList首元素 100 LinkedList尾元素 200 LinkedList下标1元素 300 头尾添加后 [50,100,300,200,250] 删除头尾后 [100,300,200] LinkedList转原生数组 [100,300,200] ArrayList初始元素 [10,20,30] 移除元素10后 [20,30] 移除下标1元素后 [20] ArrayList下标1元素 40 ArrayList当前容量 10 扩容后容量 20 优化后容量 3 ArrayList转原生数组 [20,40,50]核心对比容器/数组类型底层实现内存结构长度可变性查询效率增删效率核心特性核心适用场景ArrayListT动态数组连续内存块✅自动扩容50%/手动调整⭐⭐⭐⭐O(1) 索引直接访问⭐O(n) 中间增删需移动元素基于数组实现原生支持下标访问可管理容量高频读取、偶尔增删如商品列表LinkedListT双向链表非连续内存节点指针✅按需创建节点⭐O(n) 需遍历链表⭐⭐⭐⭐O(1) 头尾增删仅修改指针每个节点指向前后元素头尾操作极致高效头尾高频增删、双向遍历如双向队列/缓存ArrayT静态数组连续内存块❌长度固定扩容需重新创建⭐⭐⭐⭐O(1) 索引直接访问❌无原生动态增删需splice模拟基础有序容器类型统一语法简单数据长度固定如配置项、固定列表提示数组还有很多内置的高级用法如filter、map、reduce等迭代方法基础阶段前期我们主要以掌握上述核心增删改查方法为主这些高级用法会在后续的进阶章节中结合业务场景详细讲解。三、元组Tuple固定长度的异类型有序集合3.1 什么是元组元组是ArkTSTypeScript中特有的数据类型是固定长度、固定类型的有序集合可以把它想象成“定制化的储物柜”每个储物柜有固定的类型可以是不同类型比如第一个放数字第二个放字符串语法上支持push添加元素但会破坏元组的固定长度特性类型层面不推荐下标从0开始访问方式和数组一致3.2 元组的创建与使用代码示例/** * 测试元组的核心操作创建、访问、修改 * 核心特性元组是固定长度、固定类型顺序的数组类型校验严格 */ export function testTupleBasic(): void { console.log(\n 元组的核心操作 ); // 1. 元组创建必须显式标注类型固定长度类型顺序 const userInfo: [number, string, boolean] [1001, 张三, true]; // 编号数字、姓名字符串、VIP布尔 const point: [number, number] [100, 200]; // 二维坐标x轴数字y轴数字 const color: readonly [number, number, number] [255, 0, 0]; // 只读元组元素不可修改RGB颜色值 console.log(用户信息元组, userInfo); // 输出[1001,张三,true] console.log(坐标元组, point); // 输出[100,200] console.log(颜色元组只读, color); // 输出[255,0,0] // 2. 元组访问通过下标访问类型与定义时一致 const userId userInfo[0]; // 数字类型 const userName userInfo[1]; // 字符串类型 console.log(用户编号, userId); // 输出1001 console.log(用户姓名, userName); // 输出张三 // 3. 元组修改仅能修改对应下标元素且类型必须匹配 userInfo[2] false; // 布尔类型修改为false符合类型定义 console.log(修改后的VIP状态, userInfo[2]); // 输出false // 错误操作示例取消注释会编译报错 // userInfo[1] 123; // 报错下标1要求string类型不能赋值number // color[0] 200; // 报错readonly元组的属性为只读无法修改 }运行效果 元组的核心操作 用户信息元组 [1001,张三,true] 坐标元组 [100,200] 颜色元组只读 [255,0,0] 用户编号 1001 用户姓名 张三 修改后的VIP状态 false3.3 数组与元组的核心区别特性数组元组长度可变可增删元素设计为固定长度语法支持push但不推荐元素类型必须同类型可以不同类型固定顺序类型标注可省略自动推断必须显式标注readonly元组也需标注使用场景存储多个同类型数据存储固定结构的不同类型数据如坐标、用户信息四、枚举Enum命名常量的集合4.1 为什么需要枚举在开发中我们经常会遇到一组固定的常量值比如性别、订单状态、颜色。如果直接用数字或字符串表示会存在可读性差、易出错的问题。枚举类型能为一组常量赋予有意义的名称让代码更清晰、更健壮。4.2 枚举的创建与使用代码示例/** * 测试枚举的定义与使用数字枚举、字符串枚举、常量枚举 * 核心价值将魔法值封装为具名常量提升代码可读性和可维护性 */ export function testEnumBasic(): void { console.log(\n 枚举的核心操作 ); // 1. 数字枚举默认从0开始递增支持反向查找值→名 enum Gender { Male, // 默认值0 Female // 默认值1 } console.log(男性的枚举值, Gender.Male); // 输出0 console.log(值为1的枚举名, Gender[1]); // 输出Female数字枚举专属反向查找 // 枚举作为类型标注变量仅能赋值枚举成员 let userGender: Gender Gender.Male; userGender Gender.Female; // 正确赋值枚举成员 // userGender 2; // 错误2不是Gender枚举的有效成员 // 2. 字符串枚举推荐使用语义更清晰无反向查找 enum OrderStatus { Pending 待支付, Paid 已支付, Canceled 已取消 } console.log(待支付状态, OrderStatus.Pending); // 输出待支付 console.log(已支付状态, OrderStatus.Paid); // 输出已支付 // 字符串枚举类型标注 let orderStatus: OrderStatus OrderStatus.Pending; orderStatus OrderStatus.Canceled; // 正确 // orderStatus 已完成; // 错误已完成不是OrderStatus的有效成员 // 3. 常量枚举编译时直接替换为常量值优化性能无反向查找 const enum Color { Red 红色, Green 绿色, Blue 蓝色 } let fontColor: Color Color.Red; console.log(字体颜色, fontColor); // 输出红色 // console.log(Color[红色]); // 错误常量枚举不支持反向查找仅普通数字枚举支持 }运行效果 枚举的核心操作 男性的枚举值 0 值为1的枚举名 Female 待支付状态 待支付 已支付状态 已支付 字体颜色 红色4.3 数组、元组与枚举的核心区别特性数组元组枚举本质同类型值的可变集合多类型值的固定集合语法支持push命名常量的不可变集合长度可变设计为固定语法支持push但不推荐固定常量数量固定元素可修改是是类型匹配时否枚举常量值不可修改使用场景存储动态的同类型数据存储固定结构的多类型数据存储固定的常量值状态、类型4.4 新手易错点总结数组下标越界始终确保访问的下标 数组长度推荐用arr.length - 1获取最后一个元素new Array() 陷阱new Array(3)是创建长度为3的空数组而非元素为3的数组新手优先用[3]元组的push误用元组设计为固定长度语法支持push但会破坏类型约束开发中禁止使用import 位置错误ArkTS中import必须放在文件顶部不能在函数/方法内枚举反向查找误区只有数字枚举支持反向查找Gender[0] Male字符串枚举/常量枚举不支持LinkedList下标访问性能LinkedList的get(index)需遍历链表频繁查询请改用ArrayList/原生数组优先使用头尾操作方法。五、完整可运行代码Index.ets// pages/Index.ets // 1. 导入工具类的所有测试函数 import { testArrayCreation, testArrayRead, testArrayModify, testArrayMethods, testHarmonyContainers, testTupleBasic, testEnumBasic } from ../utils/ArrayTupleEnumTest; Entry Component struct Index { // 生命周期方法统一调用所有测试方法 aboutToAppear(): void { testArrayCreation(); // 数组创建 testArrayRead(); // 数组读取 testArrayModify(); // 数组修改 testArrayMethods(); // 数组常用方法 testHarmonyContainers(); //线性容器LinkedList/ArrayList testTupleBasic(); // 元组核心操作 testEnumBasic(); // 枚举核心操作 } build() { Column() { Text(数组、元组与枚举基础) .fontSize(30) .fontWeight(FontWeight.Bold) Text(运行日志请查看控制台) .fontSize(16) .marginTop(20) .fontColor(Color.Grey) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center); } }六、内容总结数组/容器类选型核心连续内存ArrayList/Array查询快非连续内存LinkedList头尾增删快ArrayList/LinkedList 支持动态调整长度Array 长度固定查多用 ArrayList/Array头尾高频增删多用 LinkedList数据固定用 Array元组使用原则固定长度/类型的异类型集合需显式标注类型readonly元组不可修改禁用push避免破坏类型约束枚举最佳实践优先使用字符串枚举解决“魔法值”问题常量枚举优化性能但不支持反向查找核心避坑点数组访问先判长度、new Array()慎用、元组禁用push、LinkedList避免频繁下标查询。七、代码仓库工程名称ArrayTupleEnumDemo仓库地址https://gitee.com/juhetianxia321/harmony-os-code-base.git八、下节预告下一节将进入分支语句if-else、switch重点包括理解流程控制的核心作用掌握if-else分支语句的语法与嵌套使用场景。掌握switch语句的语法与break关键字的作用理解switch与if-else的适用场景差异。掌握三元运算符的使用区分三元运算符与if-else的使用场景。能结合业务场景使用分支语句处理条件判断如成绩等级判断、订单状态处理。