719

JavaScriptで配列を複製するには:次のうちどれがより速く使用できますか?

Slice方法

var dup_array = original_array.slice();

Forループ

for(var i = 0, len = original_array.length; i < len; ++i)
   dup_array[i] = original_array[i];

どちらの方法でも浅いコピーしか実行されないことを知っています。オブジェクトへの参照が含まれている場合original_array、オブジェクトは複製されませんが、参照のみがコピーされるため、両方の配列に同じオブジェクトへの参照が含まれます。しかし、これはこの質問のポイントではありません。

私は速度についてだけ尋ねています。

4

24 に答える 24

866

アレイのクローンを作成するには、少なくとも6つの(!)方法があります。

  • ループ
  • スライス
  • Array.from()
  • concat
  • スプレッド演算子(最速)
  • 地図A.map(function(e){return e;});

次の情報を提供する巨大なベンチマークスレッドがあります。

  • ブリンクブラウザの場合slice()は、最速の方法でconcat()あり、少し遅く、while loop2.4倍遅くなります。

  • 他のブラウザの場合は、およびwhile loopの内部最適化がないため、最速の方法です。sliceconcat

これは2016年7月も当てはまります。

以下は、ブラウザのコンソールにコピーして貼り付け、数回実行して画像を表示できる簡単なスクリプトです。それらはミリ秒を出力します、低いほど良いです。

whileループ

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = Array(n); 
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);

スライス

n = 1000*1000;
start = + new Date();
a = Array(n); 
b = a.slice();
console.log(new Date() - start);

これらのメソッドはArrayオブジェクト自体のクローンを作成しますが、配列の内容は参照によってコピーされ、ディープクローンされないことに注意してください。

origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
于 2013-12-12T15:42:47.020 に答える
293

技術的slice には最速の方法です。ただし0開始インデックスを追加するとさらに高速になります。

myArray.slice(0);

よりも速い

myArray.slice();

https://web.archive.org/web/20170824010701/https://jsperf.com/cloning-arrays/3

于 2014-02-02T18:11:04.340 に答える
171

es6の方法はどうですか?

arr2 = [...arr1];
于 2015-07-06T16:34:08.017 に答える
53

配列またはオブジェクトのクローンを作成する最も簡単な方法:

var dup_array = JSON.parse(JSON.stringify(original_array))
于 2014-05-05T20:23:29.933 に答える
37
var cloned_array = [].concat(target_array);
于 2016-10-05T12:00:15.707 に答える
31

アレイのクローンを作成する最速の方法

配列のクローンを作成するのにかかる時間をテストするために、この非常にわかりやすいユーティリティ関数を作成しました。100%信頼できるわけではありませんが、既存のアレイのクローンを作成するのにかかる時間については、大まかなアイデアが得られます。

function clone(fn) {
  const arr = [...Array(1000000)];
  console.time('timer');
  fn(arr);
  console.timeEnd('timer');
}

そして、異なるアプローチをテストしました:

1)   5.79ms -> clone(arr => Object.values(arr));
2)   7.23ms -> clone(arr => [].concat(arr));
3)   9.13ms -> clone(arr => arr.slice());
4)  24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.push(val); } return a; });
5)  30.02ms -> clone(arr => [...arr]);
6)  39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7)  99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));

更新

  1. テストは2018年に行われたため、今日では、現在のブラウザで異なる結果が得られる可能性があります。
  2. これらすべての中で、配列をディープクローンする唯一の方法は、を使用することJSON.parse(JSON.stringify(arr))です。

    とはいえ、配列に関数が含まれている可能性がある場合は、上記を使用しないでくださいnull
    このアップデートをありがとう@GilEpshtain

ChromeとFirefoxで@mesqueebのベンチマークを実行しました。slice()それでもかなり良い選択肢のようです。

Chrome 95.0: Chromeアレイ複製ベンチマーク

Firefox 94.0: Firefoxアレイ複製ベンチマーク

于 2018-10-22T12:50:54.063 に答える
26

簡単なデモをまとめました:http://jsbin.com/agugo3/edit

Internet Explorer 8での私の結果は156、782、および750でありslice、この場合ははるかに高速であることを示しています。

于 2010-10-20T14:06:07.003 に答える
22

a.map(e => e)この仕事の別の選択肢です。今日の時点で、Firefoxでは.map()非常に高速です(ほぼ同じくらい高速です.slice(0))が、Chromeではそうではありません。

一方、配列が多次元の場合、配列はオブジェクトであり、オブジェクトは参照型であるため、sliceメソッドまたはconcatメソッドはいずれも解決策にはなりません...したがって、配列をクローン化する適切な方法の1つは、Array.prototype.clone()asの発明です。次のとおりです。

Array.prototype.clone = function(){
  return this.map(e => Array.isArray(e) ? e.clone() : e);
};

var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
    brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));

于 2016-05-28T21:32:12.747 に答える
8

見てください:リンク。それはスピードではなく、快適さです。ご覧のとおり、プリミティブ型ではslice(0)のみを使用できます。

配列への参照のコピーではなく、配列の独立したコピーを作成するには、配列スライスメソッドを使用できます。

例:

配列への参照のコピーではなく、配列の独立したコピーを作成するには、配列スライスメソッドを使用できます。

var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();

オブジェクトをコピーまたは複製するには:

function cloneObject(source) {
    for (i in source) {
        if (typeof source[i] == 'source') {
            this[i] = new cloneObject(source[i]);
        }
        else{
            this[i] = source[i];
  }
    }
}

var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2= new cloneObject(obj1);

出典:リンク

于 2010-10-20T13:52:05.273 に答える
8

Spread演算子を使用したECMAScript2015の方法:

基本的な例:

var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]

ブラウザコンソールで試してください:

var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)

var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);

参考文献

于 2017-12-16T08:24:36.080 に答える
8

オブジェクトの配列を複製する最も速い方法は、スプレッド演算子を使用することです

var clonedArray=[...originalArray]

ただし、その複製された配列内のオブジェクトは、引き続き古いメモリ位置を指します。したがって、clonedArrayオブジェクトに変更すると、orignalArrayも変更されます。それで

var clonedArray = originalArray.map(({...ele}) => {return ele})

これにより、新しい配列が作成されるだけでなく、オブジェクトのクローンも作成されます。

于 2021-02-12T15:04:29.413 に答える
7

@Danが「この回答はすぐに古くなります。ベンチマークを使用して実際の状況を確認してください」と述べたように、jsperfからは、それ自体に対する回答がない特定の回答が1つあります

var i = a.length;
while(i--) { b[i] = a[i]; }

960,589 ops /秒で、ランナーアップa.concat()は578,129 ops /秒で、これは60%です。

これは最新のFirefox(40)64ビットです。


@aleclarsonは、より信頼性の高い新しいベンチマークを作成しました。

于 2015-08-27T15:33:14.483 に答える
6

ブラウザによって異なります。ブログ投稿Array.prototype.sliceと手動アレイ作成を見ると、それぞれのパフォーマンスの大まかなガイドがあります。

ここに画像の説明を入力してください

結果:

ここに画像の説明を入力してください

于 2010-10-20T13:55:22.603 に答える
6

はるかにクリーンな解決策があります:

var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);

Arrayコンストラクターが1つの引数で呼び出された場合、コンストラクターの動作が異なるため、長さのチェックが必要です。

于 2014-04-28T13:31:57.677 に答える
6

.slice()は2次元配列では機能しないことに注意してください。次のような関数が必要になります。

function copy(array) {
  return array.map(function(arr) {
    return arr.slice();
  });
}
于 2016-01-21T13:02:10.700 に答える
6

ベンチマーク時間!

function log(data) {
  document.getElementById("log").textContent += data + "\n";
}

benchmark = (() => {
  time_function = function(ms, f, num) {
    var z = 0;
    var t = new Date().getTime();
    for (z = 0;
      ((new Date().getTime() - t) < ms); z++)
      f(num);
    return (z)
  }

  function clone1(arr) {
    return arr.slice(0);
  }

  function clone2(arr) {
    return [...arr]
  }

  function clone3(arr) {
    return [].concat(arr);
  }

  Array.prototype.clone = function() {
    return this.map(e => Array.isArray(e) ? e.clone() : e);
  };

  function clone4(arr) {
    return arr.clone();
  }


  function benchmark() {
    function compare(a, b) {
      if (a[1] > b[1]) {
        return -1;
      }
      if (a[1] < b[1]) {
        return 1;
      }
      return 0;
    }

    funcs = [clone1, clone2, clone3, clone4];
    results = [];
    funcs.forEach((ff) => {
      console.log("Benchmarking: " + ff.name);
      var s = time_function(2500, ff, Array(1024));
      results.push([ff, s]);
      console.log("Score: " + s);

    })
    return results.sort(compare);
  }
  return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();

console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
  log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>

ボタンをクリックしてから、ベンチマークは10秒間実行されます。

私の結果:

Chrome(V8エンジン):

1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)

Firefox(SpiderMonkeyエンジン):

1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)

勝者コード:

function clone1(arr) {
    return arr.slice(0);
}

勝者エンジン:

SpiderMonkey(Mozilla / Firefox)

于 2019-08-29T11:50:01.757 に答える
5

配列の長さによって異なります。配列の長さが1,000,000未満の場合、メソッドsliceconcatメソッドはほぼ同じ時間を要します。しかし、より広い範囲を与えると、そのconcat方法が勝ちます。

たとえば、次のコードを試してください。

var original_array = [];
for(var i = 0; i < 10000000; i ++) {
    original_array.push( Math.floor(Math.random() * 1000000 + 1));
}

function a1() {
    var dup = [];
    var start = Date.now();
    dup = original_array.slice();
    var end = Date.now();
    console.log('slice method takes ' + (end - start) + ' ms');
}

function a2() {
    var dup = [];
    var start = Date.now();
    dup = original_array.concat([]);
    var end = Date.now();
    console.log('concat method takes ' + (end - start) + ' ms');
}

function a3() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with push method takes ' + (end - start) + ' ms');
}

function a4() {
    var dup = [];
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup[i] = original_array[i];
    }
    var end = Date.now();
    console.log('for loop with = method takes ' + (end - start) + ' ms');
}

function a5() {
    var dup = new Array(original_array.length)
    var start = Date.now();
    for(var i = 0; i < original_array.length; i ++) {
        dup.push(original_array[i]);
    }
    var end = Date.now();
    console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}

a1();
a2();
a3();
a4();
a5();

original_arrayの長さを1,000,000に設定すると、sliceメソッドとconcatメソッドはほぼ同じ時間(乱数に応じて3〜4ミリ秒)かかります。

original_arrayの長さを10,000,000に設定するsliceと、メソッドは60ミリ秒以上concatかかり、メソッドは20ミリ秒以上かかります。

于 2015-11-18T07:35:52.867 に答える
4

ES6では、Spread構文を簡単に利用できます。

例:

let arr = ['a', 'b', 'c'];
let arr2 = [...arr];

Spread演算子は完全に新しい配列を生成するため、一方を変更しても他方には影響しないことに注意してください。

例:

arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']
于 2020-05-15T12:06:40.947 に答える
4

アレイのクローンを作成する方法はいくつかありました。基本的に、クローン作成は2つの方法で分類されました。

  1. 浅いコピー
  2. ディープコピー

浅いコピーはアレイの第1レベルのみをカバーし、残りは参照されます。配列内のネストされた要素の真のコピーが必要な場合は、ディープクローンが必要になります。

例 :

const arr1 = [1,2,3,4,5,6,7]           
// Normal Array (shallow copy is enough)     
const arr2 = [1,2,3,[4],[[5]],6,7]          
// Nested Array  (Deep copy required) 


Approach 1 : Using (...)Spread Operator  (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]

Approach 2 : Using Array builtIn Slice method (Deep copy)  
const newArray = arr1.slice()  // [1,2,3,4,5,6,7]

Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1)  // [1,2,3,4,5,6,7]

Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));)  // [1,2,3,[4],[[5]],6,7]

Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)
于 2021-06-20T08:13:13.217 に答える
3

簡単な解決策:

original = [1,2,3]
cloned = original.map(x=>x)
于 2017-08-16T00:24:16.463 に答える
3
        const arr = ['1', '2', '3'];

         // Old way
        const cloneArr = arr.slice();

        // ES6 way
        const cloneArrES6 = [...arr];

// But problem with 3rd approach is that if you are using muti-dimensional 
 // array, then only first level is copied

        const nums = [
              [1, 2], 
              [10],
         ];

        const cloneNums = [...nums];

// Let's change the first item in the first nested item in our cloned array.

        cloneNums[0][0] = '8';

        console.log(cloneNums);
           // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

        // NOOooo, the original is also affected
        console.log(nums);
          // [ [ '8', 2 ], [ 10 ], [ 300 ] ]

したがって、これらのシナリオが発生しないようにするには、

        const arr = ['1', '2', '3'];

        const cloneArr = Array.from(arr);
于 2019-06-07T17:39:30.093 に答える
1

JavaScriptで配列を順番に複製する高速な方法:

#1: array1copy = [...array1];

#2: array1copy = array1.slice(0);

#3: array1copy = array1.slice();

配列オブジェクトにJSONでシリアル化できないコンテンツ(関数、Number.POSITIVE_INFINITYなど)が含まれている場合は、

array1copy = JSON.parse(JSON.stringify(array1))

于 2019-12-26T07:38:33.047 に答える
1

このコードに従うことができます。不変の方法の配列クローン。これは、アレイのクローン作成に最適な方法です


const array = [1, 2, 3, 4]

const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)
于 2020-03-14T20:08:38.517 に答える
1

すべての属性とサブオブジェクトの複製された参照を使用して、JSでREALの複製されたオブジェクト/配列が必要な場合:

export function clone(arr) {
    return JSON.parse(JSON.stringify(arr))
}

他のすべての操作はクローンを作成しません。これは、含まれているオブジェクトではなく、ルート要素のベースアドレスを変更するだけだからです。

オブジェクトツリーを再帰的にトラバースする場合を除きます。

単純なコピーの場合、これらは問題ありません。ストレージアドレスに関連する操作については、文字列に変換して完全に新しいオブジェクトに戻すことをお勧めします(そして、他のほとんどの場合、これは高速です!)。

于 2020-08-25T21:28:52.323 に答える