Js中的稀疏数组与密集数组

更新日期: 2021-11-03 阅读: 1.6k 标签: 数组

一般而言,js中的数组是稀疏的,也就是说数组中的元素之间可以有空隙,因为一个数组其实就是一个键值映射。这篇本文解释了如何创建稀疏数组和不稀疏的数组,以及稀疏数组和密集数组之间的区别。

例如以下的js代码创建的就是一个密集数组:

var data = [3,1,6,9,2];

什么是稀疏数组呢?与密集数组相反。javascript并不强制要求数组元素是紧密相连的,即同意间隙的存在。例如以下的js代码是合法的:

 var sparse = new Array();
 sparse[0] = 0;
 sparse[3] = 3;
 alert(sparse[0]);//输出0
 alert(sparse[1]);//输出undefined


创建稀疏数组

例如以下代码创建了一个固定长度的稀疏数组

var a = new Array(3); 
a[2] = 1;
alert(a[0]);//undefined
alert(a[2]);//1

说白了js中建立稀疏数组非常easy,仅仅要你有益让数组元素之间存在间隙就可以。如

var arr = []; 
arr[0] = 0;
arr[200] = 200;


创建密集数组

什么是密集数组呢?在java和C语言中,数组是一片连续的存储空间,有着固定的长度。增加数组事实上位置是address。长度为n。那么占用的存储空间是address[0],address[1],address[2].......address[n-1]。即数组元素之间是紧密相连的,不存在空隙。

能够看到js中的数组一般都是稀疏的,一般来说稀疏数组的遍历比较麻烦。

var dense = Array.apply(null, Array(3)); 

这行代码等同于var  dense = Array(undefined, undefined, undefined) ;呵呵是不是认为非常奇怪,这样的方式跟稀疏数组没有什么区别。

看代码:

//稀疏数组
var array = new Array(3); 
array[2] = "name";
for(var a in array) {
    console.log("index=" + a + ",value=" + array[a]);
}
// 密集数组
var dense = Array.apply(null, Array(3)); 
dense[2] = "name";
for(var a in dense) {
  console.log("index=" + a + ",value=" + dense[a]);
}

能够看到确实是有区别的:稀疏数组仅仅遍历了一次(由于仅仅有一个元素),密集数组遍历了3次。


稀疏数组与密集数组区别

稀疏数组:索引不连续,数组长度大于元素个数的数组, 可以简单理解为有empty的数组;
密集数组:索引连续, 数组长度等于元素个数的数组;

稀疏数组在大多数遍历数组的方法中,遇到「empty」元素的时候,callback 函数是不会执行的,如:map, forEach, filter 等, 而且这种现象在 for...in 语句中,同样适用。

const arr = [3,,4,,5] // 稀疏数组 
arr.forEach(item => { console.log(item)}) // 输出:3,4,5

console.log(arr.map(item => {
console.log(item)
return item+1
})) // 输出:3,4,5,[4, empty, 5, empty, 6]
// 值得注意的是:稀疏数组中 「empty」元素在 map 后返回的数组中仍然为 「empty」

console.log(arr1.filter(item => item === undefined)) // 输出:[]
console.log(arr1.filter(item => item > 3 )) // 输出:[4,5]

for (var i in a) { console.log(a[i]) } // 输出:3,4,5
for (var i of a) { console.log(i) } // 输出:3,undefined,4,undefined,5

稀疏数组在访问元素的速度上比密集数组慢

const arr = new Array(200000)
arr[19999] = 88
console.time('using[]')
arr[19999]
console.timeEnd('using[]')
// using[]: 0.031982421875ms

const ddd = [...new Array(200000)]
ddd[19999] = 88
console.time('using[]')
ddd[19999]
console.timeEnd('using[]')
// using[]: 0.010009765625ms

具体原因是,对于稀疏数组 V8 引擎访问对象是使用 散列表模式的,该种模式在访问时需要计算一遍哈希值,所以会比较慢,但散列表对于空间利用来说,效率更高。而密集数组,它是申请一段连续的内存空间,访问时可以直接通过「索引」来访问,所以速度比较快;

稀疏数组在一些数组方法中与密集数组存在差异

var a = [1,,,,]
var b = new Array(5)
var c = []  

// Array.prototype.every() 和 Array.prototype.some() 

b.every(i => i === undefined); // true  
a.some(i => i === undefined); // false

前面说到遍历数组的方法会跳过「empty」元素。 所以在排除后「empty」元素后,数组内找不到 undefined 元素, some 会返回 false。空数组使用 every 时,返回 true。

// Array.prototype.find() 和 Array.prototype.findIndex()
a.findIndex(i => i === undefined) // 1
a.find(i => i === undefined) //undefined

由于find和findIndex是使用 for 循环来实现的,与forEach有所不同,所以这两种方法能遍历到「empty」元素。

// Array.prototype.includes() 
a.includes() // true
b.includes() // true
c.includes() // false
a.includes(undefined) // true
b.includes(undefined) // true  

includes() 方法表现较为特殊,大体可以总结为:当数组长度为 0 时,include 一定返回 false;当数组为稀疏数组且长度不为 0 ,且入参为空或 undefined 时,include 一定返回 true;

// Array.prototype.sort()
var sortArr = [5,,9,,1]
sortArr.sort()
console.log(sortArr) // 输出:[1, 5, 9, empty × 2]

sort 方法能够正常排序,且sort 方法不会遍历「empty」元素,但 sort 后数组的长度并不会变化,这一点与map的表现一致,map得到的数组长度也不变。

// Array.prototype.join()  
[undefined,undefined,1].join() // 输出:",,1"
[5,,9,,1].join() // 输出:"5,,9,,1"

「empty」元素仍然会被保留。Array.prototype上的其他方法,稀疏数组和密集数组表现基本一致。


总结

JavaScript中的数组并不像我们在C或java等语言中遇到的常规数组,在js中数组并非起始地址+长度构成的一片连续的地址空间。javascript中数组事实上就是个对象,仅仅只是会自己主动管理一些"数字"属性和length属性罢了。

说的更直接一点,JavaScript中的数组根本没有索引,由于索引应该是数字,而JavaScript中数组的索引事实上是字符串。arr[1]事实上就是arr["1"],给arr["1000"] = 1,arr.length也会自己主动变为1001。这些表现的根本原因就是:JavaScript中的对象就是字符串到随意值的键值对。

通过new Array(len)的方式创建的数组属于稀疏数组,稀疏数组在一些数组方法中,特别是遍历数组的方法,往往与我们预期的不太一样,如果对其不了解,容易导致问题,而且稀疏数组在创建和访问元素方面性能表现并不好,所以在平时代码中应该尽量避免使用稀疏数组。


本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/10769

相关推荐

indexOf的三种使用方法

indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。这里基本用法大家一般都清楚,一般在实际工作中常与数组的方法合用来对数组进行一些操作

关于Vue不能监听(watch)数组变化

vue无法监听数组变化的情况,但是数组在下面两种情况下无法监听:利用索引直接设置数组项时,例如arr[indexofitem]=newValue;修改数组的长度时,例如arr.length=newLength

JS计算两个数组的交集、差集、并集、补集(多种实现方式)

使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本,使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本。也不用引入其他第三方库。

js数组中改变元素的位置:互换,置顶,上移,下移

unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。splice() 方法可删除从 index 处开始的零个或多个元素

js使用数组+循环+条件实现数字转换为汉字的简单方法。

单个数字转汉字的解决方法:利用数组存储0-9的汉字、 ary.length和str.length不用多说,这是指ary数组和str字符串的长度。这里我们需要注意的是str.charAt(j)和ary[i],分别指在str这个字符串中索引为j的元素,在ary中索引为i的元素。

Js遍历数组时注意 Empty Item 的影响

这两天碰到个问题:从日志中发现一些来自 iOS 10.3 的报错「Cannot read property \\\'xxx\\\' of undefined」,定位到代码的报错位置,发现是遍历某数组时产生的报错,该数组的元素应该全都是 Object,但实际上出现了异常的元素

JS数组扁平化(flat)方法总结

需求:多维数组=>一维数组 ;flat和flatMap方法为ES2019(ES10)方法,目前还未在所有浏览器完全兼容。第四种处理:用 reduce 实现数组的 flat 方法

数组、字符串去重

今天说的数组和字符串去重呢,主要用到es6新的数据结构 Set,它类似于数组,但是成员的值都是唯一的,没有重复的值,所以活用Set来进行数组和字符串的去重。

Js数组中所有方法(超详细)

concat()把元素衔接到数组中。 every() 方法使用指定函数检测数组中的所有元素:filter()返回满足断言函数的数组元素。forEach()为数组的每一个元素调用指定函数。

[译]async-await 数组循环的几个坑

在 Javascript 循环中使用 async/ await 循环遍历数组似乎很简单,但是在将两者结合使用时需要注意一些非直观的行为。让我们看看三个不同的例子,看看你应该注意什么,以及哪个循环最适合特定用例。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!