おそらく数千のオブジェクトを持つモデルがあります。それらを保存し、IDを取得したら単一のオブジェクトを取得する最も効率的な方法は何だろうと思っていました。 idは長い数字です。
だから、これらは私が考えていた2つのオプションです。オプション1では、増分インデックスを持つ単純な配列です。オプション2では、違いがある場合は、連想配列であり、おそらくオブジェクトです。私の質問は、1つのオブジェクトを取得する必要がある場合に効率的ですが、ループして並べ替えることもあります。
非連想配列を持つオプション1:
var a = [{id: 29938, name: 'name1'},
{id: 32994, name: 'name1'}];
function getObject(id) {
for (var i=0; i < a.length; i++) {
if (a[i].id == id)
return a[i];
}
}
連想配列を使用したオプション2:
var a = []; // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
return a[id];
}
更新:
OK、2番目のオプションで配列を使用することは問題外です。したがって、2番目のオプションの宣言行は、実際にはvar a = {};
である必要があります。唯一の質問は、指定されたIDを持つオブジェクトを取得する場合のパフォーマンスの向上です。
また、リストを何度も並べ替える必要がある場合、答えは変わりますか?
短いバージョン:配列はオブジェクトよりも高速です。しかし、100%正しい解決策はありません。
var a1 = [{id: 29938, name: 'name1'}, {id: 32994, name: 'name1'}];
var a2 = [];
a2[29938] = {id: 29938, name: 'name1'};
a2[32994] = {id: 32994, name: 'name1'};
var o = {};
o['29938'] = {id: 29938, name: 'name1'};
o['32994'] = {id: 32994, name: 'name1'};
for (var f = 0; f < 2000; f++) {
var newNo = Math.floor(Math.random()*60000+10000);
if (!o[newNo.toString()]) o[newNo.toString()] = {id: newNo, name: 'test'};
if (!a2[newNo]) a2[newNo] = {id: newNo, name: 'test' };
a1.Push({id: newNo, name: 'test'});
}
あなたの質問にはいくつかの誤解があります。
これらは配列です:
var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";
これも配列です:
var a3 = [];
a3[29938] = "a";
a3[32994] = "b";
すべての配列には連続したインデックスが付いているため、基本的には穴のある配列です。穴のない配列よりも低速です。しかし、配列を手動で繰り返すと、さらに遅くなります(ほとんど)。
これはオブジェクトです:
var a3 = {};
a3[29938] = "a";
a3[32994] = "b";
以下に3つの可能性のパフォーマンステストを示します。
Smashing Magazineのこれらのトピックに関する優れた記事: 高速メモリ効率のJavaScriptの記述
配列とオブジェクトの動作は非常に異なっているため(少なくとも、そうなるはずです)、実際にはパフォーマンスの問題ではありません。配列には連続インデックス0..n
があり、オブジェクトは任意のキーを任意の値にマッピングします。 youが特定のキーを提供する場合、唯一の選択肢はオブジェクトです。キーを気にしない場合は、配列です。
配列に任意の(数値)キーを設定しようとすると、実際にはパフォーマンスlossがあります。これは、動作によって配列がすべてのインデックスを埋めるためです:
> foo = [];
[]
> foo[100] = 'a';
"a"
> foo
[undefined, undefined, undefined, ..., "a"]
(配列には実際にはには99個のundefined
値が含まれていないことに注意してください。ただし、iteratingある時点で配列。)
両方のオプションのリテラルは、それらの使用方法を非常に明確にする必要があります。
var arr = ['foo', 'bar', 'baz']; // no keys, not even the option for it
var obj = { foo : 'bar', baz : 42 }; // associative by its very nature
ES6で最もパフォーマンスの高い方法は、マップを使用することです。
var myMap = new Map();
myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });
myMap.get(1);
myMap.get(2);
今日、シムを使用してES6機能を使用できます( https://github.com/es-shims/es6-shim )。
パフォーマンスは、ブラウザーとシナリオによって異なります。ただし、Map
のパフォーマンスが最も高い1つの例を次に示します。 https://jsperf.com/es6-map-vs-object-properties/2
リファレンス https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map
NodeJSでは、ID
がわかっている場合、object[ID]
に比べて配列のループ処理が非常に遅くなります。
const uniqueString = require('unique-string');
const obj = {};
const arr = [];
var seeking;
//create data
for(var i=0;i<1000000;i++){
var getUnique = `${uniqueString()}`;
if(i===888555) seeking = getUnique;
arr.Push(getUnique);
obj[getUnique] = true;
}
//retrieve item from array
console.time('arrTimer');
for(var x=0;x<arr.length;x++){
if(arr[x]===seeking){
console.log('Array result:');
console.timeEnd('arrTimer');
break;
}
}
//retrieve item from object
console.time('objTimer');
var hasKey = !!obj[seeking];
console.log('Object result:');
console.timeEnd('objTimer');
そして結果:
Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms
シークIDが配列/オブジェクトの最初のIDであっても:
Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms
これを文字通り次の次元に持ち込もうとしました。
X軸とy軸が常に同じ長さである2次元配列を考えると、次のことが速くなりますか?
a)2次元配列を作成し、最初のインデックスを検索し、次に2番目のインデックスを検索して、セルを検索します。
var arr=[][]
var cell=[x][y]
または
b)x座標とy座標の文字列表現でオブジェクトを作成し、そのobjで単一のルックアップを実行します。
var obj={}
var cell = obj['x,y']
結果:
オブジェクトの1つのプロパティルックアップよりも、配列の2つの数値インデックスルックアップの方がはるかに高速であることがわかります。
ここでの結果:
使い方に依存します。ケースがルックアップの場合、オブジェクトは非常に高速です。
配列とオブジェクトのルックアップのパフォーマンスをテストするPlunkerの例を次に示します。
https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview
あなたはそれを見るでしょう。 5.000の5.000の長さの配列コレクションのアイテムを探して、3000
を引き継ぎますミリセコン
ただし、5.000のルックアップには、オブジェクトに5.000プロパティがあります。 2
または3
milisecons
また、オブジェクトツリーを作成しても大きな違いはありません
検索配列:O(n n)==
キーでオブジェクトを検索:O(n 1)==
したがって、オブジェクトの方が優れています。
ソートされた配列がある場合、バイナリ検索を実行でき、オブジェクト検索よりもはるかに高速です。ここで私の答えを見ることができます:
Javascriptを使用してソートされた配列でより高速に検索する方法
X個のアイテムに限定されたイベントソースからのライブローソク足を保存する必要がある場合に直面している同様の問題がありました。各キャンドルのタイムスタンプがキーとして機能し、キャンドル自体が値として機能するオブジェクトに保存することができます。別の可能性は、各アイテムがろうそくそのものである配列に保存できることでした。ライブキャンドルに関する問題の1つは、最新の更新が最新のデータを保持しているのと同じタイムスタンプで更新を送信し続けるため、既存のアイテムを更新するか、新しいアイテムを追加することです。そのため、3つの可能性をすべて組み合わせようとするNiceベンチマークがあります。以下のソリューションの配列は、平均で少なくとも4倍高速です。気軽にプレイしてください
"use strict";
const EventEmitter = require("events");
let candleEmitter = new EventEmitter();
//Change this to set how fast the setInterval should run
const frequency = 1;
setInterval(() => {
// Take the current timestamp and round it down to the nearest second
let time = Math.floor(Date.now() / 1000) * 1000;
let open = Math.random();
let high = Math.random();
let low = Math.random();
let close = Math.random();
let baseVolume = Math.random();
let quoteVolume = Math.random();
//Clear the console everytime before printing fresh values
console.clear()
candleEmitter.emit("candle", {
symbol: "ABC:DEF",
time: time,
open: open,
high: high,
low: low,
close: close,
baseVolume: baseVolume,
quoteVolume: quoteVolume
});
}, frequency)
// Test 1 would involve storing the candle in an object
candleEmitter.on('candle', storeAsObject)
// Test 2 would involve storing the candle in an array
candleEmitter.on('candle', storeAsArray)
//Container for the object version of candles
let objectOhlc = {}
//Container for the array version of candles
let arrayOhlc = {}
//Store a max 30 candles and delete older ones
let limit = 30
function storeAsObject(candle) {
//measure the start time in nanoseconds
const hrtime1 = process.hrtime()
const start = hrtime1[0] * 1e9 + hrtime1[1]
const { symbol, time } = candle;
// Create the object structure to store the current symbol
if (typeof objectOhlc[symbol] === 'undefined') objectOhlc[symbol] = {}
// The timestamp of the latest candle is used as key with the pair to store this symbol
objectOhlc[symbol][time] = candle;
// Remove entries if we exceed the limit
const keys = Object.keys(objectOhlc[symbol]);
if (keys.length > limit) {
for (let i = 0; i < (keys.length - limit); i++) {
delete objectOhlc[symbol][keys[i]];
}
}
//measure the end time in nano seocnds
const hrtime2 = process.hrtime()
const end = hrtime2[0] * 1e9 + hrtime2[1]
console.log("Storing as objects", end - start, Object.keys(objectOhlc[symbol]).length)
}
function storeAsArray(candle) {
//measure the start time in nanoseconds
const hrtime1 = process.hrtime()
const start = hrtime1[0] * 1e9 + hrtime1[1]
const { symbol, time } = candle;
if (typeof arrayOhlc[symbol] === 'undefined') arrayOhlc[symbol] = []
//Get the bunch of candles currently stored
const candles = arrayOhlc[symbol];
//Get the last candle if available
const lastCandle = candles[candles.length - 1] || {};
// Add a new entry for the newly arrived candle if it has a different timestamp from the latest one we storeds
if (time !== lastCandle.time) {
candles.Push(candle);
}
//If our newly arrived candle has the same timestamp as the last stored candle, update the last stored candle
else {
candles[candles.length - 1] = candle
}
if (candles.length > limit) {
candles.splice(0, candles.length - limit);
}
//measure the end time in nano seocnds
const hrtime2 = process.hrtime()
const end = hrtime2[0] * 1e9 + hrtime2[1]
console.log("Storing as array", end - start, arrayOhlc[symbol].length)
}
結論10はここでの制限です
Storing as objects 4183 nanoseconds 10
Storing as array 373 nanoseconds 10