在JavaScript中创建任意长度的零填充数组最有效的方法是什么?


当前回答

在答案中没有看到这种方法,所以这里是:

"0".repeat( 200 ).split("").map( parseFloat )

结果,您将得到长度为200的零值数组:

[ 0, 0, 0, 0, ... 0 ]

我不确定这段代码的性能,但如果您将它用于相对较小的阵列,这应该不是问题。

其他回答

使用对象表示法

var x = [];

零填充?喜欢

var x = [0,0,0,0,0,0];

填充了“undefined”。。。

var x = new Array(7);

带零的obj符号

var x = [];
for (var i = 0; i < 10; i++) x[i] = 0;

作为补充说明,如果修改Array的原型

var x = new Array();

and

var y = [];

将对原型进行修改

无论如何,我不会过分关注这个操作的效率或速度,你可能会做很多其他事情,这些事情比安装一个包含零的任意长度数组更浪费和昂贵。

截至ECMAScript2016,大型阵列有一个明确的选择。

由于这一答案在谷歌搜索中仍然排名靠前,所以这里有一个2017年的答案。

这里有一个当前的jsbench,其中有几十种流行的方法,包括迄今为止提出的许多方法。如果你找到更好的方法,请添加、分叉和分享。

我想指出,没有真正最有效的方法来创建任意长度的零填充数组。您可以优化速度,也可以优化清晰度和可维护性——根据项目的需要,两者都可以被视为更有效的选择。

在优化速度时,您需要:使用文字语法创建数组;设置长度,初始化迭代变量,并使用while循环遍历数组。这里有一个例子。

常量arr=[];arr.length=120000;设i=0;而(i<120000){arr[i]=0;i++;}

另一种可能的实施方式是:

(arr = []).length = n;
let i = 0;
while (i < n) {
    arr[i] = 0;
    i++;
}

但我强烈反对在实践中使用第二次植入,因为它不太清楚,也不允许在数组变量上保持块范围。

这比用for循环填充要快得多,比标准方法快90%左右

const arr = Array(n).fill(0);

但这种填充方法由于其清晰、简洁和可维护性,对于较小的阵列来说仍然是最有效的选择。除非您制作了大量长度为数千或更多的阵列,否则性能差异可能不会让您丧命。

其他一些重要的注意事项。大多数风格指南都建议您在使用ES6或更高版本时,如果没有非常特殊的原因,不要再使用var。对于不会被重新定义的变量使用const,对于会被重新定义变量使用let。MDN和Airbnb的风格指南是获取更多最佳实践信息的好地方。这些问题并不涉及语法,但重要的是,在搜索大量新旧答案时,熟悉JS的人必须了解这些新标准。

用预先计算的值填充数组的优雅方式

这里有另一种使用ES6的方法,到目前为止没有人提到:

> Array.from(Array(3), () => 0)
< [0, 0, 0]

它通过传递一个map函数作为Array.from的第二个参数来工作。

在上面的示例中,第一个参数分配一个由3个位置组成的数组,其中填充了未定义的值,然后lambda函数将每个位置映射到值0。

虽然Array(len).fill(0)更短,但如果您需要先进行一些计算来填充数组,它就不起作用了(我知道这个问题并没有提出,但很多人最终都在这里寻找这个问题)。

例如,如果需要包含10个随机数的数组:

> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]

它比同类产品更简洁(更优雅):

const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
    numbers[i] = Math.round(10 * Math.random());
}

此方法还可用于通过利用回调中提供的索引参数生成数字序列:

> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

额外答案:使用String repeat()填充数组

由于这个答案受到了很多关注,我也想展示一下这个很酷的技巧。虽然不如我的主要答案有用,但将介绍一个仍然不是很有名,但非常有用的Stringrepeat()方法。诀窍如下:

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

酷吧?repeat()是创建字符串的一种非常有用的方法,该字符串将原始字符串重复一定次数。之后,split()为我们创建一个数组,然后将其映射()到所需的值。按步骤分解:

> "?".repeat(10)
< "??????????"

> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]

> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]

我通常使用Uint8Array(速度惊人)。例如,创建1M个元素的零填充矢量:

  var zeroFilled = [].slice.apply(new Uint8Array(1000000))

我是一个Linux用户,一直为我工作,但有一次,一个朋友使用Mac时有一些非零元素。我以为他的机器出故障了,但我们找到了最安全的方法来修复它:

  var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000)) 

已编辑

铬25.0.1364.160

弗雷德里克·戈特利布-6.43萨姆·巴纳姆-4.83伊莱-3.68约书亚2.91马修·克鲁姆利-2.67硼-2.55艾伦·赖斯-2.11康纳斯-0.68推压器温度-0.67zertosh-错误

Firefox 20.0

艾伦·赖斯-1.85约书亚-1.82马修·克鲁姆利-1.79硼-137弗雷德里克·戈特利布-0.67萨姆·巴纳姆-0.63伊莱-0.59kagax-0.13推压器温度-0.13zertosh-错误

错过了最重要的测试(至少对我来说):Node.js测试。我怀疑它接近Chrome基准。

2013年8月添加的注释,2015年2月更新:以下2009年的答案与JavaScript的通用数组类型有关。它与ES2015中定义的较新类型数组(现在在许多浏览器中都可用)无关,如Int32Array等。还要注意,ES2015为数组和类型化数组都添加了填充方法,这可能是填充它们的最有效方法。。。

此外,它可以对某些实现的创建数组的方式产生很大的影响。特别是Chrome的V8引擎,如果它认为可以的话,它会尝试使用一个高效的、连续的内存数组,只有在必要的时候才转向基于对象的数组。


对于大多数语言,它将是预分配的,然后是零填充,如下所示:

function newFilledArray(len, val) {
    var rv = new Array(len);
    while (--len >= 0) {
        rv[len] = val;
    }
    return rv;
}

但是,JavaScript数组并不是真正的数组,它们是键/值映射,就像所有其他JavaScript对象一样,所以没有“预分配”(设置长度不会分配那么多槽来填充),也没有任何理由相信倒计时到零的好处(这只是为了使循环中的比较更快)不会被按相反顺序添加键所抵消,因为实现可能已经优化了它们对与数组相关的键的处理,理论上你通常会按顺序进行。

事实上,Matthew Crumley指出,在Firefox上,向下计数比向上计数要慢得多,我可以证实这一点——这是数组的一部分(向下循环到零仍然比向上循环到var中的一个极限更快)。显然,在Firefox上,以相反的顺序将元素添加到数组是一个缓慢的操作。事实上,结果因JavaScript实现而异(这并不奇怪)。下面是一个针对浏览器实现的快速而肮脏的测试页面(非常脏,在测试过程中不会产生结果,因此提供的反馈最少,并且会违反脚本时间限制)。我建议在测试之间刷新;如果你不这样做,FF(至少)会在重复测试中减速。

使用Array#concat的相当复杂的版本比FF上的直接初始化快,大约在1000到2000个元素数组之间。不过,在Chrome的V8引擎上,每次都是直接初始化获胜。。。

这里有一个测试:

常量测试=[{name:“倾盆大雨”,总计:0,desc:“倒计数,预减量”,函数:makeWithCountDownPre},{name:“downpost”,总计:0,desc:“倒数,递减后”,函数:makeWithCountDownPost},{name:“up”,总计:0,desc:“向上计数(正常)”,func:makeWithCountUp},{name:“向下和向上”,总计:0,desc:“向下计数(循环)和向上计数(填充)”,func:makeWithCountDownArrayUp},{name:“concat”,总计:0,desc:“凹形”,函数:makeWithConcat}];const q=sel=>document.querySelector(sel);let markup=“”;for(测试的常量{name,desc}){标记+=`<div><input type=“checkbox”id=“chk_${name}”checked><label for=“chk_${name}”>${desc}</label></div>`;}q(“#checkbox”).innerHTML=标记;q(“#btnTest”).addEventListener(“单击”,btnTestClick);函数btnTestClick(){//清除日志q(“#log”).innerHTML=“测试…”;//显示正在运行q(“#btnTest”).disabled=true;//在浏览器更新显示时暂停后运行setTimeout(btnTestClickPart2,0);}函数btnTestClickPart2(){尝试{运行测试();}捕获(e){日志(`异常:${e.message}`);}//重新启用按钮q(“#btnTest”).disabled=false;}函数getNumField(名称){const val=q(“#”+名称).value.trim();常量num=/^\d+$/.test(val)?parseInt(val):NaN;如果(isNaN(num)||num<=0){抛出新错误(`无效的${name}值${JSON.stringify(val)}`);}返回num;}函数runTests(){尝试{//清除日志q(“#log”).innerHTML=“”;const runCount=getNumField(“loops”);const length=getNumField(“长度”);//做它(我们运行runCount+1次,第一次是热身)for(让counter=0;counter<=runCount;++counter){for(测试的常量测试){如果(q(“#chk_”+测试名称)已选中){const start=Date.now();const a=测试函数(长度);const time=Date.now()-开始;如果(计数器==0){//不要计算(预热),但要检查算法是否有效常量无效=validateResult(a,长度);if(无效){日志(`<span class=error>FAILURE</span>,测试${test.name}:${invalid}`);回来}}其他{//数数这个日志(“#${counter}:${test.desc}:${time}毫秒”);test.total+=时间;}}}}for(测试的常量测试){如果(q(“#chk_”+测试名称)已选中){test.avg=测试总数/运行计数;if(typeof lowest!=“number”||最低>测试.avg){最低=测试平均值;}}}let结果=“<p>结果:”+“<br>长度:”+长度+“<br>循环:”+runCount+“</p>”;for(测试的常量测试){如果(q(“#chk_”+测试名称)已选中){结果+=`<p${最低==test.avg?“class=winner”:“”}>${test.desc},平均时间:${test.avg}毫秒</p>`;}}结果+=“<hr>”;q(“#log”).insertAdjacentHTML(“afterbegin”,结果);}捕获(e){日志(e.message);回来}}函数validateResult(a,长度){如果(a.length!=长度){return“长度错误”;}for(设n=长度-1;n>=0;--n){如果(a[n]!=0){return“索引”+n+“不为零”;}}返回未定义;}函数makeWithCountDownPre(len){const a=新数组(len);而(--len>=0){a[len]=0;}返回a;}函数makeWithCountDownPost(len){const a=新数组(len);而(len-->0){a[len]=0;}返回a;}函数makeWithCountUp(len){const a=新数组(len);for(设i=0;i<len;++i){a[i]=0;}返回a;}函数makeWithCountDownArrayUp(len){const a=新数组(len);设i=0;而(--len>=0){a[i++]=0;}返回a;}函数makeWithConcat(len){如果(长度==0){返回[];}设a=[0];设currlen=1;while(currlen<len){常量rem=长度-电流;如果(rem<currlen){a=a.oncat(a.slice(0,rem));}其他{a=混凝土(a);}currlen=a.length;}返回a;}函数日志(msg){const p=document.createElement(“p”);p.text内容=消息;q(“#log”).appendChild(p);}正文{字体系列:无衬线;}#对数p{边距:0;填充:0;}.错误{颜色:红色;}.获胜者{颜色:绿色;}<div><label for='textLength'>长度:</label><input type='text'id='Length'value='1000'><br><label for='xtLoops'>循环:</label><input type='text'id='Loops'value='100000'><div id='checkboxs'></div><br><input-type='button'id='btnTest'value='Test'><小时><div id='log'></div></div>