it-swarm.com.de

Array vs. Objekteffizienz in JavaScript

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?

102
Moshe Shaham

Die kurze Version: Arrays sind meistens schneller als Objekte. Es gibt jedoch keine zu 100% korrekte Lösung.

Update 2017 - Test und Ergebnisse

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'});
}

 test setup  test results

Originalpost - Erklärung

In Ihrer Frage gibt es einige falsche Vorstellungen.

Es gibt keine assoziativen Arrays in Javascript. Nur Arrays und Objekte.

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:

Lookup-Array vs. Holey-Array im Vergleich zu Objektleistungstest

Eine ausgezeichnete Lektüre zu diesen Themen im Smashing Magazine: Schnelles speichereffizientes JavaScript schreiben

119
Alp

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
19
deceze

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

11
sandstrom

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
3
Paweł

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:

http://jsperf.com/arr-vs-obj-lookup-2

3
Davem M

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

2
Mehmet Otkun

Suchfeld: O(n) = n

Objekt nach Schlüssel suchen: O(n) = 1

Objekte sind also besser.

1

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

0
Enrique

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
0
PirateApp