web-dev-qa-db-fra.com

Javascript ES6 complexité de calcul / temps des collections

Quelle complexité temporelle (en notation big-O) est fournie par la spécification ES6 pour les collections à clés (Set, Map, WeakSet et WeakMap)?

Mon attente, et j'attends celle de la plupart des développeurs, est que les spécifications et implémentations utilisent largement acceptées des algorithmes performants, auquel cas Set.prototype.has, add et delete pour que tous soient O(1) dans le cas moyen. De même pour les Map et Weak– équivalents.

Il n'est pas tout à fait évident pour moi si la complexité temporelle des implémentations était obligatoire, par exemple in ECMAScript 2015 Language Specification - 6th Edition - 23.2 Set Objects .

À moins que je ne le comprenne mal (et c'est certainement très possible, je le fais), il semble que la spécification ECMA exige que les implémentations (par exemple Set.prototype.has ) doivent utiliser un algorithme de temps linéaire ( O (n) ). Il me semblerait extrêmement surprenant que des algorithmes plus performants ne soient pas mandatés ni même autorisés par la spécification, et je serais très intéressé par une explication de la raison pour laquelle c'est le cas.

56
Brian M. Hunt

Dès ce même paragraphe votre lien vers:

Les objets set doivent être implémentés à l'aide de [mécanismes] qui, en moyenne, fournissent des temps d'accès qui sont sublinéaires par rapport au nombre d'éléments dans la collection.

Vous trouverez la même phrase pour Maps , WeakMaps et WeakSets .

Il semble que la spécification ECMA stipule que les implémentations (par exemple Set.prototype.has) doivent utiliser un algorithme de temps linéaire (O(n)).

Non:

Les structures de données utilisées dans cette spécification d'objets Set sont uniquement destinées à décrire la sémantique observable requise des objets Set. Il n'est pas destiné à être un modèle d'implémentation viable.

La sémantique observable est principalement liée à l'ordre d'itération prévisible (qui peut toujours être mis en œuvre efficace et rapide ). Il est en effet prévu par la spécification qu'une implémentation utilise une table de hachage ou quelque chose de similaire avec un accès constant, bien que les arbres (avec une complexité d'accès logarithmique) soient également autorisés.

41
Bergi

Pour tous ceux qui sont curieux, j'ai fait un benchmark très rapide:

const benchmarkMap = size => {
  console.time('benchmarkMap');
  var map = new Map();
  for (var i = 0; i < size; i++) map.set(i, i);
  for (var i = 0; i < size; i++) var x = map.get(i);
  console.timeEnd('benchmarkMap');
}

const benchmarkObj = size => {
  console.time('benchmarkObj');
  var obj = {};
  for (var i = 0; i < size; i++) obj[i] = i;
  for (var i = 0; i < size; i++) var x = obj[i];
  console.timeEnd('benchmarkObj');
}

var size = 1000000;

benchmarkMap(size);
benchmarkObj(size);

J'ai couru cela plusieurs fois et j'ai donné les résultats suivants:

(MacBook Pro 2017, i7 2,5 GHz avec RAM 16 G)

benchmarkMap: 189.120ms
benchmarkObj: 44.214ms

benchmarkMap: 200.817ms
benchmarkObj: 38.963ms

benchmarkMap: 187.968ms
benchmarkObj: 41.633ms

benchmarkMap: 186.533ms
benchmarkObj: 35.850ms

benchmarkMap: 187.339ms
benchmarkObj: 44.515ms
11
domdambrogia