ECMAScript 2015: Estruturas de dados eficientes

ES2015 Gotchas é uma série quinzenal sobre as novidades e pegadinhas do ES2015.

Algumas estruturas de dados comumente utilizadas por grande parte dos algoritmos costumam ter implementações próprias e mais eficientes com suporte da própria linguagem, algo inexistente no javascript até então. Agora com o ES2015 temos 4 estruturas novas! Sempre que precisarmos de pilhas de dados com valores únicos podemos usar um Set ou WeakSet, ou se precisarmos de um mapa de chave valor de chave única podemos usar um Map ou WeakMap. Por que temos duas opções para Sets e Maps? Bora lá:

Estruturas iteráveis

Maps e Sets comuns são os mais simples de entender, funcionando com a maioria esperaria, podemos iterar sobre eles através de loopings, funcionando perfeitamente com o mais novo for...of do ES2015 navegando diretamente sobre os itens da coleção, diferente do antigofor...in do ES5 que navega sobre os índices. Também podemos verificar seu tamanho com a contagem total de itens, lembrando, sempre únicos.

Veja o Set em ação:

var mySet = new Set();

mySet.add("hello")  
     .add("hello")
     .add(123);

console.log(mySet.has("hello")); // true

console.log(mySet.size); // 2

for(i of mySet) console.log(i);  
  // hello
  // 123

Um Map funciona de forma bem parecida:

var myMap = new Map();

myMap.set("hello", 42)  
     .set("hello", 34)
     .set(123, "value");

console.log(myMap.get("hello")); // 34

console.log(myMap.size); // 2

for(i of myMap) console.log(i);  
  // [ 'hello', 34 ]
  // [ 123, 'value' ]

Estruturas fracas

WeakMap e WeakSet, são conhecidos como leak free, que por tradução livre seria livres de vazamento, funcionam de forma muito parecida com seus irmãos de nomes correspondentes, mas é possível usar apenas objetos como chave dessas estruturas, qualquer tentativa de usar um tipo primitivo, como números, strings e etc, resultarão em um erro de execução, e mais, são estas estruturas de dado não são iteráveis e não temos como saber o total de itens de cada coleção.

Vamos ao código, veja o WeakSet funcionando:

var myWSet = new WeakSet(),  
    myObj = { abc: 123 };

myWSet.add(myObj)  
      .add(myObj)
      .add({});

console.log(myWSet.has(myObj)); // true

console.log(myWSet.size); // undefined

for(i of myWSet) console.log(i); // Throw Error  

E agora o WeakMap:

var myWMap = new WeakMap(),  
    myObj = { abc: 123 };

myWMap.set(myObj, 42)  
      .set(myObj, 34)
      .set({}, "value");

console.log(myWMap.get(myObj)); // 34

console.log(myWMap.size); // undefined

for(i of myWMap) console.log(i); // Throw Error  

Todas essas limitações vem com uma troca justa, pois essas coleções não guardam uma referencia direta dos objetos utilizados como chaves, sendo assim é possível utiliza-los nestas estruturas sem medo de segurar referencias a esses objetos e impedi-los de serem coletados pelo coletor de lixo do javascript, segurando fantasmas que podem continuar a responder a eventos e entupindo sua memória disponível.

Um exemplo de utilização dessas estruturas seria distribuir alguns nós de DOM que você esta gerenciando em pilhas para dividi-los em tipos, e sempre poder verificar é de qual tipo, mas essas nós nascem e morrem por diversos motivos em vários momentos e para evitar que fique com vazamento de memória de nós fantasmas, que talvez alem de tudo ainda respondam a eventos e etc, você pode simplesmente utilizar um WeakSet para não ter de gerenciar a exclusão de referencias aos nós excluídos, e assim ser devidamente recolhido pelo garbage collector do javascript automaticamente. Tudo isso claro, se não tiver a necessidade de iterar diretamente pelos tipos de nós.

Até a próxima! Fiquem ligados na série ES2015 Gotchas!

William Grasel

Read more posts by this author.