JavaScript 数据结构与算法中的数组(Array)和列表(List)是两种基本的数据结构,它们在存储和操作数据时起着重要的作用。数组和列表都支持常见的操作,如插入、删除、查找和遍历等,但它们在内存分配、插入删除操作的效率等方面有所不同。了解数组和列表的特性以及它们的适用场景,对于编写高效的 JavaScript 程序以及理解复杂的算法和数据结构都至关重要。
数组
数组的标准定义是:
一个存储元素的线性集合(collection),元素可以通过索引来任意存取,索引通常是数字,用来计算元素之间存储位置的偏移量。
JavaScript 数组用于在单一变量中存储多个值。
- 使用数组文本是创建 JavaScript 数组最简单的方法。(
推荐
)
var array-name = [item1, item2, ...];
var cars = ["Saab", "Volvo", "BMW"];
- 使用 JavaScript 关键词 new 创建数组。(
不推荐
)
var cars = new Array("Saab", "Volvo", "BMW");
push
向数组末尾添加一个或多个元素,并返回新的长度
const arr = [1, 2, 3];
arr.push(4);
// 现在 arr 为 [1, 2, 3, 4]
unshift
向数组的开头添加一个或多个元素,并返回新的长度
const arr = [2, 3];
arr.unshift(1);
// 现在 arr 为 [1, 2, 3]
pop
移除并返回数组的最后一个元素
const arr = [1, 2, 3];
const poppedElement = arr.pop(); // poppedElement = 3
// 现在 arr 为 [1, 2]
shift
移除并返回数组的第一个元素
const arr = [1, 2, 3];
const shiftedElement = arr.shift(); // shiftedElement = 1
// 现在 arr 为 [2, 3]
splice
从数组中添加/删除元素,获得一个新数组
const arr = [1, 2, 3, 4, 5];
arr.splice(2, 1); // 从索引2开始删除1个元素
// 现在 arr 为 [1, 2, 4, 5]
var nums = [1,2,3,7,8,9];
var newElements = [4,5,6];
nums.splice(3,0,newElements);
print(nums); // 1,2,3,4,5,6,7,8,9
slice
返回数组的一部分,不修改原数组
const arr = [1, 2, 3, 4, 5];
const slicedArr = arr.slice(2, 4); // 从索引2到索引4(不包括)的元素
// slicedArr 为 [3, 4], arr 仍为 [1, 2, 3, 4, 5]
concat
将两个或多个数组合并成一个新数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const mergedArr = arr1.concat(arr2);
// mergedArr 为 [1, 2, 3, 4]
every
检测数组中的所有元素是否都符合指定条件
const arr = [1, 2, 3, 4, 5];
// 检查数组中的所有元素是否都大于0
const allGreaterThanZero = arr.every(element => element > 0);
// allGreaterThanZero 为 true,因为所有元素都大于0
// 检查数组中的所有元素是否都是偶数
const allEvenNumbers = arr.every(element => element % 2 === 0);
// allEvenNumbers 为 false,因为数组中有一个奇数(1)
some
测试数组的至少一个元素是否通过了指定函数的测试
const arr = [1, 2, 3];
const hasEven = arr.some(item => item % 2 === 0);
// hasEven 为 true
forEach
遍历数组的每个元素并执行提供的函数
const arr = [1, 2, 3];
arr.forEach(item => {
console.log(item);
});
// 输出: 1
// 输出: 2
// 输出: 3
map
遍历数组的每个元素并返回一个新数组,新数组的元素由回调函数的返回值组成
const arr = [1, 2, 3];
const doubledArr = arr.map(item => item * 2);
// doubledArr 为 [2, 4, 6]
filter
筛选出数组中满足条件的元素,并将这些元素放入一个新数组中返回
const arr = [1, 2, 3, 4, 5];
// 筛选出数组中的所有偶数
const evenNumbers = arr.filter(element => element % 2 === 0);
// evenNumbers 为 [2, 4]
// 筛选出数组中大于2的元素
const greaterThanTwo = arr.filter(element => element > 2);
// greaterThanTwo 为 [3, 4, 5]
reduce
将数组中的元素通过指定的函数进行累积计算,最终返回一个值
const arr = [1, 2, 3, 4, 5];
// 计算数组中所有元素的和
const sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
// sum 为 15 (1 + 2 + 3 + 4 + 5)
// 找出数组中的最大值
const max = arr.reduce((accumulator, currentValue) => Math.max(accumulator, currentValue), arr[0]);
// max 为 5
reduceRight()
类似于 reduce() 方法,但是从数组的末尾开始执行
const arr = ['a', 'b', 'c'];
const reversedStr = arr.reduceRight((accumulator, currentValue) => accumulator + currentValue);
// reversedStr 为 'cba'
for...of
很方便地迭代数组中的元素,而无需手动管理索引
const arr = [1, 2, 3, 4, 5];
for (const element of arr) {
console.log(element);
}
// 输出: 1 2 3 4 5
@@iterator
返回一个包含数组键值对的迭代器对象,可以通过同步调用得到数组元素的键值对
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let iterator = numbers[Symbol.iterator]()
iterator.next().value // 1
iterator.next().value // 2
iterator.next().value // 3
iterator.next().value // 4
iterator.next().value // 5
entries
返回数组的键值对的迭代器
const arr = ['a', 'b', 'c'];
const entry = arr.entries();
for (const [index, value] of entry) {
console.log(index, value); // 0 'a', 1 'b', 2 'c'
}
keys
返回数组的索引/键的迭代器
const arr = ['a', 'b', 'c'];
const keys = arr.keys();
for (const key of keys) {
console.log(key); // 0, 1, 2
}
values
返回数组的值的迭代器
const arr = ['a', 'b', 'c'];
const values = arr.values();
for (const value of values) {
console.log(value); // 'a', 'b', 'c'
}
from
通过类数组对象或可迭代对象创建一个新的数组实例
const arr = Array.from('hello');
// arr 为 ['h', 'e', 'l', 'l', 'o']
of
一个静态方法,用于创建一个新的数组实例,该数组包含任意数量的参数
const arr = Array.of(1, 2, 3, 4, 5);
// arr 为 [1, 2, 3, 4, 5]
const emptyArray = Array.of();
// emptyArray 为 []
fill
用一个固定值填充数组的所有元素
const arr = [1, 2, 3, 4, 5];
arr.fill(0, 2, 4); // 从索引2开始到索引4之前的位置填充0
// arr 变为 [1, 2, 0, 0, 5]
copyWithin
复制数组的一部分到同一数组中的另一个位置,并返回修改后的数组
// array.copyWithin(target, start, end)
// 目标位置索引 `target`:指的是要复制到的位置。
// 复制的起始位置索引 `start`:指的是要复制的起始位置。
// 复制的结束位置索引 `end`:指的是要复制的结束位置。
var array = [1, 2, 3, 4, 5];
// 将从索引 0 开始的元素复制到索引 3 处,覆盖后面的元素
array.copyWithin(3, 0);
console.log(array); // 输出: [1, 2, 3, 1, 2]
// 目标位置索引是 `3`,表示我们要将复制的元素粘贴到数组中索引为 `3` 的位置。
// 起始位置索引是 `0`,表示我们从数组的开头开始复制元素。
// 没有提供 `end` 参数,因此默认为数组末尾。
const arr = [1, 2, 3, 4, 5];
arr.copyWithin(0, 3, 4); // 从索引3开始复制到索引4之前的位置
// arr 变为 [4, 2, 3, 4, 5]
reverse
反转数组中元素的顺序,修改原数组
const arr = [1, 2, 3, 4, 5];
arr.reverse();
// arr 变为 [5, 4, 3, 2, 1]
sort
对数组元素进行排序,默认按照字符串 Unicode 顺序排序,修改原数组
const arr = [3, 1, 4, 1, 5, 9, 2, 6];
arr.sort((a, b) => a - b); // 数字升序排序
// arr 变为 [1, 1, 2, 3, 4, 5, 6, 9]
var nums = [3,1,2,100,4,200];
nums.sort();
print(nums); // 1,100,2,200,3,4
function compare(num1, num2) {
return num1 - num2;
}
var nums = [3,1,2,100,4,200];
nums.sort(compare);
print(nums); // 1,2,3,4,100,200
indexOf
在数组中查找指定元素并返回其第一次出现的索引,如果数组中不存在该元素,则返回 -1
const arr = [1, 2, 3, 4, 5];
const index = arr.indexOf(3);
// index 为 2
const notFoundIndex = arr.indexOf(6);
// notFoundIndex 为 -1,因为数组中不存在元素 6
const fromIndex = arr.indexOf(2, 2);
// fromIndex 为 -1,因为从索引 2 开始后面没有元素等于 2
lastIndexOf
从数组的末尾开始搜索指定的元素,并返回它在数组中最后一次出现的索引。如果数组中不存在该元素,则返回 -1
const arr = [1, 2, 3, 4, 3, 2, 1];
const lastIndex = arr.lastIndexOf(3);
// lastIndex 为 4,因为元素 3 在索引 4 处最后一次出现
const notFoundIndex = arr.lastIndexOf(5);
// notFoundIndex 为 -1,因为数组中不存在元素 5
const fromIndex = arr.lastIndexOf(2, 3);
// fromIndex 为 1,因为从索引 3 开始向前搜索,找到元素 2 的最后一次出现在索引 1 处
find
查找数组中第一个满足条件的元素,并返回该元素。如果找到满足条件的元素,则返回该元素;如果找不到,则返回 undefined
const arr = [1, 2, 3, 4, 5];
const found = arr.find(element => element > 2);
// found 为 3,因为 3 是数组中第一个大于 2 的元素
const notFound = arr.find(element => element > 5);
// notFound 为 undefined,因为数组中没有大于 5 的元素
findIndex
返回数组中满足提供的测试函数的第一个元素的索引,否则返回 -1
const arr = [1, 2, 3, 4, 5];
const foundIndex = arr.findIndex(item => item > 3);
// foundIndex 为 3
includes
判断数组是否包含指定的元素,返回布尔值
const arr = [1, 2, 3, 4, 5];
const hasThree = arr.includes(3);
// hasThree 为 true
toString
将数组转换为字符串,返回一个字符串表示数组
const arr = [1, 2, 3];
const str = arr.toString();
// str 为 "1,2,3"
join
将数组中所有元素连接成一个字符串
const arr = [1, 2, 3];
const str = arr.join('-');
// str 为 "1-2-3"
valueOf
和 toString 类似,将数组作为字符串返回
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May'];
document.getElementById("result").innerHTML = months.valueOf();
flat()
将嵌套数组扁平化为指定深度的新数组
const arr = [1, [2, [3, 4]]];
const flatArr = arr.flat();
// flatArr 为 [1, 2, [3, 4]]
flatMap()
首先使用映射函数映射每个元素,然后将结果扁平化为一个新数组
const arr = [1, 2, 3];
const doubledArr = arr.flatMap(item => [item, item * 2]);
// doubledArr 为 [1, 2, 2, 4, 3, 6]
isArray()
判断给定参数是否为数组
const arr = [1, 2, 3];
const isArray = Array.isArray(arr); // true
列表
列表介绍:
- 列表是一组有序的数据、一种线性数据结构
- 每个列表中的数据项称为元素
- 元素的数量受内存控制(即:元素不是很多)
- 不包含任何元素的列表称为空列表
// 创建 List 构造函数
function List() {
this.listSize = 0 // 列表元素个数
this.pos = 0 // 列表当前位置
this.dataStore = [] // 初始化一个空数组用来保存列表元素
this.clear = clear // 清空列表中的所有元素
this.find = find // 查找元素
this.toString = toString // 返回列表字符串形式
this.insert = insert // 在现有元素后插入新元素
this.append = append // 在列表元素末尾增加新元素
this.remove = remove // 从列表中删除元素
this.front = front // 从列表的当前位置移动到第一元素
this.end = end // 从列表的当前位置移动到最后一个位置
this.prev = prev // 将当前位置前移一个位置
this.next = next // 将当前位置后移一个位置
this.length = length // 列表包含元素的个数
this.currPos = currPos // 返回列表当前位置方法
this.moveTo = moveTo // 将当前位置移动到指定位置
this.getElement = getElement // 显示当前的元素
this.contains = contains // 是否包含该元素
}
function append(element) {
this.dataStore[this.listSize++] = element
}
function find(element) {
// 此处的++应置于之前 以便返回正确的索引
for (let i = 0; i < this.dataStore.length; ++i) { // ++i 和 i++ 区别
if (this.dataStore[i] == element) return i
}
return -1
}
function remove(element) {
const foundAt = this.find(element)
if (foundAt > -1) {
this.dataStore.splice(foundAt, 1) // splice 和 slice 区别,各自使用方法
--this.listSize
console.log(this.dataStore, this.listSize)
return
}
return false
}
function length() {
// 列表自己维护的一个变量
return this.listSize
}
function toString() {
return this.dataStore
}
function insert(element, afterElemnt) {
const insertPos = this.find(afterElemnt)
if (insertPos > -1) {
this.dataStore.splice(insertPos + 1, 0, element)
++this.listSize
return true
}
return false
}
function clear() {
delete this.dataStore
this.dataStore = [] // 创建一个空数组 this.dataStore.length = 0 会你报错 this.dataStore是 undefined 的length 不存在
this.listSize = this.pos = 0
}
function contains(element) {
for (let i = 0; i < this.dataStore.length; ++i) {
if (this.dataStore[i] == element) {
return true
}
}
return false
}
// 遍历列表
function front() {
this.pos = 0
}
function end() {
this.pos = this.listSize - 1
}
function prev() {
if (this.pos > 0) --this.pos
}
function next() {
if (this.pos < this.listSize) ++this.pos
}
function currPos() {
return this.pos
}
function moveTo(position) {
this.pos = position
}
function getElement() {
return this.dataStore[this.pos]
}
适用场景
数组适用场景:
- 需要快速访问元素:如果需要通过索引快速访问元素,数组是一个很好的选择。
- 固定大小的集合:当需要存储固定数量的元素时,例如存储一组数据的情况,可以使用数组。
- 数值计算:数组通常用于存储数值,因为它们支持快速的数值计算和访问。
列表适用场景:
- 动态数据集合:当需要在运行时动态添加或删除元素时,列表是一个很好的选择。
- 数据结构的构建:许多常见的数据结构,如链表、栈和队列,都可以使用列表来实现。
- 对元素顺序敏感:如果需要保持元素的插入顺序,并且不关心访问元素的效率,可以使用列表。
- 不确定集合大小:当集合的大小不确定或需要频繁的大小变化时,列表比数组更灵活。
最后
脸皮厚的作者想申请一个免费的 star
,请各位领导批准!
请跳转 GitHub 地址,也大家请批评指导!