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

评论

关于此的一些实际数据:jsperf.com/zeroarrayjs

ES6填充允许本地执行此操作。

arr = new Array(length + 1).joint(character).split('');

更新2016:此处是另一个自定义基准测试:jsfiddle.net/basickarl/md5z0Lqq

让我= 0; Array.from(Array(10),()=> i ++);

#1 楼

ES6引入了Array.prototype.fill。可以这样使用:

new Array(len).fill(0);


不知道它是否很快,但是我喜欢它,因为它很简短并且可以自我描述。

它仍然不在IE(检查兼容性)中,但是有一个polyfill可用。

评论


填充很快。 new Array(len)非常慢。 (arr = [])。length = len; arr.fill(0);关于在任何地方都可以看到的最快的解决方案...

–皮条客Trizkit
2015年9月22日在16:08



@PimpTrizkit arr = Array(n)和(arr = [])。length = n根据规范具有相同的行为。在某些实现中,速度可能会更快,但我认为没有太大的区别。

– Oriol
2015年9月22日下午16:39

...我会承认我错过了这一部分...当我在测试中添加第二行时... arr.fill(0)...一切都会改变。现在,在大多数情况下,使用新的Array()更快,除非您获得的数组大小> 100000 ...然后您可以开始看到速度再次提高。但是,如果您实际上不必用零预填充它,则可以使用空数组的标准伪造。然后(arr = [])。length = x在我的测试用例中大多数时候都非常快。

–皮条客Trizkit
2015年9月23日在0:15



请注意,要遍历数组(例如map或forEach),必须设置值,否则将跳过这些索引。您设置的值可以是您想要的任何值,甚至可以是未定义的值。示例:尝试new Array(5).forEach(val => console.log('hi')); vs新的Array(5).fill(undefined).forEach(val => console.log('hi'));。

– ArneHugo
16 Jun 2'在11:03



我看到当数组变得很大时,fill()比for循环要慢很多:jsperf.com/zero-filling-large-arrays新Array(n)和a = []之间没有显着差异。长度= n

– delucis
18年11月11日在18:56



#2 楼

尽管这是旧线程,但我想在其中加2美分。不知道这有多慢/快,但这只是一个快速的班轮。这是我的工作:

如果要预填数字:

Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]


如果要预填数字字符串:

Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi")
// ["hi", "hi", "hi"]



其他答案建议:

new Array(5+1).join('0').split('')
// ["0", "0", "0", "0", "0"]


,但如果需要0(数字)而不是“ 0”(字符串中为零),则可以执行以下操作:

new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]


评论


好答案!你能用Array.apply(null,new Array(5))。map(...)解释这个技巧吗?原因只是做(new Array(5))。map(...)不能按规范说明

–德米特里·帕什赫维奇(Dmitry Pashkevich)
13年7月3日在11:03

(顺便说一句,我们真的不需要新的)当您执行Array(5)时,您正在创建一个看起来像这样的对象:{长度:5,__proto__:Array.prototype}-尝试console.dir(Array( 5))。请注意,它没有任何属性0、1、2等。但是,将其应用于Array构造函数时,就像说Array(未定义,未定义,未定义,未定义,未定义)。您会得到一个看起来像{长度:5,0:未定义,1:未定义...}的对象。 map在属性0,1等上起作用,这就是为什么您的示例不起作用,但是当您使用apply时它起作用的原因。

–zertosh
13年7月3日在15:58

.apply的第一个参数实际上是您想要的。出于这些目的,这无关紧要-我们只在乎.apply的参数扩展“功能”-因此它可以是任何值。我喜欢null,因为它很便宜,您可能不想使用{}或[],因为您无缘无故地实例化一个对象。

–zertosh
13年4月4日,0:15



同样,使用大小+赋值进行初始化要比推送更快。参见测试用例jsperf.com/zero-fill-2d-array

–科林
14年4月15日在15:05

Array.apply(null,Array(5))。map(x => 0)呢?有点短!

– Arch Linux Tux
16 Mar 7 '16 at 21:34

#3 楼

简而言之
最快的解决方案
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
最短的(方便的)解决方案(小型阵列慢3倍,大型阵列慢3倍(在Firefox上最慢))
Array(n).fill(0)

详情
今天2020.06.09我在浏览器Chrome 83.0,Firefox 77.0和Safari 13.1上对macOS High Sierra 10.13.6进行了测试。我测试了两个测试用例的选择解决方案

小数组-包含10个元素-您可以在此处执行测试

大数组-包含1M个元素-您可以在此处执行测试
/>

结论

基于new Array(n)+for(N)的解决方案是适用于小型阵列和大型阵列(Chrome除外,但仍然非常快)的最快解决方案,因此建议将其作为快速解决方案跨浏览器解决方案
基于new Float32Array(n)(I)的解决方案返回非典型数组(例如,您无法在其上调用push(..)),因此我不将其结果与其他解决方案进行比较-但是该解决方案的速度比其他解决方案快10-20倍适用于所有浏览器上的大型阵列
基于for(L,M,N,O)的解决方案非常快
基于fill(B,C)的解决方案在Chrome和Safari上很快在Firefox上使用大型阵列。对于小型阵列,它们中等速度
基于Array.apply(P)的解决方案对于大型阵列会引发错误


 function P(n) {
  return Array.apply(null, Array(n)).map(Number.prototype.valueOf,0);
}


try {
  P(1000000);
} catch(e) { 
  console.error(e.message);
} 






代码和示例
下面的代码提供了用于测量的解决方案



 function A(n) {
  return [...new Array(n)].fill(0);
}

function B(n) {
  return new Array(n).fill(0);
}

function C(n) {
  return Array(n).fill(0);
}

function D(n) {
  return Array.from({length: n}, () => 0);
}

function E(n) {
  return [...new Array(n)].map(x => 0);
}

// arrays with type

function F(n) {
  return Array.from(new Int32Array(n));
}

function G(n) {
  return Array.from(new Float32Array(n));
}

function H(n) {
  return Array.from(new Float64Array(n)); // needs 2x more memory than float32
}

function I(n) {
  return new Float32Array(n); // this is not typical array
}

function J(n) {
  return [].slice.apply(new Float32Array(n));
}

// Based on for

function K(n) {
  let a = [];
  a.length = n;
  let i = 0;
  while (i < n) {
    a[i] = 0;
    i++;
  }
  return a;
}

function L(n) {
  let a=[]; for(let i=0; i<n; i++) a[i]=0;
  return a;
}

function M(n) {
  let a=[]; for(let i=0; i<n; i++) a.push(0);
  return a;
}

function N(n) {
  let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
  return a;
}

function O(n) {
  let a = new Array(n); for (let i=n; i--;) a[i] = 0;
  return a;
}

// other

function P(n) {
  return Array.apply(null, Array(n)).map(Number.prototype.valueOf,0);
}

function Q(n) {
  return "0".repeat( n ).split("").map( parseFloat );
}

function R(n) {
  return new Array(n+1).join('0').split('').map(parseFloat)
}


// ---------
// TEST
// ---------

[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R].forEach(f => {
  let a = f(10); 
  console.log(`${f.name} length=${a.length}, arr[0]=${a[0]}, arr[9]=${a[9]}`)
}); 

 This snippets only present used codes 




Chrome的结果示例


评论


刚刚在Chrome 77上运行了一些测试,并且push()的简单循环比fill()快两倍...我想知道fill()的哪些细微副作用会阻止更有效的实现?

–埃里克·格兰奇
19-10-8在10:05

@EricGrange我更新答案-在底部,我用您的主张更新指向Benchamrk的链接:情况P让a = []; for(i = n; i--;)a.push(0); -但它比fill(0)慢4倍-因此,在这种情况下,我什至不会更新图片。

–卡米尔(KamilKiełczewski)
19-10-9在14:10



不错的测量。分析:G很慢,因为每次迭代都会调整数组的大小,而调整大小意味着进行新的内存分配。 A,B,M快速,因为调整仅执行一次。 +1

–罗兰
3月5日,11:41

@Roland我想你是说N而不是M?

–Cyber​​Mew
11月7日14:20

for循环(N)仅比Safari中的.fill(C)快1.835,值得注意的是,当我现在运行它(6个月后)时,其差异已降至1.456x。因此,对于Safari而言,最快的解决方案(N)仅比最短和最简单的版本快45%。道德:坚持最短和最简单的版本(对于大多数情况,即使不是所有情况)。它通过更快的读取速度,更易于维护的方式节省了昂贵的开发人员时间,并且随着时间和CPU速度的提高,其回报越来越多,而无需进行额外的维护。

–镁
昨天



#4 楼

用预先计算的值填充数组的一种优雅方法

这是迄今为止尚无人提及的使用ES6的另一种方法:

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


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

在上面的示例中,第一个参数分配了一个由3个位置组成的数组,其中填充了值undefined,然后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());
}


也可以利用此方法通过利用回调中提供的index参数来生成数字序列:

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



奖励答案:使用字符串repeat()填充数组


由于这个答案引起了很多关注,我也想展示一下很酷的把戏。尽管不如我的主要回答有用,将介绍仍然不是很了解,但是非常有用的String repeat()方法。这是窍门:

> "?".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]


评论


该帖子中有许多客厅技巧,但希望这些技巧不会达到生产代码:)

–埃里克·格兰奇
19-10-8在10:01

尽管绝对不需要在生产中使用重复技巧,但Array.from()很好:-)

– Lucio Paiva
19-10-14在0:27

并非如此,Array.from()基本上是在创建一个数组,然后使用map()对其进行迭代,在每个项目上调用一个函数以创建一个新数组,然后丢弃第一个数组...对于一个小的数组,这可能是无害的,对于更大的数组,这是导致人们称浏览器为“内存猪”的一种模式:)

–埃里克·格兰奇
19-10-15在9:07

肯定,与大型阵列打交道的人应该比这更好地知道。但是,对于普通应用程序,创建可以立即处理的常规大小的辅助数组(最多10k元素)是完全可以的(花费的时间与避免额外数组创建所花费的时间相同-已通过最新的Chrome测试)。对于这种情况,可读性比微小的性能优化更为重要。关于O(n)时间,如果您需要为每个元素(我的答案的主要主题)计算不同的值,则很有必要。这次讨论非常有趣,很高兴您提出了!

– Lucio Paiva
19-10-15在15:00

#5 楼

已经提到的ES 6填充方法很好地解决了这一问题。迄今为止,大多数现代桌面浏览器已经支持所需的Array原型方法(Chromium,FF,Edge和Safari)[1]。您可以在MDN上查找详细信息。一个简单的用法示例是

a = new Array(10).fill(0);


鉴于当前的浏览器支持,除非您确定观众使用的是现代桌面浏览器,否则应谨慎使用此功能。

评论


如果填写引用类型,则所有引用类型都是相同的。新的Array(10).fill(null).map(()=> [])将是解决此问题的一种简洁方法(最初烧死了我哈哈)

– John Culviner
16-2-17在18:17



2016年更新:此方法将所有其他东西都炸掉了,请单击此处以获取基准:jsfiddle.net/basickarl/md5z0Lqq

–basickarl
16 Jul 26'14:09



这将适用于数组。 a = Array(10).fill(null).map(()=> {return [];});

–安德鲁(Andrew)
16年8月7日在17:54

@AndrewAnthonyGerst Terser:a = Array(10).fill(0).map(_ => []);

– Phrogz
17年7月22日在5:16

#6 楼

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

此外,它对某些实现有很大的不同您创建数组。特别是Chrome的V8引擎会尝试使用高效的连续内存数组,如果可能的话,仅在必要时才转移到基于对象的数组。


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

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


但是,JavaScript数组并不是真正的数组,它们是键/值就像所有其他JavaScript对象一样进行映射,因此无需“预分配”操作(设置长度不会分配要填充的太多插槽),也没有任何理由相信递减为零的好处(即只是为了使循环中的比较快速)在实现可能已经很好地优化了它们与数组相关的键的处理时,并没有按相反的顺序添加键,这不胜过。 br />
实际上,马修·克鲁姆利(Matthew Crumley)指出,在Firefox上向下计数比向上计数明显要慢,这是我可以确认的结果-这是它的数组部分(向下循环到零仍然比循环到变量中的极限要快)。显然,以相反的顺序将元素添加到数组是Firefox上的缓慢操作。实际上,JavaScript实现的结果差异很大(这并不奇怪)。这是一个用于浏览器实现的快速且肮脏的测试页面(如下)(非常肮脏,在测试过程中不会产生,因此提供的反馈最少,并且会违反脚本时间限制)。我建议在测试之间进行刷新。如果您不这样做,FF(至少)会减慢重复测试的速度。

使用Array#concat的相当复杂的版本要比FF上的直接初始化快,因为FF介于1,000和2,000之间。不过,在Chrome的V8引擎上,每次都会直接获得胜利...

这是测试页(实时副本):

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Zero Init Test Page</title>
<style type='text/css'>
body {
    font-family:    sans-serif;
}
#log p {
    margin:     0;
    padding:    0;
}
.error {
    color:      red;
}
.winner {
    color:      green;
    font-weight:    bold;
}
</style>
<script type='text/javascript' src='prototype-1.6.0.3.js'></script>
<script type='text/javascript'>
var testdefs = {
    'downpre':  {
        total:  0,
        desc:   "Count down, pre-decrement",
        func:   makeWithCountDownPre
    },
    'downpost': {
        total:  0,
        desc:   "Count down, post-decrement",
        func:   makeWithCountDownPost
    },
    'up':       {
        total:  0,
        desc:   "Count up (normal)",
        func:   makeWithCountUp
    },
    'downandup':  {
        total:  0,
        desc:   "Count down (for loop) and up (for filling)",
        func:   makeWithCountDownArrayUp
    },
    'concat':   {
        total:  0,
        desc:   "Concat",
        func:   makeWithConcat
    }
};

document.observe('dom:loaded', function() {
    var markup, defname;

    markup = "";
    for (defname in testdefs) {
        markup +=
            "<div><input type='checkbox' id='chk_" + defname + "' checked>" +
            "<label for='chk_" + defname + "'>" + testdefs[defname].desc + "</label></div>";
    }
    $('checkboxes').update(markup);
    $('btnTest').observe('click', btnTestClick);
});

function epoch() {
    return (new Date()).getTime();
}

function btnTestClick() {

    // Clear log
    $('log').update('Testing...');

    // Show running
    $('btnTest').disabled = true;

    // Run after a pause while the browser updates display
    btnTestClickPart2.defer();
}
function btnTestClickPart2() {

    try {
        runTests();
    }
    catch (e) {
        log("Exception: " + e);
    }

    // Re-enable the button; we don't yheidl
    $('btnTest').disabled = false;
}

function runTests() {
    var start, time, counter, length, defname, def, results, a, invalid, lowest, s;

    // Get loops and length
    s = $F('txtLoops');
    runcount = parseInt(s);
    if (isNaN(runcount) || runcount <= 0) {
        log("Invalid loops value '" + s + "'");
        return;
    }
    s = $F('txtLength');
    length = parseInt(s);
    if (isNaN(length) || length <= 0) {
        log("Invalid length value '" + s + "'");
        return;
    }

    // Clear log
    $('log').update('');

    // Do it
    for (counter = 0; counter <= runcount; ++counter) {

        for (defname in testdefs) {
            def = testdefs[defname];
            if ($('chk_' + defname).checked) {
                start = epoch();
                a = def.func(length);
                time = epoch() - start;
                if (counter == 0) {
                    // Don't count (warm up), but do check the algorithm works
                    invalid = validateResult(a, length);
                    if (invalid) {
                        log("<span class='error'>FAILURE</span> with def " + defname + ": " + invalid);
                        return;
                    }
                }
                else {
                    // Count this one
                    log("#" + counter + ": " + def.desc + ": " + time + "ms");
                    def.total += time;
                }
            }
        }
    }

    for (defname in testdefs) {
        def = testdefs[defname];
        if ($('chk_' + defname).checked) {
            def.avg = def.total / runcount;
            if (typeof lowest != 'number' || lowest > def.avg) {
                lowest = def.avg;
            }
        }
    }

    results =
        "<p>Results:" +
        "<br>Length: " + length +
        "<br>Loops: " + runcount +
        "</p>";
    for (defname in testdefs) {
        def = testdefs[defname];
        if ($('chk_' + defname).checked) {
            results += "<p" + (lowest == def.avg ? " class='winner'" : "") + ">" + def.desc + ", average time: " + def.avg + "ms</p>";
        }
    }
    results += "<hr>";
    $('log').insert({top: results});
}

function validateResult(a, length) {
    var n;

    if (a.length != length) {
        return "Length is wrong";
    }
    for (n = length - 1; n >= 0; --n) {
        if (a[n] != 0) {
            return "Index " + n + " is not zero";
        }
    }
    return undefined;
}

function makeWithCountDownPre(len) {
    var a;

    a = new Array(len);
    while (--len >= 0) {
        a[len] = 0;
    }
    return a;
}

function makeWithCountDownPost(len) {
    var a;

    a = new Array(len);
    while (len-- > 0) {
        a[len] = 0;
    }
    return a;
}

function makeWithCountUp(len) {
    var a, i;

    a = new Array(len);
    for (i = 0; i < len; ++i) {
        a[i] = 0;
    }
    return a;
}

function makeWithCountDownArrayUp(len) {
    var a, i;

    a = new Array(len);
    i = 0;
    while (--len >= 0) {
        a[i++] = 0;
    }
    return a;
}

function makeWithConcat(len) {
    var a, rem, currlen;

    if (len == 0) {
        return [];
    }
    a = [0];
    currlen = 1;
    while (currlen < len) {
        rem = len - currlen;
        if (rem < currlen) {
            a = a.concat(a.slice(0, rem));
        }
        else {
            a = a.concat(a);
        }
        currlen = a.length;
    }
    return a;
}

function log(msg) {
    $('log').appendChild(new Element('p').update(msg));
}
</script>
</head>
<body><div>
<label for='txtLength'>Length:</label><input type='text' id='txtLength' value='10000'>
<br><label for='txtLoops'>Loops:</label><input type='text' id='txtLoops' value='10'>
<div id='checkboxes'></div>
<br><input type='button' id='btnTest' value='Test'>
<hr>
<div id='log'></div>
</div></body>
</html>


评论


由于您仅访问元素(而不是删除它们)并且您已经预先分配了,因此不确定向后填充是否在这里很重要。我错了吗?

–三联画
09年8月18日在20:52

向后填充的点不是特别与数组有关,而是与一段时间内的转义条件有关-假0非常有效地终止了循环

– annakata
09年8月18日在20:54

(尽管我刚刚注意到这段代码实际上并没有利用它)

– annakata
09年8月18日在20:56

@annakata,您不能在这里使用它,因为0是有效索引。

–三联画
09年8月18日在20:59

@triptych:不正确,所需要的只是正确的顺序-请参阅我的文章

– annakata
09年8月18日在21:05

#7 楼

默认情况下,Uint8ArrayUint16ArrayUint32Array类将零保留为其值,因此您不需要任何复杂的填充技术,只需执行以下操作即可:

var ary = new Uint8Array(10);


数组ary的所有元素默认为零。

评论


这很好,但请注意,这不能与普通数组一样对待,例如Array.isArray(ary)为false。该长度也是只读的,因此您不能像ary.push一样向其推送新项目

–MusikAnimal
17年1月14日在2:22

Fwiw所有类型的数组都将0保留为其默认值。

– jfunk
17年5月2日,2:55



@MusikAnimal,Array.from(new Uint8Array(10))将提供一个普通数组。

– Tomas Langkaas
17年6月23日在14:31

@TomasLangkaas:是的,但是另一个答案表明,如果您真正需要的是JS数组,那么它比Chrome中的Array(n).fill(0)慢5倍。但是,如果可以使用TypedArray,则它甚至比.fill(0)还要快得多,尤其是如果您可以使用默认的初始化值0时。尤其是似乎没有构造函数需要填充值和长度,即C ++ std :: vector的方式。似乎对于任何非零值,您都必须构造一个清零的TypedArray,然后填充它。 :/

– Peter Cordes
19年11月20日在7:22



#8 楼

如果使用ES6,则可以像这样使用Array.from():

Array.from({ length: 3 }, () => 0);
//[0, 0, 0]




Array.from({ length: 3 }).map(() => 0)
//[0, 0, 0]


因为

Array.from({ length: 3 })
//[undefined, undefined, undefined]


#9 楼

function makeArrayOf(value, length) {
  var arr = [], i = length;
  while (i--) {
    arr[i] = value;
  }
  return arr;
}

makeArrayOf(0, 5); // [0, 0, 0, 0, 0]

makeArrayOf('x', 3); // ['x', 'x', 'x']


请注意,while通常比for-inforEach等更有效。

评论


i局部变量不是多余的吗?长度是通过值传递的,因此您应该可以直接将其递减。

– Sean Bright
09年8月18日在18:26

虽然起初看起来不错,但不幸的是,在Arary中的任意点赋值非常慢(例如arr [i] = value)。从头到尾循环使用arr.push(value)更快。这很烦人,因为我更喜欢您的方法。

–尼克·布鲁特(Nick Brunt)
2014年7月26日15:13

#10 楼

使用对象符号

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();




var y = [];


将对原型进行修改

无论如何,我不会过分担心此操作的效率或速度,这里有很多您可能要做的其他事情比实例化包含零的任意长度的数组要浪费得多且昂贵得多。

评论


错误...此数组中没有null-var x = new Array(7);

– kangax
09年8月18日在18:25

实际上,新的Array(n)不会填充任何数组,甚至不会包含“ undefined”,它只是将数组的长度值设置为n。您可以通过调用(new Array(1))。forEach(...)进行检查。 forEach永远不会执行,这与您在[undefined]上调用它不同。

– JussiR
2013年9月19日15:03

new Array(7)不会创建“充满未定义”的数组。它创建一个长度为7的空数组。

–RobG
2014年12月17日23:06

您可能想重新考虑部分答案,因为@RobG所说的很关键(如果您所说的是正确的,则映射会容易得多)

– Abdo
15年8月6日在10:06

这些天,您可以做(new Array(10))。fill(0)。

–哈维尔·德拉罗莎(Javier de la Rosa)
18年2月16日在21:17

#11 楼

我已经测试了IE 6/7/8,Firefox 3.5,Chrome和Opera中预分配/不预分配,向上/向下计数以及for / while循环的所有组合。

以下功能始终是Firefox,Chrome和IE8中最快或非常接近的功能,并且不比Opera和IE 6中最快的速度慢很多。在我看来,这也是最简单,最清晰的功能。我发现有几种浏览器的while循环版本稍快一些,因此也将其包括在内以供参考。

function newFilledArray(length, val) {
    var array = [];
    for (var i = 0; i < length; i++) {
        array[i] = val;
    }
    return array;
}




function newFilledArray(length, val) {
    var array = [];
    var i = 0;
    while (i < length) {
        array[i++] = val;
    }
    return array;
}


评论


您也可以将var array = []声明实际放入for循环的第一部分,仅以逗号分隔。

– Damianb
2012年11月19日14:51



我喜欢达米安(damianb)的建议,但请记住将作业和逗号放在加号之前!对于(var i = 0; i
– punstress
15年3月15日在1:39

执行您的第二个命令所缺少的所有其他操作,并将数组的长度设置为已给定的长度值,以使其不会不断变化。在我的机器上将一百万个长度为零的数组从40ms扩展为8。

–乔纳森·格雷(Jonathan Gray)
2015年9月7日在7:49

当我将此解决方案重构为一个衬板时,我似乎可以将速度提高10-15%。 for(i = 0,array = []; i var i,array = []; array.length =长度; while(i
–皮条客Trizkit
15/09/22在23:11



我还将在测试中注意到这一点。在我相当多的测试用例中,上述最终版本的执行速度似乎是3倍,甚至快10倍以上。。。我不确定为什么...(在chrome和FF之间测试了不同的数组大小)

–皮条客Trizkit
15/09/22在23:23



#12 楼

如果您需要在执行代码的过程中创建许多不同长度的零填充数组,我发现实现此目标的最快方法是使用本主题中提到的一种方法,一次创建一个零数组,该数组的长度为您将永远不会超出的范围,然后根据需要对数组进行切片。例如(使用上面选择的答案中的函数初始化数组),创建一个长度为maxLength的零填充数组,作为需要零数组的代码可见的变量:

var zero = newFilledArray(maxLength, 0);

现在,每当您需要零填充的数组,且长度为requiredLength 时,就对该数组进行切片。
zero.slice(0, requiredLength);


我在执行代码的过程中数千次创建了零填充数组,这极大地加快了处理速度。

#13 楼

function zeroFilledArray(size) {
    return new Array(size + 1).join('0').split('');
}


评论


您可能还使用new Array(size + 1).join(“ x”)。split(“ x”)。map(function(){return 0;})获得实际数字

–卵黄质
2012年4月6日23:27

@Yuval或者只是新的Array(size + 1).join('0')。split('')。map(Number)

– Paul
2014年1月13日17:25



#14 楼

我没有反对:Zertosh建议使用
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
new Array(5+1).join('0').split('').map(parseFloat);


,但是在新的ES6数组扩展中,您可以使用fill方法来本机执行此操作。现在IE edge,Chrome和FF支持它,但是检查兼容性表

new Array(3).fill(0)将为您提供[0, 0, 0]。您可以使用new Array(5).fill('abc')之类的任何值填充数组(甚至对象和其他数组)。

最重要的是,您可以使用fill修改以前的数组:

arr = [1, 2, 3, 4, 5, 6]
arr.fill(9, 3, 5)  # what to fill, start, end


这会给您:[1, 2, 3, 9, 9, 6]

#15 楼

ES6解决方案:

[...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0]


#16 楼

我通常这样做的方法(而且速度很快)是使用Uint8Array。例如,创建一个由1M个元素组成的零填充向量:

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


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

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


编辑



弗雷德里克·戈特利布(Frederik Gottlieb)-6.43
萨姆·巴纳姆(Sam Barnum)-4.83
伊莱(Eli)-3.68
约书亚2.91
马修·克鲁姆(Mathew Crumley)-2.67
bduran- 2.55
艾伦·赖斯-2.11
袋鼠-0.68
Tj。 Crowder-0.67
zertosh-错误

Firefox 20.0


Allen Rice-1.85
约书亚-1.82
Mathew Crumley- 1.79
bduran-1.37
弗雷德里克·戈特利布-0.67
萨姆·巴纳姆-0.63
伊莱-0.59
Tj。 Crowder-0.13
zertosh-错误

缺少最重要的测试(至少对我而言):Node.js。我怀疑它接近Chrome基准测试。

评论


这是我的手指和眼睛最有效的方式。但是Chrome浏览器的运行速度非常慢(根据jsperf的数据,速度要慢99%)。

–嗜尿菌
16 Mar 5 '16 at 5:20



我想知道您朋友的Mac上的问题是否与以下问题有关:stackoverflow.com/questions/39129200/…还是Array.slice不能处理UInt8Array并泄漏未初始化的内存? (安全问题!)。

–robocat
17年8月31日在5:04



@robocat好抓住!如果我没记错的话,我们使用的是Node.js 0.6或0.8。我们考虑过某种泄漏,但由于无法在生产堆栈中进行重现,因此我们决定忽略它。

– durum
17年4月4日在9:48

#17 楼

使用lodash或下划线

_.range(0, length - 1, 0);


或者如果现有数组,并且想要长度相同的数组

array.map(_.constant(0));


评论


很高兴您添加了这个答案,因为我使用了下划线,并且我知道有一些解决方法……但是还没有找到它。我只是希望我可以使用此对象创建数组

– PandaWood
14-10-22在6:02



我认为@PandaWood _.range(0,长度-1,0).map(Object.new)。

– djechlin
14-10-22在11:51

我相信应该是_.range(0,length,0)。 Lodash不包含最终价值

–user4815162342
16 Jun 25'21:26



#18 楼

创建一个全新的阵列

new Array(arrayLength).fill(0);


在现有阵列的末尾添加一些值

[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]

>示例




 //**To create an all new Array**

console.log(new Array(5).fill(0));

//**To add some values at the end of an existing Array**

let existingArray = [1,2,3]

console.log([...existingArray, ...new Array(5).fill(0)]); 




#19 楼




 const arr = Array.from({ length: 10 }).fill(0) 




#20 楼

从ECMAScript2016开始,大型阵列有一个明确的选择。

由于此答案仍显示在Google搜索的顶部附近,因此,这是2017年的答案。

这是当前的jsbench,其中包含数十种流行的方法,其中包括许多建议的方法到现在为止这个问题。如果找到更好的方法,请添加,分叉并共享。

我想指出的是,没有一种真正有效的方法可以创建任意长度的零填充数组。您可以针对速度进行优化,也可以针对清晰度和可维护性进行优化-根据项目的需求,可以将其视为更有效的选择。

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




 const arr = [];
arr.length = 120000;
let i = 0;
while (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新手在搜索这些新旧答案时了解这些新标准。

#21 楼

没有在答案中看到此方法,因此它是:

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


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

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


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

评论


无论是最快还是最短的解决方案,对解决方案的多样性都是一个不错的贡献。

– 7vujy0f0hy
17年4月19日在13:42

#22 楼

在我在Chrome(2013-03-21)上的测试中,此concat版本要快得多。 10,000,000个元素大约需要200毫秒,而直接init则需要675毫秒。

function filledArray(len, value) {
    if (len <= 0) return [];
    var result = [value];
    while (result.length < len/2) {
        result = result.concat(result);
    }
    return result.concat(result.slice(0, len-result.length));
}


奖励:如果要用字符串填充数组,这是一种简洁的方法(不是与concat相当快):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}


评论


好的,野性。这比使用新的Array(len)更快。但!我在Chrome浏览器中看到,后续读取该数据所花费的时间会更长。以下是一些时间戳,以显示我的意思:(使用新的Array(len))0.365:制作数组4.526:执行卷积10.75:完成卷积(使用concat)0.339:制作数组0.591:执行卷积// OMG,方法更快18.056:卷积完成

–布鲁克斯
13年5月1日在17:06



#23 楼

我正在测试T.J. Crowder,并基于concat解决方案提出了一个递归合并,该合并性能优于他在Chrome中进行的测试(我未测试其他浏览器)。

function makeRec(len, acc) {
    if (acc == null) acc = [];
    if (len <= 1) return acc;
    var b = makeRec(len >> 1, [0]);
    b = b.concat(b);
    if (len & 1) b = b.concat([0]);
    return b;
},


makeRec(29)的方法。

#24 楼

new Array(51).join('0').split('')怎么样?

评论


然后.map(function(a){return + a})吗?

–lonewarrior556
16年10月10日在13:32

至于2020年,新的Array(51).fill(0)呢?它给出完全相同的输出。

–phen0menon
10月19日下午13:36



#25 楼

可能值得指出的是,已将Array.prototype.fill作为ECMAScript 6(Harmony)提案的一部分进行了添加。在考虑线程上提到的其他选项之前,我宁愿使用下面编写的polyfill。

if (!Array.prototype.fill) {
  Array.prototype.fill = function(value) {

    // Steps 1-2.
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    var O = Object(this);

    // Steps 3-5.
    var len = O.length >>> 0;

    // Steps 6-7.
    var start = arguments[1];
    var relativeStart = start >> 0;

    // Step 8.
    var k = relativeStart < 0 ?
      Math.max(len + relativeStart, 0) :
      Math.min(relativeStart, len);

    // Steps 9-10.
    var end = arguments[2];
    var relativeEnd = end === undefined ?
      len : end >> 0;

    // Step 11.
    var final = relativeEnd < 0 ?
      Math.max(len + relativeEnd, 0) :
      Math.min(relativeEnd, len);

    // Step 12.
    while (k < final) {
      O[k] = value;
      k++;
    }

    // Step 13.
    return O;
  };
}


#26 楼

最短的循环代码

a=i=[];for(;i<100;)a[i++]=0;

edit:
for(a=i=[];i<100;)a[i++]=0;
or
for(a=[],i=100;i--;)a[i]=0;


安全var版本

var a=[],i=0;for(;i<100;)a[i++]=0;

edit:
for(var i=100,a=[];i--;)a[i]=0;


评论


假设长度是一个定义的变量n,则它会更短:for(var a = []; n-; a [n] = 0);

– Tomas Langkaas
16 Dec 8'在12:49

#27 楼




 let filled = [];
filled.length = 10;
filled.fill(0);

console.log(filled); 




#28 楼

我最快的功能是:

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


使用本机推入和移位将项目添加到数组的速度比声明数组范围和引用要快得多(约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


我有兴趣知道什么是TJ人群构成了什么? :-)

评论


您可以通过将其更改为while(len--)来加快处理速度,这将我的处理时间从大约60ms缩短到了大约54ms

–nickf
09年8月21日在8:23

马修·克鲁布利(Matthew Crumbly)的答案实际上仍然胜过了这个(30毫秒)!

–nickf
09年8月21日在8:31

#29 楼

我知道我在某处有这个原型:)

Array.prototype.init = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this[n] = x; }
    return this;
}

var a = (new Array(5)).init(0);

var b = [].init(0,4);


编辑:tests

为了响应Joshua和其他方法,我运行了自己的基准测试,结果与报告的结果完全不同。

这是我测试的结果:

//my original method
Array.prototype.init = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this[n] = x; }
    return this;
}

//now using push which I had previously thought to be slower than direct assignment
Array.prototype.init2 = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this.push(x); }
    return this;
}

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

//test m1 and m2 with short arrays many times 10K * 10

var a = new Date();
for(var i=0; i<10000; i++)
{
    var t1 = [].init(0,10);
}
var A = new Date();

var b = new Date();
for(var i=0; i<10000; i++)
{
    var t2 = [].init2(0,10);
}
var B = new Date();

//test m1 and m2 with long array created once 100K

var c = new Date();
var t3 = [].init(0,100000);
var C = new Date();

var d = new Date();
var t4 = [].init2(0,100000);
var D = new Date();

//test m3 with short array many times 10K * 10

var e = new Date();
for(var i=0; i<10000; i++)
{
    var t5 = newFilledArray(10,0);
}
var E = new Date();

//test m3 with long array created once 100K

var f = new Date();
var t6 = newFilledArray(100000, 0)
var F = new Date();


结果:

IE7 deltas:
dA=156
dB=359
dC=125
dD=375
dE=468
dF=412

FF3.5 deltas:
dA=6
dB=13
dC=63
dD=8
dE=12
dF=8


因此,根据我的估算,推送通常确实较慢,但在FF中使用较长的数组时性能更好,但在IE中则差强人意(令人惊讶)。

评论


我刚刚对此进行了测试:第二种方法(b = [] ...)比第一种方法快10-15%,但比Joshua的答案慢10倍以上。

–nickf
09年8月21日在8:27

我知道这是一个古老的帖子。但是也许它仍然对其他人(例如我)感兴趣。因此,我想建议对原型函数进行补充:在this.length-check之后添加else {this.length = n;}。如果需要,在将其重新初始化为其他长度n时,这将缩短已经存在的数组。

–汽车10分钟
15年8月25日在7:42



#30 楼

匿名函数:

(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]


与任何Object兼容,只需更改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