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


当前回答

return Array(数量).fill(1).map(n=>return n*Math.abs(~~(Math.random()*(1000-1+1))+1));

一行。

其他回答

如果您需要在代码执行期间创建许多不同长度的零填充数组,我发现实现这一点的最快方法是使用本主题中提到的方法之一创建一次零数组,其长度永远不会超过,然后根据需要分割该数组。

例如(使用上面所选答案中的函数初始化数组),创建长度为maxLength的零填充数组,作为需要零数组的代码可见的变量:

var zero = newFilledArray(maxLength, 0);

现在,每当您需要长度为requiredLength<maxLength:

zero.slice(0, requiredLength);

在执行代码期间,我创建了数千次零填充数组,这大大加快了这个过程。

const item = 0
const arr = Array.from({length: 10}, () => item)

常量项=0constarr=Array.from({length:42},()=>项)控制台日志('arr',arr)

我最快的功能是:

function newFilledArray(len, val) {
    var a = [];
    while(len--){
        a.push(val);
    }
    return a;
}

var st = (new Date()).getTime();
newFilledArray(1000000, 0)
console.log((new Date()).getTime() - st); // returned 63, 65, 62 milliseconds

使用本机push和shift向数组中添加项比声明数组范围并引用每个项来设置其值快得多(大约10倍)。

fyi:在firebug(firefox扩展)中运行时,第一个循环一直在倒计时,我总是会得到更快的时间。

var a = [];
var len = 1000000;
var st = (new Date()).getTime();
while(len){
    a.push(0);
    len -= 1;
}
console.log((new Date()).getTime() - st); // returned 863, 894, 875 milliseconds
st = (new Date()).getTime();
len = 1000000;
a = [];
for(var i = 0; i < len; i++){
    a.push(0);
}
console.log((new Date()).getTime() - st); // returned 1155, 1179, 1163 milliseconds

我很想知道T.J.Crowder对此有何看法?:-)

匿名函数:

(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]

用for循环稍微短一点:

(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]

适用于任何对象,只需更改this.push()中的内容即可。

您甚至可以保存函数:

function fill(size, content) {
  for(;size--;this.push(content));
  return this;
}

使用以下方法调用:

var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']

将元素添加到已存在的数组:

var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']

性能:http://jsperf.com/zero-filled-array-creation/25

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>