私は、おそらく何千ものオブジェクトを持つモデルを持っています。それらを保存し、オブジェクトのIDを取得した後、1つのオブジェクトを検索する最も効率的な方法は何だろうと考えていました。id'は長い数字です。
オプション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];
}
更新:」。
なるほど、2つ目の選択肢で配列を使うのは問題外ですね。だから、2番目の選択肢の宣言行は、本当はこうあるべきだ:var a = {};` で、唯一の問題は、与えられたidを持つオブジェクトを検索する際に、配列と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'});
}
[テストセットアップ]1です。 [テスト結果]2です。
ご質問には誤解があるようです。
これらは配列です:。
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
を持ち、オブジェクトは任意のキーと任意の値を対応させます。もし、特定のキーを与えたいのであれば、オブジェクトを使うしかありません。キーにこだわらないのであれば、配列になります。
配列に任意の(数値)キーを設定しようとすると、挙動上、配列がその間のすべてのインデックスを埋めてしまうので、本当にパフォーマンスが低下してしまいます*:
> foo = [];
[]
> foo[100] = 'a';
"a"
> foo
[undefined, undefined, undefined, ..., "a"]
(Note that array does not actually contain 99 undefined
values, but it will behave this way because you're [supposed to be] iterating the array at some point.)</sub>;
両方のオプションのリテラルは、それらがどのように使用されるかを明確にする必要があります:
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);
今日は、shim(https://github.com/es-shims/es6-shim)を使用してES6機能を使用できます。。
パフォーマンスはブラウザとシナリオによって異なります。 ただし、ここで「マップ」が最もパフォーマンスが高い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に対して1回のルックアップを行う、つまり
var obj={}
var cell = obj['x,y']
その結果 オブジェクトのプロパティを1回検索するよりも、配列の数値インデックスを2回検索する方がはるかに高速であることが判明しました。
結果はこちら:
使用状況によります。 ケースがルックアップの場合、オブジェクトは非常に高速です。
以下は、配列とオブジェクトルックアップのパフォーマンスをテストするためのプランカーの例です。
https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p =プレビュー。
あなたはそれを見るでしょう;。
5.000 長さ配列コレクションの 5.000 アイテムを探して、 3000
miliseconsを引き継ぎます。
ただし、オブジェクト内の 5.000 アイテムを検索すると、 5.000 のプロパティがあり、 2
または3
のミリセコンのみを取得します。
また、オブジェクトツリーを作成しても大きな違いはありません。
Xアイテムに限定されたイベントソースからのライブローソク足を保管する必要がある場所に直面している同様の問題がありました。 各キャンドルのタイムスタンプがキーとして機能し、キャンドル自体が値として機能するオブジェクトにそれらを保存することができます。 別の可能性は、各アイテムがキャンドル自体である配列にそれを保存できたことでした。 ライブキャンドルの1つの問題は、最新の更新が最新のデータを保持するのと同じタイムスタンプで更新を送信し続けることです。したがって、既存のアイテムを更新するか、新しいアイテムを追加します。 これが、3つの可能性すべてを組み合わせようとする素晴らしいベンチマークです。 以下の溶液の配列は、平均で少なくとも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