web-dev-qa-db-ja.com

JavaScriptセットと配列のパフォーマンス

SetはJavascriptが比較的新しいためかもしれませんが、StackOまたはその他の場所で、Javascriptの2つのパフォーマンスの違いに関する記事を見つけることができませんでした。それでは、パフォーマンスの点で、両者の違いは何ですか?具体的には、削除、追加、および反復処理に関してです。

61
snowfrogdev

さて、配列とセットの両方から要素を追加、反復、削除することをテストしました。 10 000個の要素を使用して「小」テストを実行し、100 000個の要素を使用して「大」テストを実行しました。結果は次のとおりです。

コレクションに要素を追加する

.Push配列メソッドは、追加される要素の数に関係なく、.add setメソッドよりも約4倍高速であると思われます。

コレクション内の要素の繰り返しと変更

テストのこの部分では、forループを使用して配列を反復処理し、for ofループを使用してセットを反復処理しました。繰り返しになりますが、配列の繰り返し処理は高速でした。今回は指数関数的であるように見えるので、「小さい」テストでは2倍、「大きい」テストではほぼ4倍時間がかかりました。

コレクションから要素を削除する

これが興味深いところです。 forループと.spliceの組み合わせを使用して配列からいくつかの要素を削除し、for of.deleteを使用してセットからいくつかの要素を削除しました。 「小さな」テストでは、セットからアイテムを削除するのに約3倍高速でした(2.6ミリ秒対7.1ミリ秒)が、「ビッグ」テストでは、それだけでアレイからアイテムを削除するのに1955.1ミリ秒かかりましたセットからそれらを削除するのに83.6ミリ秒かかり、23倍速くなりました。

結論

1万個の要素では、両方のテストで同等の時間を実行しました(配列:16.6ミリ秒、セット:20.7ミリ秒)が、1万個の要素を処理する場合、明確な勝者(配列:1974.8ミリ秒、セット:83.6ミリ秒)でしたが、操作。それ以外の場合、配列は高速でした。その理由を正確に言うことはできません。

配列を作成してデータを入力し、いくつかの要素を削除するセットに変換した後、そのセットを配列に再変換するハイブリッドシナリオをいくつか試しました。これを行うと、配列内の要素を削除するよりもはるかに優れたパフォーマンスが得られますが、セットとの間で転送するために必要な追加の処理時間は、セットではなく配列にデータを取り込むことの利点よりも重要です。最終的に、セットのみを処理する方が高速です。それでも、重複のないビッグデータのデータコレクションとして配列を使用することを選択した場合、1つの多くの要素を削除する必要がある場合、パフォーマンスの面で有利になる可能性があるというのは興味深いアイデアです操作。配列をセットに変換し、削除操作を実行し、セットを配列に戻します。

配列コード:

var timer = function(name) {
  var start = new Date();
  return {
    stop: function() {
      var end = new Date();
      var time = end.getTime() - start.getTime();
      console.log('Timer:', name, 'finished in', time, 'ms');
    }
  }
};

var getRandom = function(min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];

var genLastName = function() {
  var index = Math.round(getRandom(0, lastNames.length - 1));
  return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
  var index = Math.round(getRandom(0, sex.length - 1));
  return sex[index];
};

var Person = function() {
  this.name = genLastName();
  this.age = Math.round(getRandom(0, 100))
  this.sex = "Male"
};

var genPersons = function() {
  for (var i = 0; i < 100000; i++)
    personArray.Push(new Person());
};

var changeSex = function() {
  for (var i = 0; i < personArray.length; i++) {
    personArray[i].sex = genSex();
  }
};

var deleteMale = function() {
  for (var i = 0; i < personArray.length; i++) {
    if (personArray[i].sex === "Male") {
      personArray.splice(i, 1)
      i--
    }
  }
};

var t = timer("Array");

var personArray = [];

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personArray.length + " persons.")

設定コード:

var timer = function(name) {
    var start = new Date();
    return {
        stop: function() {
            var end  = new Date();
            var time = end.getTime() - start.getTime();
            console.log('Timer:', name, 'finished in', time, 'ms');
        }
    }
};

var getRandom = function (min, max) {
  return Math.random() * (max - min) + min;
};

var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];

var genLastName = function() {
    var index = Math.round(getRandom(0, lastNames.length - 1));
    return lastNames[index];
};

var sex = ["Male", "Female"];

var genSex = function() {
    var index = Math.round(getRandom(0, sex.length - 1));
    return sex[index];
};

var Person = function() {
        this.name = genLastName();
        this.age = Math.round(getRandom(0,100))
        this.sex = "Male"
};

var genPersons = function() {
for (var i = 0; i < 100000; i++)
        personSet.add(new Person());
};

var changeSex = function() {
        for (var key of personSet) {
                key.sex = genSex();
        }
};

var deleteMale = function() {
        for (var key of personSet) {
                if (key.sex === "Male") {
                        personSet.delete(key)
                }
        }
};

var t = timer("Set");

var personSet = new Set();

genPersons();

changeSex();

deleteMale();

t.stop();

console.log("Done! There are " + personSet.size + " persons.")
72
snowfrogdev

観測値

  • セット操作は、実行ストリーム内のスナップショットとして理解できます。
  • 私たちは決定的な代替品ではありません。
  • Setクラスの要素にはアクセス可能なインデックスがありません。
  • Set classArray class補完であり、基本的な追加を適用するコレクションを保存する必要があるシナリオで役立ちます。削除、チェック、および反復操作。

パフォーマンスのテストをいくつか共有します。コンソールを開いて、以下のコードをコピー&ペーストしてみてください。

配列の作成(125000)

var n = 125000;
var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i );
console.info( arr.length ); // 125000

1。インデックスの検索

SetのhasメソッドとArray indexOfを比較しました。

Array /indexOf(0.281ms)| Set /has(0.053ms)

// Helpers
var checkArr = ( arr, item ) => arr.indexOf( item ) !== -1;
var checkSet = ( set, item ) => set.has( item );

// Vars
var set, result;

console.time( 'timeTest' );
result = checkArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
checkSet( set, 123123 );
console.timeEnd( 'timeTest' );

2。新しい要素の追加

SetオブジェクトとArrayオブジェクトのaddメソッドとPushメソッドをそれぞれ比較します。

Array /Push(1.612ms)| Set /add(0.006ms)

console.time( 'timeTest' );
arr.Push( n + 1 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.add( n + 1 );
console.timeEnd( 'timeTest' );

console.info( arr.length ); // 125001
console.info( set.size ); // 125001

3。要素の削除

要素を削除するとき、ArrayとSetは等しい条件下では開始しないことに注意する必要があります。配列にはネイティブメソッドがないため、外部関数が必要です。

Array /deleteFromArr(0.356ms)| Set /remove(0.019ms)

var deleteFromArr = ( arr, item ) => {
    var i = arr.indexOf( item );
    i !== -1 && arr.splice( i, 1 );
};

console.time( 'timeTest' );
deleteFromArr( arr, 123123 );
console.timeEnd( 'timeTest' );

set = new Set( arr );

console.time( 'timeTest' );
set.delete( 123123 );
console.timeEnd( 'timeTest' );

記事全文を読む here

私の観察では、大規模な配列の場合、2つの落とし穴を考慮してSetが常に優れているということです。

a)配列からのセットの作成は、プリキャッシュされた長さのforループで実行する必要があります。

遅い(例:18ms)new Set(largeArray)

高速(6msなど)const SET = new Set(); const L = largeArray.length; for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }

b)for ofループよりも高速であるため、同じ方法で反復を実行できます...

https://jsfiddle.net/0j2gkae7/5/ を参照してください

difference()intersection()union()、およびuniq()(+それらの反復コンパニオンなど)と40.000要素との実際の比較

3
sebilasse

私は最近このテストを実行し、Setが1000項目の配列よりもはるかに優れていることを発見しました(同じ時間枠で約10倍の操作が発生する可能性があります)。そして、同様のテストのように、ブラウザに応じてObject.hasOwnPropertyを破るか失います。

SetとObjectの両方には、O(1)で償却されているように見える「has」メソッドがありますが、ブラウザの実装によっては、単一の操作に時間がかかるか、高速になる場合があります。

https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1 異なるブラウザー/環境で独自のテストを実行したい場合。

1
Zargold