Ich habe ein Modell mit möglicherweise tausenden Objekten. Ich habe mich gefragt, was die effizienteste Art ist, sie zu speichern und ein einzelnes Objekt abzurufen, sobald ich seine ID habe. Die IDs sind lange Zahlen.
Das sind also die 2 Optionen, über die ich nachgedacht habe. Bei Option Eins handelt es sich um ein einfaches Array mit einem inkrementierenden Index. In Option 2 ist es ein assoziatives Array und möglicherweise ein Objekt, wenn es einen Unterschied macht. Meine Frage ist die, die effizienter ist, wenn ich meistens ein einzelnes Objekt abrufen muss, aber auch manchmal durchlaufen und sortieren kann.
Option eins mit nicht assoziativem Array:
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];
}
}
Option zwei mit assoziativem Array:
var a = []; // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
return a[id];
}
Update:
OK, ich verstehe, dass die Verwendung eines Arrays in der zweiten Option nicht in Frage kommt. In der Deklarationszeile sollte die zweite Option wirklich die folgende sein: var a = {};
und die einzige Frage ist: Was ist besser beim Abrufen eines Objekts mit einer gegebenen ID: ein Array oder ein Objekt, bei dem die ID der Schlüssel ist.
und auch, wird sich die Antwort ändern, wenn ich die Liste viele Male sortieren muss?
Die kurze Version: Arrays sind meistens schneller als Objekte. Es gibt jedoch keine zu 100% korrekte Lösung.
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'});
}
In Ihrer Frage gibt es einige falsche Vorstellungen.
Dies sind Arrays:
var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";
Dies ist auch ein Array:
var a3 = [];
a3[29938] = "a";
a3[32994] = "b";
Es ist im Grunde ein Array mit Löchern, da jedes Array eine kontinuierliche Indizierung hat. Es ist langsamer als Arrays ohne Löcher. Das manuelle Durchlaufen des Arrays ist jedoch (meistens) noch langsamer.
Dies ist ein Objekt:
var a3 = {};
a3[29938] = "a";
a3[32994] = "b";
Hier ist ein Leistungstest von drei Möglichkeiten:
Eine ausgezeichnete Lektüre zu diesen Themen im Smashing Magazine: Schnelles speichereffizientes JavaScript schreiben
Es ist überhaupt keine Frage der Performance, da Arrays und Objekte sehr unterschiedlich arbeiten (oder es zumindest sein sollen). Arrays haben einen fortlaufenden Index 0..n
, während Objekte beliebige Schlüssel auf beliebige Werte abbilden. Wenn Sie bestimmte Schlüssel angeben möchte, können Sie nur ein Objekt auswählen. Wenn Sie sich nicht für die Schlüssel interessieren, ist es ein Array.
Wenn Sie versuchen, beliebige (numerische) Schlüssel in einem Array zu setzen, haben Sie wirklich eine Performance loss, da das Array alle Indizes dazwischen füllt:
> foo = [];
[]
> foo[100] = 'a';
"a"
> foo
[undefined, undefined, undefined, ..., "a"]
(Beachten Sie, dass das Array nicht tatsächlich 99 undefined
-Werte enthält, aber es wird sich so verhalten, da Sie das Array an einem bestimmten Punkt iterierend
Die Literale für beide Optionen sollten deutlich machen, wie sie verwendet werden können:
var arr = ['foo', 'bar', 'baz']; // no keys, not even the option for it
var obj = { foo : 'bar', baz : 42 }; // associative by its very nature
Bei ES6 wäre die Verwendung einer Map die leistungsfähigste Methode.
var myMap = new Map();
myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });
myMap.get(1);
myMap.get(2);
Sie können ES6-Funktionen heute mit einem Shim verwenden ( https://github.com/es-shims/es6-shim ).
Die Leistung variiert je nach Browser und Szenario. Hier ist ein Beispiel, bei dem Map
am performantesten ist: https://jsperf.com/es6-map-vs-object-properties/2
REFERENCE https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Map
In NodeJS Wenn Sie ID
kennen, ist das Durchlaufen des Arrays im Vergleich zu object[ID]
sehr langsam.
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');
Und die Ergebnisse:
Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms
Auch wenn die Such-ID die erste im Array/Objekt ist:
Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms
Ich habe versucht, dies buchstäblich in die nächste Dimension zu bringen.
Ist ein zweidimensionales Array, in dem die x- und y-Achse immer die gleiche Länge haben, schneller:
a) Nachschlagen der Zelle durch Erstellen eines zweidimensionalen Arrays und Nachschlagen des ersten Index, gefolgt vom zweiten Index, d.h.
var arr=[][]
var cell=[x][y]
oder
b) Erstellen Sie ein Objekt mit einer Zeichenfolgendarstellung der X- und Y-Koordinaten, und führen Sie dann eine einzelne Suche nach diesem Objekt durch, d. h.
var obj={}
var cell = obj['x,y']
Ergebnis:
Es stellt sich heraus, dass es viel schneller ist, zwei numerische Index-Lookups für Arrays durchzuführen als ein Property-Lookup für das Objekt.
Ergebnisse hier:
Das hängt von der Nutzung ab. Wenn es sich um Suchobjekte handelt, ist das sehr viel schneller.
Hier ist ein Plunker-Beispiel zum Testen der Leistung von Array- und Objektsuchen.
https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview
Sie werden sehen, dass; Nach 5.000 Elementen in 5.000 Längenfeldsammlung suchen, übernehmen Sie 3000
Milisecons
Wenn Sie jedoch nach 5.000 -Objekten in einem Objekt suchen, verfügt es über 5.000 - Eigenschaften. Nehmen Sie nur 2
oder 3
-Milisecons
Objektbaum zu machen macht keinen großen Unterschied
Suchfeld: O(n) = n
Objekt nach Schlüssel suchen: O(n) = 1
Objekte sind also besser.
Wenn Sie ein sortiertes Array haben, können Sie eine binäre Suche durchführen und das ist viel schneller als eine Objektsuche. Sie können meine Antwort hier sehen:
Wie man mit Javascript schneller in einem sortierten Array sucht
Ich hatte ein ähnliches Problem, dem ich gegenüber stehe, wo ich Live-Kerzenhalter aus einer Ereignisquelle aufbewahren muss, die auf x Elemente beschränkt ist. Ich könnte sie in einem Objekt aufbewahren lassen, wo der Zeitstempel jeder Kerze als Schlüssel und die Kerze selbst als Wert fungieren würde. Eine andere Möglichkeit war, dass ich es in einem Array aufbewahren konnte, in dem jedes Element die Kerze selbst war. Ein Problem bei Live-Kerzen ist das Senden von Updates zu demselben Zeitpunkt, zu dem das letzte Update die neuesten Daten enthält. Daher aktualisieren Sie entweder ein vorhandenes Element oder fügen ein neues hinzu. Hier ist also ein schöner Benchmark, der versucht, alle drei Möglichkeiten zu kombinieren. Arrays in der folgenden Lösung sind im Durchschnitt mindestens 4x schneller. Fühlen Sie sich frei zu spielen
"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)
}
Schlussfolgerung 10 ist hier die Grenze
Storing as objects 4183 nanoseconds 10
Storing as array 373 nanoseconds 10