web-dev-qa-db-fra.com

Array vs efficacité des objets en JavaScript

J'ai un modèle avec éventuellement des milliers d'objets. Je me demandais quel serait le moyen le plus efficace de les stocker et de récupérer un seul objet une fois que j'ai son identifiant. Les identifiants sont des nombres longs.

Donc, ce sont les 2 options que je pensais. dans l'option un c'est un tableau simple avec un index incrémentant. dans l'option 2, il s'agit d'un tableau associatif et peut-être d'un objet, si cela fait une différence. Ma question est de savoir lequel est le plus efficace, lorsque je dois surtout récupérer un seul objet, mais aussi parfois les parcourir et les trier.

Option 1 avec tableau non associatif:

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 deux avec tableau associatif:

var a = [];  // maybe {} makes a difference?
a[29938] = {id: 29938, name: 'name1'};
a[32994] = {id: 32994, name: 'name1'};
function getObject(id) {
    return a[id];
}

Mettre à jour:

OK, je comprends que l'utilisation d'un tableau dans la deuxième option est hors de question. Ainsi, la ligne de déclaration de la deuxième option devrait être réellement: var a = {}; et la seule question à poser est celle-ci: qu'est-ce qui est le mieux pour extraire un objet avec un identifiant donné: un tableau ou un objet où l'identifiant est la clé?.

et aussi, la réponse changera-t-elle si je dois trier la liste plusieurs fois?

102
Moshe Shaham

La version courte: Les tableaux sont généralement plus rapides que les objets. Mais il n'y a pas de solution correcte à 100%.

Mise à jour 2017 - Test et résultats

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

Message original - Explication

Il y a des idées fausses dans votre question.

Il n'y a pas de tableaux associatifs en Javascript. Seuls les tableaux et les objets.

Ce sont des tableaux:

var a1 = [1, 2, 3];
var a2 = ["a", "b", "c"];
var a3 = [];
a3[0] = "a";
a3[1] = "b";
a3[2] = "c";

C'est aussi un tableau:

var a3 = [];
a3[29938] = "a";
a3[32994] = "b";

C'est en gros un tableau avec des trous, car chaque tableau a une indexation continue. C'est plus lent que les tableaux sans trous. Mais itérer manuellement à travers le tableau est encore plus lent (surtout).

Ceci est un objet:

var a3 = {};
a3[29938] = "a";
a3[32994] = "b";

Voici un test de performance de trois possibilités:

Lookup Array vs Holey Array vs test de performance d'objet

Une excellente lecture de ces sujets sur Smashing Magazine: Rédaction rapide de mémoire efficace JavaScript

119
Alp

Ce n'est pas vraiment une question de performance, car les tableaux et les objets fonctionnent très différemment (ou sont supposés le faire, du moins). Les tableaux ont un index continu 0..n, tandis que les objets mappent des clés arbitraires en valeurs arbitraires. Si vous voulez fournir des clés spécifiques, le seul choix est un objet. Si vous ne vous souciez pas des clés, c'est un tableau.

Si vous essayez de définir des clés (numériques) arbitraires sur un tableau, vous avez réellement une performance perte , puisque le tableau remplit tous les index entre les deux:

> foo = [];
  []
> foo[100] = 'a';
  "a"
> foo
  [undefined, undefined, undefined, ..., "a"]

(Notez que le tableau ne contient pas en fait contient 99 valeurs undefined, mais il se comportera de cette façon puisque vous êtes [censé être] itérer le tableau à un moment donné.)

Les littéraux des deux options doivent indiquer très clairement comment ils peuvent être utilisés:

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

Avec ES6, le moyen le plus performant serait d’utiliser une carte.

var myMap = new Map();

myMap.set(1, 'myVal');
myMap.set(2, { catName: 'Meow', age: 3 });

myMap.get(1);
myMap.get(2);

Vous pouvez utiliser les fonctionnalités ES6 aujourd'hui à l'aide d'un shim ( https://github.com/es-shims/es6-shim ).

Les performances varient en fonction du navigateur et du scénario. Mais voici un exemple où Map est le plus performant: https://jsperf.com/es6-map-vs-object-properties/2


REFERENCE https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map

11
sandstrom

Dans NodeJS si vous connaissez la ID, la boucle dans le tableau est très lente comparée à 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');

Et les résultats:

Array result:
arrTimer: 12.857ms
Object result:
objTimer: 0.051ms

Même si l'ID de recherche est le premier dans le tableau/objet:

Array result:
arrTimer: 2.975ms
Object result:
objTimer: 0.068ms
3
Paweł

J'ai essayé de prendre cela à la prochaine dimension, littéralement. 

Étant donné un tableau à 2 dimensions, dans lequel les axes x et y ont toujours la même longueur, est-il plus rapide de:

a) recherchez la cellule en créant un tableau à deux dimensions et en recherchant le premier index, suivi du second, c.-à-d. 

var arr=[][]    
var cell=[x][y]    

ou

b) créez un objet avec une représentation sous forme de chaîne des coordonnées x et y, puis effectuez une recherche unique sur cet objet, à savoir: 

var obj={}    
var cell = obj['x,y']    

Résultat:
Il s'avère qu'il est beaucoup plus rapide de faire deux recherches d'index numérique sur les tableaux, qu'une recherche de propriété sur l'objet.

Résultats ici:

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

3
Davem M

Cela dépend de l'utilisation. Si le cas est la recherche d'objets est très rapide. 

Voici un exemple Plunker pour tester les performances des recherches sur des tableaux et des objets.

https://plnkr.co/edit/n2expPWVmsdR3zmXvX4C?p=preview

Vous verrez que; En recherchant les éléments 5.000 dans la collection de tableaux 5.000, prenez 3000 milisecons

Cependant, la recherche d'éléments 5.000 dans l'objet possède des propriétés 5.000, n'utilisez que 2 ou 3 milisecons.

Faire aussi des objets ne font pas une grande différence

2
Mehmet Otkun

Tableau de recherche: O(n) = n

Rechercher un objet par clé: O(n) = 1

Donc les objets sont meilleurs.

1
Alexander Danilov

Si vous avez un tableau trié, vous pouvez faire une recherche binaire et c'est beaucoup plus rapide qu'une recherche d'objet, vous pouvez voir ma réponse ici:
Comment rechercher plus rapidement dans un tableau trié en utilisant Javascript

0
Enrique

J'ai eu un problème similaire auquel je suis confronté lorsque j'ai besoin de stocker des chandeliers vivants provenant d'une source d'événements limitée à x éléments. Je pourrais les avoir stockées dans un objet où l'horodatage de chaque bougie agirait comme la clé et la bougie elle-même agirait comme la valeur. Une autre possibilité était que je puisse le stocker dans un tableau où chaque élément était la bougie elle-même. Un problème avec les bougies actives est qu’elles envoient des mises à jour à la même heure que la dernière mise à jour contenant les données les plus récentes. Vous devez donc mettre à jour un élément existant ou en ajouter un nouveau. Voici donc un repère de Nice qui tente de combiner les 3 possibilités. Les tableaux de la solution ci-dessous sont au moins 4 fois plus rapides en moyenne. N'hésitez pas à jouer

"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)
}

Conclusion 10 est la limite ici

Storing as objects 4183 nanoseconds 10
Storing as array 373 nanoseconds 10
0
PirateApp