ArkTS 数组方法

ArkTS 数组方法全解

按使用频率讲解 ArkTS 数组方法,每个方法给出 ArkTS 示例与 Kotlin / Java 对照,并标出原地修改、越界、排序等常见坑。

按使用频率 对照 Kotlin / Java HarmonyOS / ArkTS 含实战速查

01数组声明与类型

先搞清「ArkTS 数组」是什么,以及它和 Kotlin List、Java 集合的差异——这是后面所有方法的前提。

ArkTS
// 两种等价写法
let nums: number[] = [1, 2, 3];
let names: Array<string> = ['a', 'b'];

// 只读:用 const + 不再重新赋值
const readOnly = [1, 2, 3];   // 元素仍可被 push(见下)
const fixed: ReadonlyArray<number> = [1, 2, 3]; // 禁止增删

// 对象数组(必须对应 interface/class)
interface User { id: number; name: string; }
const users: User[] = [
  { id: 1, name: 'Tom' },
  { id: 2, name: 'Joy' }
];
Kotlin
// 不可变 List(默认)
val nums = listOf(1, 2, 3)
// 可变
val m = mutableListOf(1, 2, 3)
m.add(4)

// 类型推断
val names = listOf("a", "b")

// 对象:data class 最常用
data class User(val id: Int, val name: String)
val users = listOf(
  User(1, "Tom"),
  User(2, "Joy")
)
Java
// 数组(定长)
int[] nums = {1, 2, 3};
// 可变集合
List<Integer> list = new ArrayList<>();
list.add(4);

// 不可变(Java 9+)
List<Integer> ro = List.of(1, 2, 3);

// 对象
record User(int id, String name) {}
List<User> users = List.of(
  new User(1, "Tom"),
  new User(2, "Joy")
);
关键差异:① ArkTS 的 const arr = [...] 只能保证「变量名不被重新赋值」,元素仍可被 push / splice 修改;要真正只读用 ReadonlyArray<T>。② Kotlin 默认 listOf 就不可变,更符合函数式习惯。③ ArkTS 数组赋值是引用传递:const b = a; b.push(9) 会改到 a,需要独立副本时用 [...a] 或 a.slice()。

02访问、长度与遍历

length 是属性不是方法;遍历推荐 for...of 或 forEach;ArkTS 禁止用 for...in 遍历数组。

ArkTS
const arr = [10, 20, 30];

// 长度(属性,不是方法)
console.log(arr.length);   // 3

// 索引访问
const first = arr[0];       // 10
const last = arr[arr.length - 1]; // 30

// 越界:返回 undefined(不会抛异常!)
const x = arr[99];          // undefined

// 推荐遍历:for...of
for (const n of arr) { console.log(n); }

// 传统 for(需要索引时)
for (let i = 0; i < arr.length; i++) { }

// ⚠️ 禁止 for...in(会遍历到数组的自定义属性)
// for (const k in arr) { }  // 编译报错
Kotlin
val arr = listOf(10, 20, 30)

// 大小(属性)
println(arr.size)          // 3

// 索引访问(越界抛异常)
val first = arr[0]
val last = arr.last()

// 安全获取
val x = arr.getOrNull(99)  // null

// 遍历
for (n in arr) { println(n) }
arr.forEach { println(it) }
arr.forEachIndexed { i, n -> }
Java
int[] arr = {10, 20, 30};
List<Integer> list = Arrays.asList(10, 20, 30);

// 数组 length(属性);List 用 size()
System.out.println(arr.length);
System.out.println(list.size());

// 越界抛异常
int x = arr[99];   // ArrayIndexOutOfBounds

// 增强 for
for (int n : arr) { }
list.forEach(n -> System.out.println(n));
避坑:ArkTS 数组越界返回 undefined 而不是抛异常,所以取索引后最好配合 if (x !== undefined) 或可选链判空。另外 for...in 在 ArkTS 中被禁用(它会遍历对象可枚举属性,对数组是坑),一律用 for...of。

03高频方法(每天写业务都用得到)

这 11 个方法覆盖 90% 的业务逻辑:遍历、转换、过滤、查找、增删。每个都给 ArkTS 示例 + Kotlin 对照。

高频 forEach arr.forEach((item, index) => void)

遍历数组、执行业务副作用(打印、改 UI、发请求),不返回新数组。需要返回值映射时用 map。

ArkTS
const list = ['a', 'b', 'c'];
list.forEach((item, i) => {
  console.log(i, item);
});
// 0 a / 1 b / 2 c
Kotlin
val list = listOf("a", "b", "c")
list.forEach { println(it) }
list.forEachIndexed { i, item ->
  println("$i $item")
}

高频 map arr.map((item) => newItem): T[]

把每个元素「1:1 转换」成新元素,返回新数组,不修改原数组。对应 Kotlin 的 map、Java Stream 的 map。

ArkTS
const nums = [1, 2, 3];
// 每个 ×2
const doubled = nums.map(n => n * 2);
// [2, 4, 6],原 nums 不变

// 对象数组:提取字段
const names = users.map(u => u.name);
Kotlin
val nums = listOf(1, 2, 3)
val doubled = nums.map { it * 2 }   // [2,4,6]
val names = users.map { it.name }
易错点:map 一定返回新数组,别写 nums.map(n => n*2) 然后期待 nums 变了——它没变。要就地改就用 forEach 或重新赋值。

高频 filter arr.filter((item) => boolean): T[]

按条件「筛选」元素,返回新数组。回调返回 true 才保留。

ArkTS
const nums = [1, 2, 3, 4, 5];
const even = nums.filter(n => n % 2 === 0);
// [2, 4]

// 对象数组:过滤状态
const active = users.filter(u => u.id > 0);
Kotlin
val nums = listOf(1, 2, 3, 4, 5)
val even = nums.filter { it % 2 == 0 } // [2,4]
val active = users.filter { it.id > 0 }

高频 push arr.push(...items): number

向尾部追加一个或多个元素,修改原数组,返回追加后的新 length。

ArkTS
const list: number[] = [1, 2];
const len = list.push(3);     // len = 3
list.push(4, 5);              // [1,2,3,4,5]
console.log(list);
Kotlin
val list = mutableListOf(1, 2)
list.add(3)             // 返回 Boolean
list.add(4); list.add(5)
// [1,2,3,4,5]
注意:push 返回的是「新长度(number)」,不是新数组!别写成 list = list.push(3)。对应头部插入是 unshift(见中频)。

高频 find arr.find((item) => boolean): T | undefined

返回第一个满足条件的元素;找不到返回 undefined。比 filter 取 [0] 更高效(找到就停)。

ArkTS
const user = users.find(u => u.id === 2);
// 找到 → {id:2,name:'Joy'}
// 没找到 → undefined(需判空!)

if (user !== undefined) {
  console.log(user.name);
}
Kotlin
// firstOrNull 等价 find
val user = users.find { it.id == 2 }
val u2 = users.firstOrNull { it.id == 2 }
// 没找到 → null(可 ?. 处理)

高频 findIndex arr.findIndex((item) => boolean): number

返回第一个满足条件的元素索引;找不到返回 -1。常配合 splice 做「按条件删除/替换」。

ArkTS
const idx = users.findIndex(u => u.id === 2);
// 2;没找到 → -1
if (idx !== -1) {
  users.splice(idx, 1);   // 删除该元素
}
Kotlin
val idx = users.indexOfFirst { it.id == 2 }
// -1 表示没找到
if (idx != -1) users.removeAt(idx)

高频 some arr.some((item) => boolean): boolean

是否至少有一个元素满足条件(有就停,短路)。对应 Kotlin 的 any。

ArkTS
const hasAdult = users.some(u => u.age >= 18);
// true / false

// 权限判断常见用法
const canEdit = roles.some(r => r === 'admin');
Kotlin
val hasAdult = users.any { it.age >= 18 }
val canEdit = roles.any { it == "admin" }

高频 every arr.every((item) => boolean): boolean

是否所有元素都满足条件。对应 Kotlin 的 all。空数组调用 every 返回 true(全称量词真空成立)。

ArkTS
const allValid = users.every(u => u.name.length > 0);
// 表单/校验场景常用
Kotlin
val allValid = users.all { it.name.isNotEmpty() }

高频 includes arr.includes(item): boolean

数组是否包含某元素(用严格相等 === 比较)。比 indexOf 语义更清晰,推荐用它做「是否存在」判断。

ArkTS
const tags = ['new', 'hot'];
if (tags.includes('hot')) { /* ... */ }

// 注意:对象按引用比较
const a = {x:1};
[{x:1}].includes(a);   // false(不同引用)
Kotlin
val tags = listOf("new", "hot")
if ("hot" in tags) { }   // 或
if (tags.contains("hot")) { }
坑:includes 对对象是「引用相等」,[{x:1}].includes({x:1}) 是 false。要按内容判断用 some。

高频 indexOf arr.indexOf(item): number

返回元素首次出现的索引,没有则返回 -1。和 includes 区别:它给你位置(常用来判断「是否存在」或「在哪」)。

ArkTS
const i = ['a','b','c'].indexOf('b'); // 1
const j = ['a','b'].indexOf('z');     // -1

// 从指定位置搜
const k = [1,2,1,2].indexOf(2, 2);    // 3
Kotlin
val i = listOf("a","b","c").indexOf("b") // 1
val j = listOf("a","b").indexOf("z")     // -1

高频 splice arr.splice(start, deleteCount?, ...items): T[]

数组的「瑞士军刀」:原地增 / 删 / 改,返回被删除的元素数组。参数(start 索引、删除个数、要插入的元素)容易记混,务必看示例。

ArkTS
let a = [1, 2, 3, 4];

a.splice(1, 1);        // 从索引1删1个 → a=[1,3,4]
a.splice(1, 0, 9);     // 索引1插9不删 → [1,9,3,4]
a.splice(2, 2, 7, 8);  // 删2个再插2个
                       // → [1,9,7,8]
Kotlin
val a = mutableListOf(1, 2, 3, 4)
a.removeAt(1)          // 删1个
a.add(1, 9)            // 索引1插9
a.subList(2, 4).clear()// 删区间
a.addAll(2, listOf(7,8))
最易错方法:① 它修改原数组(和 slice 相反);② 第二个参数是「删除个数」不是「结束索引」;③ 返回的是被删掉的片段,不是改完的数组。配合 findIndex 做「按条件删除」是高频组合。

04中频方法

归约、排序、切片、合并、转字符串、首尾增删、类型转换——业务中经常出现,但没高频方法那么每天都写。

中频 reduce arr.reduce((acc, item) => acc, init)

把数组「归约」成单个值(求和、拼对象、分组)。对应 Kotlin 的 fold / reduce,Java Stream 的 reduce。

ArkTS
const nums = [1, 2, 3, 4];
// 求和(init=0 是初始累加器)
const sum = nums.reduce((acc, n) => acc + n, 0); // 10

// 转成 Map(按 id 分组)
const byId = users.reduce((m, u) => {
  m[u.id] = u; return m;
}, {} as Record<number, User>);
Kotlin
val nums = listOf(1, 2, 3, 4)
val sum = nums.fold(0) { acc, n -> acc + n }
// 或 nums.sum()
val byId = users.associateBy { it.id }
ArkTS 特有的坑:reduce 的「初始值」建议总是显式传(第二个参数),否则空数组会抛 TypeError,且类型推断容易出问题(ArkTS 严格)。上面用 {} as Record<number, User> 给累加器标注类型。

中频 sort arr.sort((a, b) => number)

原地排序,返回原数组(已排好)。回调返回负数 / 0 / 正数 表示 小于 / 等于 / 大于。不传回调会按字符串 Unicode 排——数字排序必传回调!

ArkTS
const nums = [3, 1, 2];
nums.sort((a, b) => a - b);  // 升序 → [1,2,3]
nums.sort((a, b) => b - a);  // 降序

// 对象排序
users.sort((a, b) => a.id - b.id);

// ⚠️ 不传回调:按字符串排!
[10, 2, 1].sort();  // [1, 10, 2] 错!
Kotlin
val nums = listOf(3, 1, 2)
nums.sorted()                 // 新数组升序
nums.sortedByDescending { it }
users.sortedBy { it.id }     // 对象排序
// sorted() 返回新数组,不改原
两个大坑:① sort 是原地修改原数组;要保留原数组先 [...arr].sort(...)。② 数字排序必须传 (a,b)=>a-b,否则按字符排 [10,2,1]→[1,10,2]。Kotlin 的 sorted() 返回新数组、更安全。

中频 slice arr.slice(start?, end?): T[]

浅拷贝出「子数组」(不修改原数组)。end 是「结束索引(不含)」。不传参数就是整份拷贝。

ArkTS
const a = [1, 2, 3, 4, 5];
a.slice(1, 3);   // [2, 3](不含索引3)
a.slice(2);      // [3, 4, 5]
const copy = a.slice();  // 整份浅拷贝
Kotlin
val a = listOf(1, 2, 3, 4, 5)
a.subList(1, 3)  // [2,3](视图,改它会影响 a!)
a.take(3)        // 前3个,新列表
a.drop(2)        // 丢前2个
ArkTS 的 slice vs Kotlin 的 subList:ArkTS 的 slice 返回独立副本;Kotlin 的 subList 是原列表的视图,改视图会改原列表。做「克隆」用 [...a] 或 a.slice() 都行。

中频 concat arr.concat(...others): T[]

合并多个数组 / 元素成新数组(不修改原)。等价写法还有展开运算符 [...a, ...b](更常用)。

ArkTS
const a = [1, 2];
const b = [3, 4];
a.concat(b);          // [1,2,3,4],a 不变
[...a, ...b];         // 展开写法(推荐)
[...a, 5];            // 追加单个元素
Kotlin
val a = listOf(1, 2)
val b = listOf(3, 4)
a + b                 // [1,2,3,4]
a + 5                 // [1,2,5]
a.plus(b)

中频 join arr.join(sep?): string

把数组元素用分隔符拼成字符串。对应 Kotlin 的 joinToString(功能更强,可加前后缀)。

ArkTS
['a', 'b', 'c'].join(',');     // "a,b,c"
['a', 'b'].join(' | ');        // "a | b"
[1, 2, 3].join('-');           // "1-2-3"
Kotlin
listOf("a","b","c").joinToString(",")
listOf("a","b").joinToString(" | ")
// joinToString 还能加 prefix/suffix
users.joinToString { it.name }

中频 reverse arr.reverse(): T[]

原地反转,返回原数组(已反转)。要保留原数组先拷贝。

ArkTS
const a = [1, 2, 3];
a.reverse();        // a 变成 [3,2,1]
[...a].reverse();   // 不破坏原数组的写法
Kotlin
val a = listOf(1, 2, 3)
a.reversed()        // 返回新列表 [3,2,1]
a.asReversed()      // 视图

中频 unshift arr.unshift(...items): number

向头部插入元素,修改原数组,返回新 length。性能是 O(n)(要搬移所有元素),频繁头部插入慎用。

ArkTS
const a = [2, 3];
a.unshift(0, 1);    // a = [0,1,2,3]
console.log(a.length);
Kotlin
val a = mutableListOf(2, 3)
a.add(0, 0)        // 索引0插入
a.add(0, 1)

中频 pop arr.pop(): T | undefined

删除并返回尾部元素,修改原数组。空数组 pop 返回 undefined。

ArkTS
const a = [1, 2, 3];
const last = a.pop();   // last=3, a=[1,2]
const empty = [].pop(); // undefined
Kotlin
val a = mutableListOf(1, 2, 3)
val last = a.removeLast()   // 3, a=[1,2]
// 或 a.removeAt(a.lastIndex)
栈结构常用:push + pop 就是「后进先出」栈。ArkTS 没有专门 Stack 类,用数组 + push/pop 即可。

中频 shift arr.shift(): T | undefined

删除并返回头部元素,修改原数组。性能 O(n)(搬移全部),大数组频繁 shift 很慢。

ArkTS
const a = [1, 2, 3];
const head = a.shift();  // head=1, a=[2,3]
Kotlin
val a = mutableListOf(1, 2, 3)
val head = a.removeFirst()  // 1, a=[2,3]
队列场景:想实现「先进先出」队列,用 push(尾部入)+ shift(头部出);但 shift 是 O(n),高频大数据建议用双端结构或反转思路。Kotlin 有 ArrayDeque 专门干这个。

中频 Array.from Array.from(iterable | arrayLike): T[]

把「类数组 / 可迭代对象」转成真正的数组——常用在把 Set、Map 的 keys、arguments、DOM/ArkUI 节点集合转成可链式调用的数组。

ArkTS
// Set → Array
const set = new Set([1, 2, 2]);
const arr = Array.from(set);   // [1, 2]

// Map 的 key 列表
const m = new Map([[1,'a'],[2,'b']]);
const keys = Array.from(m.keys()); // [1,2]

// 带映射:等于 from + map
const doubles = Array.from([1,2,3], n => n*2); // [2,4,6]
Kotlin
val set = setOf(1, 2, 2)
val arr = set.toList()        // [1,2]
val m = mapOf(1 to "a", 2 to "b")
val keys = m.keys.toList()    // [1,2]
ArkTS 注意:ArkTS 里 Set / Map 也可用,但它们的 keys() / values() 返回迭代器,不能直接 .map。要先 Array.from(...) 转数组再链式调用。

中频 flatMap arr.flatMap((item) => T[]): T[]

先 map 再「扁平一层」——每个元素映射成一个小数组,最后拼成一级数组。对应 Kotlin 的 flatMap。

ArkTS
const groups = [['a','b'], ['c']];
const flat = groups.flatMap(g => g);  // ['a','b','c']

// 经典:一对多展开
const tags = users.flatMap(u => u.tags);
Kotlin
val groups = listOf(listOf("a","b"), listOf("c"))
val flat = groups.flatMap { it }  // [a,b,c]
val tags = users.flatMap { it.tags }

05低频与特殊方法

这些要么偏底层、要么有更常用替代,实际业务很少手写,但面试/读源码/特殊场景会碰到。扫一遍知道有啥即可。

低频 flat arr.flat(depth? = 1): T[]

把嵌套数组「扁平化」,depth 指定层数(默认 1)。对应 Kotlin 的 flatten。

ArkTS
[1, [2, 3]].flat();          // [1,2,3]
[1, [2, [3]]].flat(2);       // [1,2,3]
[1, [2, [3]]].flat(Infinity);// 彻底摊平
Kotlin
listOf(1, listOf(2, 3)).flatten()
// [1,2,3]

低频 fill arr.fill(value, start?, end?): T[]

原地用某个值填充数组(可指定区间)。常用于初始化占位数组。

ArkTS
const a = new Array(3).fill(0);  // [0,0,0]
[1,2,3,4].fill(9, 1, 3);         // [1,9,9,4]
Kotlin
val a = MutableList(3) { 0 }     // [0,0,0]
val b = MutableList(4) { 1 }
b.fill(9, 1, 3)                  // [1,9,9,4]

低频 at arr.at(index): T | undefined

用负数索引从末尾取元素(-1 = 最后一个)。API 9+ 支持。比 arr[arr.length-1] 更直观。

ArkTS
const a = [10, 20, 30];
a.at(-1);   // 30(最后一个)
a.at(-2);   // 20
a.at(0);    // 10
Kotlin
val a = listOf(10, 20, 30)
a.last()        // 30
a[a.lastIndex]  // 30
// Kotlin 没有负索引,用 last()/get

低频 entries / keys / values arr.entries(): Iterator

返回迭代器:entries 是 [index, value],keys 是索引,values 是元素。通常配合 for...of 或直接 Array.from 转数组。

ArkTS
const a = ['x', 'y'];
for (const [i, v] of a.entries()) {
  console.log(i, v);   // 0 x / 1 y
}
const idx = Array.from(a.keys());  // [0,1]
const vals = Array.from(a.values()); // ['x','y']
Kotlin
val a = listOf("x", "y")
for ((i, v) in a.withIndex()) {
  println("$i $v")
}
val idx = a.indices.toList()  // 0..1
val vals = a.toList()

低频 copyWithin arr.copyWithin(target, start, end?): T[]

原地把数组内部一段复制到另一段(性能优化用,不常用,易读性差)。

ArkTS
// 把 [2,3) 复制到从索引0开始
[1, 2, 3, 4].copyWithin(0, 2, 4);
// → [3, 4, 3, 4]
Kotlin
// Kotlin 标准库无直接等价,
// 一般用 subList + addAll 组合实现

低频 reduceRight arr.reduceRight((acc, item) => acc, init)

和 reduce 一样,但从右往左遍历。处理「从右结合」语义(如嵌套结构)时才需要。

ArkTS
const s = ['a','b','c'].reduceRight(
  (acc, v) => acc + v, '');  // "cba"
Kotlin
listOf("a","b","c").foldRight("") { v, acc -> acc + v }
// "cba"

低频 Array.of Array.of(...items): T[]

用给定元素创建数组。和 new Array(n) 不同:Array.of(3) 是 [3],而 new Array(3) 是长度为 3 的空位数组。日常基本用字面量 [] 就够了。

ArkTS
Array.of(1, 2, 3);   // [1,2,3]
Array.of(3);         // [3]
new Array(3);        // [empty×3] 长度3空位
Kotlin
arrayOf(1, 2, 3)     // [1,2,3]
// Kotlin 没「长度空位」歧义

低频 Array.isArray Array.isArray(x): boolean

运行时判断某值是不是数组。ArkTS 静态类型强,但遇到 union / 接口入参仍可能需要运行时区分类型。

ArkTS
Array.isArray([1, 2]);   // true
Array.isArray('hi');     // false

function handle(x: number[] | string) {
  if (Array.isArray(x)) {
    x.forEach(n => console.log(n));
  }
}
Kotlin
// Kotlin 用 is 运行时判断
fun handle(x: Any) {
  if (x is List<*>) {
    x.forEach { println(it) }
  }
}

06实战速查:场景 → 方法 + 性能注意

写业务时「想干嘛就用哪招」,以及 ArkTS 特有的性能坑。

我想做的事优先用别用 / 注意
遍历做副作用(打印/改UI)forEach / for...of别用 map(map 会白建数组)
把每个元素变个样mapmap 不改原数组,记得接返回值
按条件筛选filter—
求和 / 统计 / 分组reduce空数组务必传初始值
找某个元素 / 位置find / findIndex找不到返回 undefined / -1,要判空
是否包含includes对象按引用比较,内容相同也不等
尾部增删push / pop修改原数组
头部增删unshift / shiftO(n) 慢,高频慎用
按条件增删改splice第2参是「删几个」不是「结束索引」
排序sort((a,b)=>a-b)数字不传回调会按字符排;原地修改
拷贝一份[...arr] / arr.slice()浅拷贝(嵌套对象共享引用)
合并数组[...a, ...b] / concat—
转字符串join(sep)—
ArkTS 性能 3 条(做了强类型仍要记):① map().filter().map() 会多次遍历,大数据量用一次 for / 单个 reduce 替代;② sort / reverse / splice / push / pop / fill 都是原地修改,想保原数组先 [...arr];③ shift / unshift 是 O(n),高频头部操作考虑反转数组用 push/pop,或用专门双端结构。另外 ArkTS 禁止 for...in 遍历数组,统一用 for...of。

面向 Android 开发者 · ArkTS 数组方法按频率全解 · WorkBuddy 整理