ArkTS 数组方法全解
按使用频率讲解 ArkTS 数组方法,每个方法给出 ArkTS 示例与 Kotlin / Java 对照,并标出原地修改、越界、排序等常见坑。
01数组声明与类型
先搞清「ArkTS 数组」是什么,以及它和 Kotlin List、Java 集合的差异——这是后面所有方法的前提。
// 两种等价写法
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' }
];// 不可变 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")
)// 数组(定长)
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")
);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 遍历数组。
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) { } // 编译报错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 -> }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));undefined 而不是抛异常,所以取索引后最好配合 if (x !== undefined) 或可选链判空。另外 for...in 在 ArkTS 中被禁用(它会遍历对象可枚举属性,对数组是坑),一律用 for...of。03高频方法(每天写业务都用得到)
这 11 个方法覆盖 90% 的业务逻辑:遍历、转换、过滤、查找、增删。每个都给 ArkTS 示例 + Kotlin 对照。
高频 forEach arr.forEach((item, index) => void)
遍历数组、执行业务副作用(打印、改 UI、发请求),不返回新数组。需要返回值映射时用 map。
const list = ['a', 'b', 'c'];
list.forEach((item, i) => {
console.log(i, item);
});
// 0 a / 1 b / 2 cval 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。
const nums = [1, 2, 3];
// 每个 ×2
const doubled = nums.map(n => n * 2);
// [2, 4, 6],原 nums 不变
// 对象数组:提取字段
const names = users.map(u => u.name);val nums = listOf(1, 2, 3)
val doubled = nums.map { it * 2 } // [2,4,6]
val names = users.map { it.name }nums.map(n => n*2) 然后期待 nums 变了——它没变。要就地改就用 forEach 或重新赋值。高频 filter arr.filter((item) => boolean): T[]
按条件「筛选」元素,返回新数组。回调返回 true 才保留。
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);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。
const list: number[] = [1, 2];
const len = list.push(3); // len = 3
list.push(4, 5); // [1,2,3,4,5]
console.log(list);val list = mutableListOf(1, 2)
list.add(3) // 返回 Boolean
list.add(4); list.add(5)
// [1,2,3,4,5]list = list.push(3)。对应头部插入是 unshift(见中频)。高频 find arr.find((item) => boolean): T | undefined
返回第一个满足条件的元素;找不到返回 undefined。比 filter 取 [0] 更高效(找到就停)。
const user = users.find(u => u.id === 2);
// 找到 → {id:2,name:'Joy'}
// 没找到 → undefined(需判空!)
if (user !== undefined) {
console.log(user.name);
}// 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 做「按条件删除/替换」。
const idx = users.findIndex(u => u.id === 2);
// 2;没找到 → -1
if (idx !== -1) {
users.splice(idx, 1); // 删除该元素
}val idx = users.indexOfFirst { it.id == 2 }
// -1 表示没找到
if (idx != -1) users.removeAt(idx)高频 some arr.some((item) => boolean): boolean
是否至少有一个元素满足条件(有就停,短路)。对应 Kotlin 的 any。
const hasAdult = users.some(u => u.age >= 18);
// true / false
// 权限判断常见用法
const canEdit = roles.some(r => r === 'admin');val hasAdult = users.any { it.age >= 18 }
val canEdit = roles.any { it == "admin" }高频 every arr.every((item) => boolean): boolean
是否所有元素都满足条件。对应 Kotlin 的 all。空数组调用 every 返回 true(全称量词真空成立)。
const allValid = users.every(u => u.name.length > 0);
// 表单/校验场景常用val allValid = users.all { it.name.isNotEmpty() }高频 includes arr.includes(item): boolean
数组是否包含某元素(用严格相等 === 比较)。比 indexOf 语义更清晰,推荐用它做「是否存在」判断。
const tags = ['new', 'hot'];
if (tags.includes('hot')) { /* ... */ }
// 注意:对象按引用比较
const a = {x:1};
[{x:1}].includes(a); // false(不同引用)val tags = listOf("new", "hot")
if ("hot" in tags) { } // 或
if (tags.contains("hot")) { }[{x:1}].includes({x:1}) 是 false。要按内容判断用 some。高频 indexOf arr.indexOf(item): number
返回元素首次出现的索引,没有则返回 -1。和 includes 区别:它给你位置(常用来判断「是否存在」或「在哪」)。
const i = ['a','b','c'].indexOf('b'); // 1
const j = ['a','b'].indexOf('z'); // -1
// 从指定位置搜
const k = [1,2,1,2].indexOf(2, 2); // 3val i = listOf("a","b","c").indexOf("b") // 1
val j = listOf("a","b").indexOf("z") // -1高频 splice arr.splice(start, deleteCount?, ...items): T[]
数组的「瑞士军刀」:原地增 / 删 / 改,返回被删除的元素数组。参数(start 索引、删除个数、要插入的元素)容易记混,务必看示例。
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]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))findIndex 做「按条件删除」是高频组合。04中频方法
归约、排序、切片、合并、转字符串、首尾增删、类型转换——业务中经常出现,但没高频方法那么每天都写。
中频 reduce arr.reduce((acc, item) => acc, init)
把数组「归约」成单个值(求和、拼对象、分组)。对应 Kotlin 的 fold / reduce,Java Stream 的 reduce。
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>);val nums = listOf(1, 2, 3, 4)
val sum = nums.fold(0) { acc, n -> acc + n }
// 或 nums.sum()
val byId = users.associateBy { it.id }TypeError,且类型推断容易出问题(ArkTS 严格)。上面用 {} as Record<number, User> 给累加器标注类型。中频 sort arr.sort((a, b) => number)
原地排序,返回原数组(已排好)。回调返回负数 / 0 / 正数 表示 小于 / 等于 / 大于。不传回调会按字符串 Unicode 排——数字排序必传回调!
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] 错!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 是「结束索引(不含)」。不传参数就是整份拷贝。
const a = [1, 2, 3, 4, 5];
a.slice(1, 3); // [2, 3](不含索引3)
a.slice(2); // [3, 4, 5]
const copy = a.slice(); // 整份浅拷贝val a = listOf(1, 2, 3, 4, 5)
a.subList(1, 3) // [2,3](视图,改它会影响 a!)
a.take(3) // 前3个,新列表
a.drop(2) // 丢前2个slice 返回独立副本;Kotlin 的 subList 是原列表的视图,改视图会改原列表。做「克隆」用 [...a] 或 a.slice() 都行。中频 concat arr.concat(...others): T[]
合并多个数组 / 元素成新数组(不修改原)。等价写法还有展开运算符 [...a, ...b](更常用)。
const a = [1, 2];
const b = [3, 4];
a.concat(b); // [1,2,3,4],a 不变
[...a, ...b]; // 展开写法(推荐)
[...a, 5]; // 追加单个元素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(功能更强,可加前后缀)。
['a', 'b', 'c'].join(','); // "a,b,c"
['a', 'b'].join(' | '); // "a | b"
[1, 2, 3].join('-'); // "1-2-3"listOf("a","b","c").joinToString(",")
listOf("a","b").joinToString(" | ")
// joinToString 还能加 prefix/suffix
users.joinToString { it.name }中频 reverse arr.reverse(): T[]
原地反转,返回原数组(已反转)。要保留原数组先拷贝。
const a = [1, 2, 3];
a.reverse(); // a 变成 [3,2,1]
[...a].reverse(); // 不破坏原数组的写法val a = listOf(1, 2, 3)
a.reversed() // 返回新列表 [3,2,1]
a.asReversed() // 视图中频 unshift arr.unshift(...items): number
向头部插入元素,修改原数组,返回新 length。性能是 O(n)(要搬移所有元素),频繁头部插入慎用。
const a = [2, 3];
a.unshift(0, 1); // a = [0,1,2,3]
console.log(a.length);val a = mutableListOf(2, 3)
a.add(0, 0) // 索引0插入
a.add(0, 1)中频 pop arr.pop(): T | undefined
删除并返回尾部元素,修改原数组。空数组 pop 返回 undefined。
const a = [1, 2, 3];
const last = a.pop(); // last=3, a=[1,2]
const empty = [].pop(); // undefinedval a = mutableListOf(1, 2, 3)
val last = a.removeLast() // 3, a=[1,2]
// 或 a.removeAt(a.lastIndex)中频 shift arr.shift(): T | undefined
删除并返回头部元素,修改原数组。性能 O(n)(搬移全部),大数组频繁 shift 很慢。
const a = [1, 2, 3];
const head = a.shift(); // head=1, a=[2,3]val a = mutableListOf(1, 2, 3)
val head = a.removeFirst() // 1, a=[2,3]中频 Array.from Array.from(iterable | arrayLike): T[]
把「类数组 / 可迭代对象」转成真正的数组——常用在把 Set、Map 的 keys、arguments、DOM/ArkUI 节点集合转成可链式调用的数组。
// 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]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]Set / Map 也可用,但它们的 keys() / values() 返回迭代器,不能直接 .map。要先 Array.from(...) 转数组再链式调用。中频 flatMap arr.flatMap((item) => T[]): T[]
先 map 再「扁平一层」——每个元素映射成一个小数组,最后拼成一级数组。对应 Kotlin 的 flatMap。
const groups = [['a','b'], ['c']];
const flat = groups.flatMap(g => g); // ['a','b','c']
// 经典:一对多展开
const tags = users.flatMap(u => u.tags);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。
[1, [2, 3]].flat(); // [1,2,3]
[1, [2, [3]]].flat(2); // [1,2,3]
[1, [2, [3]]].flat(Infinity);// 彻底摊平listOf(1, listOf(2, 3)).flatten()
// [1,2,3]低频 fill arr.fill(value, start?, end?): T[]
原地用某个值填充数组(可指定区间)。常用于初始化占位数组。
const a = new Array(3).fill(0); // [0,0,0]
[1,2,3,4].fill(9, 1, 3); // [1,9,9,4]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] 更直观。
const a = [10, 20, 30];
a.at(-1); // 30(最后一个)
a.at(-2); // 20
a.at(0); // 10val 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 转数组。
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']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[]
原地把数组内部一段复制到另一段(性能优化用,不常用,易读性差)。
// 把 [2,3) 复制到从索引0开始
[1, 2, 3, 4].copyWithin(0, 2, 4);
// → [3, 4, 3, 4]// Kotlin 标准库无直接等价,
// 一般用 subList + addAll 组合实现低频 reduceRight arr.reduceRight((acc, item) => acc, init)
和 reduce 一样,但从右往左遍历。处理「从右结合」语义(如嵌套结构)时才需要。
const s = ['a','b','c'].reduceRight(
(acc, v) => acc + v, ''); // "cba"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 的空位数组。日常基本用字面量 [] 就够了。
Array.of(1, 2, 3); // [1,2,3]
Array.of(3); // [3]
new Array(3); // [empty×3] 长度3空位arrayOf(1, 2, 3) // [1,2,3]
// Kotlin 没「长度空位」歧义低频 Array.isArray Array.isArray(x): boolean
运行时判断某值是不是数组。ArkTS 静态类型强,但遇到 union / 接口入参仍可能需要运行时区分类型。
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 用 is 运行时判断
fun handle(x: Any) {
if (x is List<*>) {
x.forEach { println(it) }
}
}06实战速查:场景 → 方法 + 性能注意
写业务时「想干嘛就用哪招」,以及 ArkTS 特有的性能坑。
| 我想做的事 | 优先用 | 别用 / 注意 |
|---|---|---|
| 遍历做副作用(打印/改UI) | forEach / for...of | 别用 map(map 会白建数组) |
| 把每个元素变个样 | map | map 不改原数组,记得接返回值 |
| 按条件筛选 | filter | — |
| 求和 / 统计 / 分组 | reduce | 空数组务必传初始值 |
| 找某个元素 / 位置 | find / findIndex | 找不到返回 undefined / -1,要判空 |
| 是否包含 | includes | 对象按引用比较,内容相同也不等 |
| 尾部增删 | push / pop | 修改原数组 |
| 头部增删 | unshift / shift | O(n) 慢,高频慎用 |
| 按条件增删改 | splice | 第2参是「删几个」不是「结束索引」 |
| 排序 | sort((a,b)=>a-b) | 数字不传回调会按字符排;原地修改 |
| 拷贝一份 | [...arr] / arr.slice() | 浅拷贝(嵌套对象共享引用) |
| 合并数组 | [...a, ...b] / concat | — |
| 转字符串 | join(sep) | — |
map().filter().map() 会多次遍历,大数据量用一次 for / 单个 reduce 替代;② sort / reverse / splice / push / pop / fill 都是原地修改,想保原数组先 [...arr];③ shift / unshift 是 O(n),高频头部操作考虑反转数组用 push/pop,或用专门双端结构。另外 ArkTS 禁止 for...in 遍历数组,统一用 for...of。