JavaScript Hashmap Equivalent

Multi tool use
Multi tool use












273














As made clear in update 3 on this answer, this notation:



var hash = {};
hash[X]


does not actually hash the object X; it actually just converts X to a string (via .toString() if it's an object, or some other built-in conversions for various primitive types) and then looks that string up, without hashing it, in "hash". Object equality is also not checked - if two different objects have the same string conversion, they will just overwrite each other.



Given this - are there any efficient implementations of hashmaps in javascript? (For example, the 2nd Google result of javascript hashmap yields an implementation which is O(n) for any operation. Various other results ignore the fact that different objects with equivalent string representations overwrite each other.










share|improve this question




















  • 1




    @Claudiu: Sorry for the edit, but the "Map" in the title was really misleading. Roll back if you disagree, I did not intend to patronize. :)
    – Tomalak
    Dec 15 '08 at 13:05






  • 4




    @Claudiu: You ask a lot of questions about javascript. Good questions. I like that.
    – some
    Dec 15 '08 at 13:39






  • 2




    @Claudiu: Also, could you link to the Google result you refer to? Different local versions of Google return different results, the implementation you refer to does not even seem to show up for me.
    – Tomalak
    Dec 15 '08 at 14:08










  • @Tomalak: I was just going to write exactly the same thing!
    – some
    Dec 15 '08 at 14:11






  • 3




    @Claudiu No, don't link to google. Link to the page you were talking about (which you happened to find through google). Linking to google has all the same problems as explaining what to search for: google customizing results based on location or on search history, google's results changing over time (currently, this is the top result for that search) and anything else that can make it show different results.
    – Jasper
    Jun 30 '14 at 12:12
















273














As made clear in update 3 on this answer, this notation:



var hash = {};
hash[X]


does not actually hash the object X; it actually just converts X to a string (via .toString() if it's an object, or some other built-in conversions for various primitive types) and then looks that string up, without hashing it, in "hash". Object equality is also not checked - if two different objects have the same string conversion, they will just overwrite each other.



Given this - are there any efficient implementations of hashmaps in javascript? (For example, the 2nd Google result of javascript hashmap yields an implementation which is O(n) for any operation. Various other results ignore the fact that different objects with equivalent string representations overwrite each other.










share|improve this question




















  • 1




    @Claudiu: Sorry for the edit, but the "Map" in the title was really misleading. Roll back if you disagree, I did not intend to patronize. :)
    – Tomalak
    Dec 15 '08 at 13:05






  • 4




    @Claudiu: You ask a lot of questions about javascript. Good questions. I like that.
    – some
    Dec 15 '08 at 13:39






  • 2




    @Claudiu: Also, could you link to the Google result you refer to? Different local versions of Google return different results, the implementation you refer to does not even seem to show up for me.
    – Tomalak
    Dec 15 '08 at 14:08










  • @Tomalak: I was just going to write exactly the same thing!
    – some
    Dec 15 '08 at 14:11






  • 3




    @Claudiu No, don't link to google. Link to the page you were talking about (which you happened to find through google). Linking to google has all the same problems as explaining what to search for: google customizing results based on location or on search history, google's results changing over time (currently, this is the top result for that search) and anything else that can make it show different results.
    – Jasper
    Jun 30 '14 at 12:12














273












273








273


142





As made clear in update 3 on this answer, this notation:



var hash = {};
hash[X]


does not actually hash the object X; it actually just converts X to a string (via .toString() if it's an object, or some other built-in conversions for various primitive types) and then looks that string up, without hashing it, in "hash". Object equality is also not checked - if two different objects have the same string conversion, they will just overwrite each other.



Given this - are there any efficient implementations of hashmaps in javascript? (For example, the 2nd Google result of javascript hashmap yields an implementation which is O(n) for any operation. Various other results ignore the fact that different objects with equivalent string representations overwrite each other.










share|improve this question















As made clear in update 3 on this answer, this notation:



var hash = {};
hash[X]


does not actually hash the object X; it actually just converts X to a string (via .toString() if it's an object, or some other built-in conversions for various primitive types) and then looks that string up, without hashing it, in "hash". Object equality is also not checked - if two different objects have the same string conversion, they will just overwrite each other.



Given this - are there any efficient implementations of hashmaps in javascript? (For example, the 2nd Google result of javascript hashmap yields an implementation which is O(n) for any operation. Various other results ignore the fact that different objects with equivalent string representations overwrite each other.







javascript data-structures language-features hashmap






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 23 '17 at 10:31









Community

11




11










asked Dec 15 '08 at 12:56









Claudiu

125k120392577




125k120392577








  • 1




    @Claudiu: Sorry for the edit, but the "Map" in the title was really misleading. Roll back if you disagree, I did not intend to patronize. :)
    – Tomalak
    Dec 15 '08 at 13:05






  • 4




    @Claudiu: You ask a lot of questions about javascript. Good questions. I like that.
    – some
    Dec 15 '08 at 13:39






  • 2




    @Claudiu: Also, could you link to the Google result you refer to? Different local versions of Google return different results, the implementation you refer to does not even seem to show up for me.
    – Tomalak
    Dec 15 '08 at 14:08










  • @Tomalak: I was just going to write exactly the same thing!
    – some
    Dec 15 '08 at 14:11






  • 3




    @Claudiu No, don't link to google. Link to the page you were talking about (which you happened to find through google). Linking to google has all the same problems as explaining what to search for: google customizing results based on location or on search history, google's results changing over time (currently, this is the top result for that search) and anything else that can make it show different results.
    – Jasper
    Jun 30 '14 at 12:12














  • 1




    @Claudiu: Sorry for the edit, but the "Map" in the title was really misleading. Roll back if you disagree, I did not intend to patronize. :)
    – Tomalak
    Dec 15 '08 at 13:05






  • 4




    @Claudiu: You ask a lot of questions about javascript. Good questions. I like that.
    – some
    Dec 15 '08 at 13:39






  • 2




    @Claudiu: Also, could you link to the Google result you refer to? Different local versions of Google return different results, the implementation you refer to does not even seem to show up for me.
    – Tomalak
    Dec 15 '08 at 14:08










  • @Tomalak: I was just going to write exactly the same thing!
    – some
    Dec 15 '08 at 14:11






  • 3




    @Claudiu No, don't link to google. Link to the page you were talking about (which you happened to find through google). Linking to google has all the same problems as explaining what to search for: google customizing results based on location or on search history, google's results changing over time (currently, this is the top result for that search) and anything else that can make it show different results.
    – Jasper
    Jun 30 '14 at 12:12








1




1




@Claudiu: Sorry for the edit, but the "Map" in the title was really misleading. Roll back if you disagree, I did not intend to patronize. :)
– Tomalak
Dec 15 '08 at 13:05




@Claudiu: Sorry for the edit, but the "Map" in the title was really misleading. Roll back if you disagree, I did not intend to patronize. :)
– Tomalak
Dec 15 '08 at 13:05




4




4




@Claudiu: You ask a lot of questions about javascript. Good questions. I like that.
– some
Dec 15 '08 at 13:39




@Claudiu: You ask a lot of questions about javascript. Good questions. I like that.
– some
Dec 15 '08 at 13:39




2




2




@Claudiu: Also, could you link to the Google result you refer to? Different local versions of Google return different results, the implementation you refer to does not even seem to show up for me.
– Tomalak
Dec 15 '08 at 14:08




@Claudiu: Also, could you link to the Google result you refer to? Different local versions of Google return different results, the implementation you refer to does not even seem to show up for me.
– Tomalak
Dec 15 '08 at 14:08












@Tomalak: I was just going to write exactly the same thing!
– some
Dec 15 '08 at 14:11




@Tomalak: I was just going to write exactly the same thing!
– some
Dec 15 '08 at 14:11




3




3




@Claudiu No, don't link to google. Link to the page you were talking about (which you happened to find through google). Linking to google has all the same problems as explaining what to search for: google customizing results based on location or on search history, google's results changing over time (currently, this is the top result for that search) and anything else that can make it show different results.
– Jasper
Jun 30 '14 at 12:12




@Claudiu No, don't link to google. Link to the page you were talking about (which you happened to find through google). Linking to google has all the same problems as explaining what to search for: google customizing results based on location or on search history, google's results changing over time (currently, this is the top result for that search) and anything else that can make it show different results.
– Jasper
Jun 30 '14 at 12:12












17 Answers
17






active

oldest

votes


















286














Why not hash your objects yourself manually, and use the resulting strings as keys for a regular JavaScript dictionary? After all you are in the best position to know what makes your objects unique. That's what I do.



Example:



var key = function(obj){
// some unique object-dependent key
return obj.totallyUniqueEmployeeIdKey; // just an example
};

var dict = {};

dict[key(obj1)] = obj1;
dict[key(obj2)] = obj2;


This way you can control indexing done by JavaScript without heavy lifting of memory allocation, and overflow handling.



Of course, if you truly want the "industrial-grade solution", you can build a class parameterized by the key function, and with all necessary API of the container, but … we use JavaScript, and trying to be simple and lightweight, so this functional solution is simple and fast.



The key function can be as simple as selecting right attributes of the object, e.g., a key, or a set of keys, which are already unique, a combination of keys, which are unique together, or as complex as using some cryptographic hashes like in DojoX Encoding, or DojoX UUID. While the latter solutions may produce unique keys, personally I try to avoid them at all costs, especially, if I know what makes my objects unique.



Update in 2014: Answered back in 2008 this simple solution still requires more explanations. Let me clarify the idea in a Q&A form.



Your solution doesn't have a real hash. Where is it???



JavaScript is a high-level language. Its basic primitive
(Object)
includes a hash table to keep properties. This hash table is usually written
in a low-level language for efficiency. Using a simple object with string keys we use an efficiently implemented hash table with no efforts on our part.



How do you know they use hash?



There are three major ways to keep a collection of objects addressable by a key:




  • Unordered. In this case to retrieve an object by its key we have to go over all keys stopping when we find it. On average it will take n/2 comparisons.

  • Ordered.


    • Example #1: a sorted array — doing a binary search we will find our key after ~log2(n) comparisons on average. Much better.

    • Example #2: a tree. Again it'll be ~log(n) attempts.



  • Hash table. On average it requires a constant time. Compare: O(n) vs. O(log n) vs. O(1). Boom.


Obviously JavaScript objects use hash tables in some form to handle general cases.



Do browser vendors really use hash tables???



Really.




  • Chrome/node.js/V8:
    JSObject. Look for
    NameDictionary and
    NameDictionaryShape with
    pertinent details in objects.cc
    and objects-inl.h.

  • Firefox/Gecko:
    JSObject,
    NativeObject, and
    PlainObject with pertinent details in
    jsobj.cpp and
    vm/NativeObject.cpp.


Do they handle collisions?



Yes. See above. If you found a collision on unequal strings, please do not hesitate to file a bug with a vendor.



So what is your idea?



If you want to hash an object, find what makes it unique and use it as a key. Do not try to calculate real hash or emulate hash tables — it is already efficiently handled by the underlying JavaScript object.



Use this key with JavaScript Object to leverage its built-in hash table while steering clear of possible clashes with default properties.



Examples to get you started:




  • If your objects include a unique user name — use it as a key.

  • If it includes a unique customer number — use it as a key.


    • If it includes unique government-issued numbers like an SSN, or a passport number, and your system doesn't allow duplicates — use it as a key.



  • If a combination of fields is unique — use it as a key.


    • State abbreviation + driver license number makes an excellent key.

    • Country abbreviation + passport number is an excellent key too.



  • Some function on fields, or a whole object, can return a unique value — use it as a key.


I used your suggestion and cached all objects using a user name. But some wise guy is named "toString", which is a built-in property! What should I do now?



Obviously, if it is even remotely possible that the resulting key will exclusively consists of Latin characters, you should do something about it. For example, add any non-Latin Unicode character you like at the beginning or at the end to un-clash with default properties: "#toString", "#MarySmith". If a composite key is used, separate key components using some kind of non-Latin delimiter: "name,city,state".



In general this is the place, where we have to be creative, and select the easiest keys with given limitations (uniqueness, potential clashes with default properties).



Note: unique keys do not clash by definition, while potential hash clashes will be handled by the underlying Object.



Why don't you like industrial solutions?



IMHO, the best code is no code at all: it has no errors, requires no maintenance, easy to understand, and executes instantaneously. All "hash tables in JavaScript" I saw were >100 lines of code, and involved multiple objects. Compare it with: dict[key] = value.



Another point: is it even possible to beat a performance of a primordial object written in a low-level language, using JavaScript and the very same primordial objects to implement what is already implemented?



I still want to hash my objects without any keys!



We are in luck: ECMAScript 6 (scheduled for mid 2015 release, give or take 1-2 years after that to become widespread) defines
map and
set.



Judging by the definition they can use object's address as a key, which makes objects instantly distinct without artificial keys. OTOH, two different, yet identical objects, will be mapped as distinct.



Comparison breakdown from MDN:




Objects are similar to Maps in that both let you set keys to values,
retrieve those values, delete keys, and detect whether something is
stored at a key. Because of this (and because there were no built-in
alternatives), Objects have been used as Maps historically; however,
there are important differences that make using a Map preferable in
certain cases:




  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.

  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of
    insertion.

  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.

  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion
    and iterating over them.

  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can
    be bypassed by using map = Object.create(null), but this is seldom
    done.

  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.







share|improve this answer



















  • 11




    This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
    – beefeather
    Dec 2 '12 at 4:14






  • 28




    Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
    – Matt R
    Feb 11 '13 at 11:05






  • 26




    @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
    – Eugene Lazutkin
    Feb 13 '13 at 19:23






  • 4




    @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
    – Matt R
    Feb 22 '13 at 7:53






  • 5




    @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
    – Matt R
    Feb 24 '13 at 8:39



















167














Problem description



JavaScript has no built-in general map type (sometimes called associative array or dictionary) which allows to access arbitrary values by arbitrary keys. JavaScript's fundamental data structure is the object, a special type of map which only accepts strings as keys and has special semantics like prototypical inheritance, getters and setters and some further voodoo.



When usings objects as maps, you have to remember that the key will be converted to a string value via toString(), which results in mapping 5 and '5' to the same value and all objects which don't overwrite the toString() method to the value indexed by '[object Object]'. You might also involuntarily access its inherited properties if you don't check hasOwnProperty().



JavaScript's built-in array type does not help one bit: JavaScript arrays are not associative arrays, but just objects with a few more special properties. If you want to know why they can't be used as maps, look here.



Eugene's Solution



Eugene Lazutkin already described the basic idea of using a custom hash function to generate unique strings which can be used to look up the associated values as properties of a dictionary object. This will most likely be the fastest solution, because objects are internally implemented as hash tables.





  • Note: Hash tables (sometimes called hash maps) are a particular implementation of the map concept using a backing array and lookup via numeric hash values. The runtime environment might use other structures (such as search trees or skip lists) to implement JavaScript objects, but as objects are the fundamental data structure, they should be sufficiently optimised.


In order to get a unique hash value for arbitrary objects, one possibility is to use a global counter and cache the hash value in the object itself (eg in a property named __hash).



A hash function which does this is and works for both primitive values and objects is:



function hash(value) {
return (typeof value) + ' ' + (value instanceof Object ?
(value.__hash || (value.__hash = ++arguments.callee.current)) :
value.toString());
}

hash.current = 0;


This function can be used as described by Eugene. For convenience, we will further wrap it in a Map class.



My Map implementation



The following implementation will additionally store the key-value-pairs in a doubly linked list in order to allow fast iteration over both keys and values. To supply your own hash function, you can overwrite the instance's hash() method after creation.



// linking the key-value-pairs is optional
// if no argument is provided, linkItems === undefined, i.e. !== false
// --> linking will be enabled
function Map(linkItems) {
this.current = undefined;
this.size = 0;

if(linkItems === false)
this.disableLinking();
}

Map.noop = function() {
return this;
};

Map.illegal = function() {
throw new Error("illegal operation for maps without linking");
};

// map initialisation from existing object
// doesn't add inherited properties if not explicitly instructed to:
// omitting foreignKeys means foreignKeys === undefined, i.e. == false
// --> inherited properties won't be added
Map.from = function(obj, foreignKeys) {
var map = new Map;

for(var prop in obj) {
if(foreignKeys || obj.hasOwnProperty(prop))
map.put(prop, obj[prop]);
}

return map;
};

Map.prototype.disableLinking = function() {
this.link = Map.noop;
this.unlink = Map.noop;
this.disableLinking = Map.noop;
this.next = Map.illegal;
this.key = Map.illegal;
this.value = Map.illegal;
this.removeAll = Map.illegal;

return this;
};

// overwrite in Map instance if necessary
Map.prototype.hash = function(value) {
return (typeof value) + ' ' + (value instanceof Object ?
(value.__hash || (value.__hash = ++arguments.callee.current)) :
value.toString());
};

Map.prototype.hash.current = 0;

// --- mapping functions

Map.prototype.get = function(key) {
var item = this[this.hash(key)];
return item === undefined ? undefined : item.value;
};

Map.prototype.put = function(key, value) {
var hash = this.hash(key);

if(this[hash] === undefined) {
var item = { key : key, value : value };
this[hash] = item;

this.link(item);
++this.size;
}
else this[hash].value = value;

return this;
};

Map.prototype.remove = function(key) {
var hash = this.hash(key);
var item = this[hash];

if(item !== undefined) {
--this.size;
this.unlink(item);

delete this[hash];
}

return this;
};

// only works if linked
Map.prototype.removeAll = function() {
while(this.size)
this.remove(this.key());

return this;
};

// --- linked list helper functions

Map.prototype.link = function(item) {
if(this.size == 0) {
item.prev = item;
item.next = item;
this.current = item;
}
else {
item.prev = this.current.prev;
item.prev.next = item;
item.next = this.current;
this.current.prev = item;
}
};

Map.prototype.unlink = function(item) {
if(this.size == 0)
this.current = undefined;
else {
item.prev.next = item.next;
item.next.prev = item.prev;
if(item === this.current)
this.current = item.next;
}
};

// --- iterator functions - only work if map is linked

Map.prototype.next = function() {
this.current = this.current.next;
};

Map.prototype.key = function() {
return this.current.key;
};

Map.prototype.value = function() {
return this.current.value;
};


Example



The following script



var map = new Map;

map.put('spam', 'eggs').
put('foo', 'bar').
put('foo', 'baz').
put({}, 'an object').
put({}, 'another object').
put(5, 'five').
put(5, 'five again').
put('5', 'another five');

for(var i = 0; i++ < map.size; map.next())
document.writeln(map.hash(map.key()) + ' : ' + map.value());


generates this output:



string spam : eggs
string foo : baz
object 1 : an object
object 2 : another object
number 5 : five again
string 5 : another five


Further considerations



PEZ suggested to overwrite the toString() method, presumably with our hash function. This is not feasible because it doesn't work for primitive values (changing toString() for primitives is a very bad idea). If we want toString() to return meaningful values for arbitrary objects, we would have to modify Object.prototype, which some people (myself not included) consider verboten.





Edit: The current version of my Map implementation as well as other JavaScript goodies can be obtained from here.






share|improve this answer























  • ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
    – broofa
    Jan 18 '12 at 11:43










  • Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
    – ahcox
    Apr 9 '13 at 13:38










  • hi @Christoph, could you update your link to where I can find your Map implementation?
    – NumenorForLife
    Jul 30 '13 at 18:16






  • 2




    @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
    – Christoph
    Jul 30 '13 at 20:16





















39














I know this question is pretty old, but there are some really great solutions nowadays with external libraries.




  • collections.js

  • immutable


JavaScript also has its language provided Map as well.




  • Map






share|improve this answer



















  • 2




    This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
    – Phung D. An
    Jan 30 '15 at 18:21






  • 1




    Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
    – Code Bling
    Sep 23 '16 at 5:42












  • @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
    – Jamel Toms
    Oct 25 '16 at 15:12






  • 2




    That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
    – Code Bling
    Oct 25 '16 at 20:40



















19














Here is an easy and convenient way of using something similar to the java map:



var map= {
'map_name_1': map_value_1,
'map_name_2': map_value_2,
'map_name_3': map_value_3,
'map_name_4': map_value_4
}


And to get the value:



alert( map['map_name_1'] );    // fives the value of map_value_1

...... etc .....





share|improve this answer



















  • 3




    best answer right here +1
    – marcusshep
    May 31 '16 at 14:38






  • 2




    This works for string keys only. I believe the OP was interested in using keys of any type.
    – fractor
    Sep 19 '17 at 14:32



















18














You can use ES6 WeakMap or Map:




  • WeakMaps are key/value maps in which keys are objects.



  • Map objects are simple key/value maps. Any value (both objects and primitive values) may be used as either a key or a value.



Be aware that neither is widely supported, but you can use ES6 Shim (requires native ES5 or ES5 Shim) to support Map, but not WeakMap (see why).






share|improve this answer





























    14














    According to ECMAScript 2015 (ES6) standard javascript has a Map implementation. More about which could be found here



    Basic usage:



    var myMap = new Map();
    var keyString = "a string",
    keyObj = {},
    keyFunc = function () {};

    // setting the values
    myMap.set(keyString, "value associated with 'a string'");
    myMap.set(keyObj, "value associated with keyObj");
    myMap.set(keyFunc, "value associated with keyFunc");

    myMap.size; // 3

    // getting the values
    myMap.get(keyString); // "value associated with 'a string'"
    myMap.get(keyObj); // "value associated with keyObj"
    myMap.get(keyFunc); // "value associated with keyFunc"





    share|improve this answer





























      13














      You'd have to store in some internal state couplets of object/value pairs



      HashMap = function(){
      this._dict = ;
      }
      HashMap.prototype._get = function(key){
      for(var i=0, couplet; couplet = this._dict[i]; i++){
      if(couplet[0] === key){
      return couplet;
      }
      }
      }
      HashMap.prototype.put = function(key, value){
      var couplet = this._get(key);
      if(couplet){
      couplet[1] = value;
      }else{
      this._dict.push([key, value]);
      }
      return this; // for chaining
      }
      HashMap.prototype.get = function(key){
      var couplet = this._get(key);
      if(couplet){
      return couplet[1];
      }
      }


      And use it as such:



      var color = {}; // unique object instance
      var shape = {}; // unique object instance
      var map = new HashMap();
      map.put(color, "blue");
      map.put(shape, "round");
      console.log("Item is", map.get(color), "and", map.get(shape));


      Of course, this implementation is also somewhere along the lines of O(n). Eugene's examples above are the only way to get a hash that works with any sort of speed you'd expect from a real hash.



      Update:



      Another approach, along the lines of Eugene's answer is to somehow attach a unique ID to all objects. One of my favorite approaches is to take one of the built-in methods inherited from the Object superclass, replace it with a custom function passthrough and attach properties to that function object. If you were to rewrite my HashMap method to do this, it would look like:



      HashMap = function(){
      this._dict = {};
      }
      HashMap.prototype._shared = {id: 1};
      HashMap.prototype.put = function put(key, value){
      if(typeof key == "object"){
      if(!key.hasOwnProperty._id){
      key.hasOwnProperty = function(key){
      return Object.prototype.hasOwnProperty.call(this, key);
      }
      key.hasOwnProperty._id = this._shared.id++;
      }
      this._dict[key.hasOwnProperty._id] = value;
      }else{
      this._dict[key] = value;
      }
      return this; // for chaining
      }
      HashMap.prototype.get = function get(key){
      if(typeof key == "object"){
      return this._dict[key.hasOwnProperty._id];
      }
      return this._dict[key];
      }


      This version appears to be only slightly faster, but in theory it will be significantly faster for large data sets.






      share|improve this answer























      • An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
        – Erik Allik
        Nov 26 '11 at 14:06










      • True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
        – pottedmeat
        Jan 4 '12 at 18:46





















      10














      Unfortunately, none of the above answers were good for my case: different key objects may have the same hash code. Therefore, I wrote a simple Java-like HashMap version:



      function HashMap() {
      this.buckets = {};
      }

      HashMap.prototype.put = function(key, value) {
      var hashCode = key.hashCode();
      var bucket = this.buckets[hashCode];
      if (!bucket) {
      bucket = new Array();
      this.buckets[hashCode] = bucket;
      }
      for (var i = 0; i < bucket.length; ++i) {
      if (bucket[i].key.equals(key)) {
      bucket[i].value = value;
      return;
      }
      }
      bucket.push({ key: key, value: value });
      }

      HashMap.prototype.get = function(key) {
      var hashCode = key.hashCode();
      var bucket = this.buckets[hashCode];
      if (!bucket) {
      return null;
      }
      for (var i = 0; i < bucket.length; ++i) {
      if (bucket[i].key.equals(key)) {
      return bucket[i].value;
      }
      }
      }

      HashMap.prototype.keys = function() {
      var keys = new Array();
      for (var hashKey in this.buckets) {
      var bucket = this.buckets[hashKey];
      for (var i = 0; i < bucket.length; ++i) {
      keys.push(bucket[i].key);
      }
      }
      return keys;
      }

      HashMap.prototype.values = function() {
      var values = new Array();
      for (var hashKey in this.buckets) {
      var bucket = this.buckets[hashKey];
      for (var i = 0; i < bucket.length; ++i) {
      values.push(bucket[i].value);
      }
      }
      return values;
      }


      Note: key objects must "implement" hashCode() and equals() methods.






      share|improve this answer



















      • 7




        The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
        – Erik Allik
        Nov 27 '11 at 19:15



















      5














      I've implemented a JavaScript HashMap which code can be obtained from http://github.com/lambder/HashMapJS/tree/master



      Here is the code:



      /*
      =====================================================================
      @license MIT
      @author Lambder
      @copyright 2009 Lambder.
      @end
      =====================================================================
      */
      var HashMap = function() {
      this.initialize();
      }

      HashMap.prototype = {
      hashkey_prefix: "<#HashMapHashkeyPerfix>",
      hashcode_field: "<#HashMapHashkeyPerfix>",

      initialize: function() {
      this.backing_hash = {};
      this.code = 0;
      },
      /*
      maps value to key returning previous assocciation
      */
      put: function(key, value) {
      var prev;
      if (key && value) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
      prev = this.backing_hash[hashCode];
      } else {
      this.code += 1;
      hashCode = this.hashkey_prefix + this.code;
      key[this.hashcode_field] = hashCode;
      }
      this.backing_hash[hashCode] = value;
      }
      return prev;
      },
      /*
      returns value associated with given key
      */
      get: function(key) {
      var value;
      if (key) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
      value = this.backing_hash[hashCode];
      }
      }
      return value;
      },
      /*
      deletes association by given key.
      Returns true if the assocciation existed, false otherwise
      */
      del: function(key) {
      var success = false;
      if (key) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
      var prev = this.backing_hash[hashCode];
      this.backing_hash[hashCode] = undefined;
      if(prev !== undefined)
      success = true;
      }
      }
      return success;
      }
      }

      //// Usage

      // creation

      var my_map = new HashMap();

      // insertion

      var a_key = {};
      var a_value = {struct: "structA"};
      var b_key = {};
      var b_value = {struct: "structB"};
      var c_key = {};
      var c_value = {struct: "structC"};

      my_map.put(a_key, a_value);
      my_map.put(b_key, b_value);
      var prev_b = my_map.put(b_key, c_value);

      // retrieval

      if(my_map.get(a_key) !== a_value){
      throw("fail1")
      }
      if(my_map.get(b_key) !== c_value){
      throw("fail2")
      }
      if(prev_b !== b_value){
      throw("fail3")
      }

      // deletion

      var a_existed = my_map.del(a_key);
      var c_existed = my_map.del(c_key);
      var a2_existed = my_map.del(a_key);

      if(a_existed !== true){
      throw("fail4")
      }
      if(c_existed !== false){
      throw("fail5")
      }
      if(a2_existed !== false){
      throw("fail6")
      }





      share|improve this answer



















      • 1




        You code does not seem to work with putting the same object in multiple HashMaps.
        – Erik Allik
        Nov 27 '11 at 19:13





















      4














      In ECMA6 you can use WeakMap



      Example:



      var wm1 = new WeakMap(),
      wm2 = new WeakMap(),
      wm3 = new WeakMap();
      var o1 = {},
      o2 = function(){},
      o3 = window;

      wm1.set(o1, 37);
      wm1.set(o2, "azerty");
      wm2.set(o1, o2); // a value can be anything, including an object or a function
      wm2.set(o3, undefined);
      wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!

      wm1.get(o2); // "azerty"
      wm2.get(o2); // undefined, because there is no value for o2 on wm2
      wm2.get(o3); // undefined, because that is the set value

      wm1.has(o2); // true
      wm2.has(o2); // false
      wm2.has(o3); // true (even if the value itself is 'undefined')

      wm3.set(o1, 37);
      wm3.get(o1); // 37
      wm3.clear();
      wm3.get(o1); // undefined, because wm3 was cleared and there is no value for o1 anymore

      wm1.has(o1); // true
      wm1.delete(o1);
      wm1.has(o1); // false


      But:



      Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). 





      share|improve this answer





















      • oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
        – Claudiu
        Nov 12 '13 at 16:56





















      2














      Javascript does not build-in Map/hashmap. It should be called associative array.



      hash["X"] is equals to hash.X, but allow "X" as a string variable.
      In other words, hash[x] is functionally equals to eval("hash."+x.toString())



      It is more similar as object.properties rather then key-value mapping.
      If you are looking for a better Key/value mapping in Javascript, please use Map object which you can find on the web.






      share|improve this answer





























        2














        Try my JavaScript hash table implementation: http://www.timdown.co.uk/jshashtable



        It looks for a hashCode() method of key objects, or you can supply a hashing function when creating a Hashtable object.






        share|improve this answer





























          2














          This looks like a pretty robust solution: https://github.com/flesler/hashmap . It will even work well for functions and objects that look identical. The only hack it uses is adding an obscure member to an object to identify it. If your program doesn't overwrite that obscure variable (its something like hashid), you're golden.






          share|improve this answer





























            2














            If performance is not critical (e.g. the amount of keys is relatively small) and you don't want to pollute your (or maybe not your) objects with additional fields like _hash, _id, etc., then you can make use of the fact that Array.prototype.indexOf employs strict equality. Here is a simple implementation:



            var Dict = (function(){
            // IE 8 and earlier has no Array.prototype.indexOf
            function indexOfPolyfill(val) {
            for (var i = 0, l = this.length; i < l; ++i) {
            if (this[i] === val) {
            return i;
            }
            }
            return -1;
            }

            function Dict(){
            this.keys = ;
            this.values = ;
            if (!this.keys.indexOf) {
            this.keys.indexOf = indexOfPolyfill;
            }
            };

            Dict.prototype.has = function(key){
            return this.keys.indexOf(key) != -1;
            };

            Dict.prototype.get = function(key, defaultValue){
            var index = this.keys.indexOf(key);
            return index == -1 ? defaultValue : this.values[index];
            };

            Dict.prototype.set = function(key, value){
            var index = this.keys.indexOf(key);
            if (index == -1) {
            this.keys.push(key);
            this.values.push(value);
            } else {
            var prevValue = this.values[index];
            this.values[index] = value;
            return prevValue;
            }
            };

            Dict.prototype.delete = function(key){
            var index = this.keys.indexOf(key);
            if (index != -1) {
            this.keys.splice(index, 1);
            return this.values.splice(index, 1)[0];
            }
            };

            Dict.prototype.clear = function(){
            this.keys.splice(0, this.keys.length);
            this.values.splice(0, this.values.length);
            };

            return Dict;
            })();


            Example of usage:



            var a = {}, b = {},
            c = { toString: function(){ return '1'; } },
            d = 1, s = '1', u = undefined, n = null,
            dict = new Dict();

            // keys and values can be anything
            dict.set(a, 'a');
            dict.set(b, 'b');
            dict.set(c, 'c');
            dict.set(d, 'd');
            dict.set(s, 's');
            dict.set(u, 'u');
            dict.set(n, 'n');

            dict.get(a); // 'a'
            dict.get(b); // 'b'
            dict.get(s); // 's'
            dict.get(u); // 'u'
            dict.get(n); // 'n'
            // etc.


            Comparing to ES6 WeakMap it has two issues: O(n) search time and non-weakness (i.e. it will cause memory leak if you don't use delete or clear to release keys).






            share|improve this answer































              2














              My Map Implementation, derived from Christoph's example:



              Example Usage:



              var map = new Map();  //creates an "in-memory" map
              var map = new Map("storageId"); //creates a map that is loaded/persisted using html5 storage




              function Map(storageId) {
              this.current = undefined;
              this.size = 0;
              this.storageId = storageId;
              if (this.storageId) {
              this.keys = new Array();
              this.disableLinking();
              }
              }

              Map.noop = function() {
              return this;
              };

              Map.illegal = function() {
              throw new Error("illegal operation for maps without linking");
              };

              // map initialisation from existing object
              // doesn't add inherited properties if not explicitly instructed to:
              // omitting foreignKeys means foreignKeys === undefined, i.e. == false
              // --> inherited properties won't be added
              Map.from = function(obj, foreignKeys) {
              var map = new Map;
              for(var prop in obj) {
              if(foreignKeys || obj.hasOwnProperty(prop))
              map.put(prop, obj[prop]);
              }
              return map;
              };

              Map.prototype.disableLinking = function() {
              this.link = Map.noop;
              this.unlink = Map.noop;
              this.disableLinking = Map.noop;

              this.next = Map.illegal;
              this.key = Map.illegal;
              this.value = Map.illegal;
              // this.removeAll = Map.illegal;


              return this;
              };

              // overwrite in Map instance if necessary
              Map.prototype.hash = function(value) {
              return (typeof value) + ' ' + (value instanceof Object ?
              (value.__hash || (value.__hash = ++arguments.callee.current)) :
              value.toString());
              };

              Map.prototype.hash.current = 0;

              // --- mapping functions

              Map.prototype.get = function(key) {
              var item = this[this.hash(key)];
              if (item === undefined) {
              if (this.storageId) {
              try {
              var itemStr = localStorage.getItem(this.storageId + key);
              if (itemStr && itemStr !== 'undefined') {
              item = JSON.parse(itemStr);
              this[this.hash(key)] = item;
              this.keys.push(key);
              ++this.size;
              }
              } catch (e) {
              console.log(e);
              }
              }
              }
              return item === undefined ? undefined : item.value;
              };

              Map.prototype.put = function(key, value) {
              var hash = this.hash(key);

              if(this[hash] === undefined) {
              var item = { key : key, value : value };
              this[hash] = item;

              this.link(item);
              ++this.size;
              }
              else this[hash].value = value;
              if (this.storageId) {
              this.keys.push(key);
              try {
              localStorage.setItem(this.storageId + key, JSON.stringify(this[hash]));
              } catch (e) {
              console.log(e);
              }
              }
              return this;
              };

              Map.prototype.remove = function(key) {
              var hash = this.hash(key);
              var item = this[hash];
              if(item !== undefined) {
              --this.size;
              this.unlink(item);

              delete this[hash];
              }
              if (this.storageId) {
              try {
              localStorage.setItem(this.storageId + key, undefined);
              } catch (e) {
              console.log(e);
              }
              }
              return this;
              };

              // only works if linked
              Map.prototype.removeAll = function() {
              if (this.storageId) {
              for (var i=0; i<this.keys.length; i++) {
              this.remove(this.keys[i]);
              }
              this.keys.length = 0;
              } else {
              while(this.size)
              this.remove(this.key());
              }
              return this;
              };

              // --- linked list helper functions

              Map.prototype.link = function(item) {
              if (this.storageId) {
              return;
              }
              if(this.size == 0) {
              item.prev = item;
              item.next = item;
              this.current = item;
              }
              else {
              item.prev = this.current.prev;
              item.prev.next = item;
              item.next = this.current;
              this.current.prev = item;
              }
              };

              Map.prototype.unlink = function(item) {
              if (this.storageId) {
              return;
              }
              if(this.size == 0)
              this.current = undefined;
              else {
              item.prev.next = item.next;
              item.next.prev = item.prev;
              if(item === this.current)
              this.current = item.next;
              }
              };

              // --- iterator functions - only work if map is linked

              Map.prototype.next = function() {
              this.current = this.current.next;
              };

              Map.prototype.key = function() {
              if (this.storageId) {
              return undefined;
              } else {
              return this.current.key;
              }
              };

              Map.prototype.value = function() {
              if (this.storageId) {
              return undefined;
              }
              return this.current.value;
              };





              share|improve this answer































                1














                Adding yet another solution: HashMap is pretty much the first class I ported from Java to Javascript. You could say there is a lot of overhead, but the implementation is almost 100% equal to Java's implementation and includes all interfaces and subclasses.



                The project can be found here: https://github.com/Airblader/jsava
                I'll also attach the (current) source code for the HashMap class, but as stated it also depends on the super class etc. The OOP framework used is qooxdoo.



                Edit: Please note that this code is already out-dated and refer to the github project for the current work. As of writing this, there is also an ArrayList implementation.



                qx.Class.define( 'jsava.util.HashMap', {
                extend: jsava.util.AbstractMap,
                implement: [jsava.util.Map, jsava.io.Serializable, jsava.lang.Cloneable],

                construct: function () {
                var args = Array.prototype.slice.call( arguments ),
                initialCapacity = this.self( arguments ).DEFAULT_INITIAL_CAPACITY,
                loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;

                switch( args.length ) {
                case 1:
                if( qx.Class.implementsInterface( args[0], jsava.util.Map ) ) {
                initialCapacity = Math.max( ((args[0].size() / this.self( arguments ).DEFAULT_LOAD_FACTOR) | 0) + 1,
                this.self( arguments ).DEFAULT_INITIAL_CAPACITY );
                loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;
                } else {
                initialCapacity = args[0];
                }
                break;
                case 2:
                initialCapacity = args[0];
                loadFactor = args[1];
                break;
                }

                if( initialCapacity < 0 ) {
                throw new jsava.lang.IllegalArgumentException( 'Illegal initial capacity: ' + initialCapacity );
                }
                if( initialCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                initialCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                }
                if( loadFactor <= 0 || isNaN( loadFactor ) ) {
                throw new jsava.lang.IllegalArgumentException( 'Illegal load factor: ' + loadFactor );
                }

                var capacity = 1;
                while( capacity < initialCapacity ) {
                capacity <<= 1;
                }

                this._loadFactor = loadFactor;
                this._threshold = (capacity * loadFactor) | 0;
                this._table = jsava.JsavaUtils.emptyArrayOfGivenSize( capacity, null );
                this._init();
                },

                statics: {
                serialVersionUID: 1,

                DEFAULT_INITIAL_CAPACITY: 16,
                MAXIMUM_CAPACITY: 1 << 30,
                DEFAULT_LOAD_FACTOR: 0.75,

                _hash: function (hash) {
                hash ^= (hash >>> 20) ^ (hash >>> 12);
                return hash ^ (hash >>> 7) ^ (hash >>> 4);
                },

                _indexFor: function (hashCode, length) {
                return hashCode & (length - 1);
                },

                Entry: qx.Class.define( 'jsava.util.HashMap.Entry', {
                extend: jsava.lang.Object,
                implement: [jsava.util.Map.Entry],

                construct: function (hash, key, value, nextEntry) {
                this._value = value;
                this._next = nextEntry;
                this._key = key;
                this._hash = hash;
                },

                members: {
                _key: null,
                _value: null,
                /** @type jsava.util.HashMap.Entry */
                _next: null,
                /** @type Number */
                _hash: 0,

                getKey: function () {
                return this._key;
                },

                getValue: function () {
                return this._value;
                },

                setValue: function (newValue) {
                var oldValue = this._value;
                this._value = newValue;
                return oldValue;
                },

                equals: function (obj) {
                if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.HashMap.Entry ) ) {
                return false;
                }

                /** @type jsava.util.HashMap.Entry */
                var entry = obj,
                key1 = this.getKey(),
                key2 = entry.getKey();
                if( key1 === key2 || (key1 !== null && key1.equals( key2 )) ) {
                var value1 = this.getValue(),
                value2 = entry.getValue();
                if( value1 === value2 || (value1 !== null && value1.equals( value2 )) ) {
                return true;
                }
                }

                return false;
                },

                hashCode: function () {
                return (this._key === null ? 0 : this._key.hashCode()) ^
                (this._value === null ? 0 : this._value.hashCode());
                },

                toString: function () {
                return this.getKey() + '=' + this.getValue();
                },

                /**
                * This method is invoked whenever the value in an entry is
                * overwritten by an invocation of put(k,v) for a key k that's already
                * in the HashMap.
                */
                _recordAccess: function (map) {
                },

                /**
                * This method is invoked whenever the entry is
                * removed from the table.
                */
                _recordRemoval: function (map) {
                }
                }
                } )
                },

                members: {
                /** @type jsava.util.HashMap.Entry */
                _table: null,
                /** @type Number */
                _size: 0,
                /** @type Number */
                _threshold: 0,
                /** @type Number */
                _loadFactor: 0,
                /** @type Number */
                _modCount: 0,
                /** @implements jsava.util.Set */
                __entrySet: null,

                /**
                * Initialization hook for subclasses. This method is called
                * in all constructors and pseudo-constructors (clone, readObject)
                * after HashMap has been initialized but before any entries have
                * been inserted. (In the absence of this method, readObject would
                * require explicit knowledge of subclasses.)
                */
                _init: function () {
                },

                size: function () {
                return this._size;
                },

                isEmpty: function () {
                return this._size === 0;
                },

                get: function (key) {
                if( key === null ) {
                return this.__getForNullKey();
                }

                var hash = this.self( arguments )._hash( key.hashCode() );
                for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                entry !== null; entry = entry._next ) {
                /** @type jsava.lang.Object */
                var k;
                if( entry._hash === hash && ((k = entry._key) === key || key.equals( k )) ) {
                return entry._value;
                }
                }

                return null;
                },

                __getForNullKey: function () {
                for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                if( entry._key === null ) {
                return entry._value;
                }
                }

                return null;
                },

                containsKey: function (key) {
                return this._getEntry( key ) !== null;
                },

                _getEntry: function (key) {
                var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() );
                for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                entry !== null; entry = entry._next ) {
                /** @type jsava.lang.Object */
                var k;
                if( entry._hash === hash
                && ( ( k = entry._key ) === key || ( key !== null && key.equals( k ) ) ) ) {
                return entry;
                }
                }

                return null;
                },

                put: function (key, value) {
                if( key === null ) {
                return this.__putForNullKey( value );
                }

                var hash = this.self( arguments )._hash( key.hashCode() ),
                i = this.self( arguments )._indexFor( hash, this._table.length );
                for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                /** @type jsava.lang.Object */
                var k;
                if( entry._hash === hash && ( (k = entry._key) === key || key.equals( k ) ) ) {
                var oldValue = entry._value;
                entry._value = value;
                entry._recordAccess( this );
                return oldValue;
                }
                }

                this._modCount++;
                this._addEntry( hash, key, value, i );
                return null;
                },

                __putForNullKey: function (value) {
                for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                if( entry._key === null ) {
                var oldValue = entry._value;
                entry._value = value;
                entry._recordAccess( this );
                return oldValue;
                }
                }

                this._modCount++;
                this._addEntry( 0, null, value, 0 );
                return null;
                },

                __putForCreate: function (key, value) {
                var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                i = this.self( arguments )._indexFor( hash, this._table.length );
                for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                /** @type jsava.lang.Object */
                var k;
                if( entry._hash === hash
                && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                entry._value = value;
                return;
                }
                }

                this._createEntry( hash, key, value, i );
                },

                __putAllForCreate: function (map) {
                var iterator = map.entrySet().iterator();
                while( iterator.hasNext() ) {
                var entry = iterator.next();
                this.__putForCreate( entry.getKey(), entry.getValue() );
                }
                },

                _resize: function (newCapacity) {
                var oldTable = this._table,
                oldCapacity = oldTable.length;
                if( oldCapacity === this.self( arguments ).MAXIMUM_CAPACITY ) {
                this._threshold = Number.MAX_VALUE;
                return;
                }

                var newTable = jsava.JsavaUtils.emptyArrayOfGivenSize( newCapacity, null );
                this._transfer( newTable );
                this._table = newTable;
                this._threshold = (newCapacity * this._loadFactor) | 0;
                },

                _transfer: function (newTable) {
                var src = this._table,
                newCapacity = newTable.length;
                for( var j = 0; j < src.length; j++ ) {
                var entry = src[j];
                if( entry !== null ) {
                src[j] = null;
                do {
                var next = entry._next,
                i = this.self( arguments )._indexFor( entry._hash, newCapacity );
                entry._next = newTable[i];
                newTable[i] = entry;
                entry = next;
                } while( entry !== null );
                }
                }
                },

                putAll: function (map) {
                var numKeyToBeAdded = map.size();
                if( numKeyToBeAdded === 0 ) {
                return;
                }

                if( numKeyToBeAdded > this._threshold ) {
                var targetCapacity = (numKeyToBeAdded / this._loadFactor + 1) | 0;
                if( targetCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                targetCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                }

                var newCapacity = this._table.length;
                while( newCapacity < targetCapacity ) {
                newCapacity <<= 1;
                }
                if( newCapacity > this._table.length ) {
                this._resize( newCapacity );
                }
                }

                var iterator = map.entrySet().iterator();
                while( iterator.hasNext() ) {
                var entry = iterator.next();
                this.put( entry.getKey(), entry.getValue() );
                }
                },

                remove: function (key) {
                var entry = this._removeEntryForKey( key );
                return entry === null ? null : entry._value;
                },

                _removeEntryForKey: function (key) {
                var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                i = this.self( arguments )._indexFor( hash, this._table.length ),
                prev = this._table[i],
                entry = prev;

                while( entry !== null ) {
                var next = entry._next,
                /** @type jsava.lang.Object */
                k;
                if( entry._hash === hash
                && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                this._modCount++;
                this._size--;
                if( prev === entry ) {
                this._table[i] = next;
                } else {
                prev._next = next;
                }
                entry._recordRemoval( this );
                return entry;
                }
                prev = entry;
                entry = next;
                }

                return entry;
                },

                _removeMapping: function (obj) {
                if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                return null;
                }

                /** @implements jsava.util.Map.Entry */
                var entry = obj,
                key = entry.getKey(),
                hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                i = this.self( arguments )._indexFor( hash, this._table.length ),
                prev = this._table[i],
                e = prev;

                while( e !== null ) {
                var next = e._next;
                if( e._hash === hash && e.equals( entry ) ) {
                this._modCount++;
                this._size--;
                if( prev === e ) {
                this._table[i] = next;
                } else {
                prev._next = next;
                }
                e._recordRemoval( this );
                return e;
                }
                prev = e;
                e = next;
                }

                return e;
                },

                clear: function () {
                this._modCount++;
                var table = this._table;
                for( var i = 0; i < table.length; i++ ) {
                table[i] = null;
                }
                this._size = 0;
                },

                containsValue: function (value) {
                if( value === null ) {
                return this.__containsNullValue();
                }

                var table = this._table;
                for( var i = 0; i < table.length; i++ ) {
                for( var entry = table[i]; entry !== null; entry = entry._next ) {
                if( value.equals( entry._value ) ) {
                return true;
                }
                }
                }

                return false;
                },

                __containsNullValue: function () {
                var table = this._table;
                for( var i = 0; i < table.length; i++ ) {
                for( var entry = table[i]; entry !== null; entry = entry._next ) {
                if( entry._value === null ) {
                return true;
                }
                }
                }

                return false;
                },

                clone: function () {
                /** @type jsava.util.HashMap */
                var result = null;
                try {
                result = this.base( arguments );
                } catch( e ) {
                if( !qx.Class.isSubClassOf( e.constructor, jsava.lang.CloneNotSupportedException ) ) {
                throw e;
                }
                }

                result._table = jsava.JsavaUtils.emptyArrayOfGivenSize( this._table.length, null );
                result.__entrySet = null;
                result._modCount = 0;
                result._size = 0;
                result._init();
                result.__putAllForCreate( this );

                return result;
                },

                _addEntry: function (hash, key, value, bucketIndex) {
                var entry = this._table[bucketIndex];
                this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                if( this._size++ >= this._threshold ) {
                this._resize( 2 * this._table.length );
                }
                },

                _createEntry: function (hash, key, value, bucketIndex) {
                var entry = this._table[bucketIndex];
                this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                this._size++;
                },

                keySet: function () {
                var keySet = this._keySet;
                return keySet !== null ? keySet : ( this._keySet = new this.KeySet( this ) );
                },

                values: function () {
                var values = this._values;
                return values !== null ? values : ( this._values = new this.Values( this ) );
                },

                entrySet: function () {
                return this.__entrySet0();
                },

                __entrySet0: function () {
                var entrySet = this.__entrySet;
                return entrySet !== null ? entrySet : ( this.__entrySet = new this.EntrySet( this ) );
                },

                /** @private */
                HashIterator: qx.Class.define( 'jsava.util.HashMap.HashIterator', {
                extend: jsava.lang.Object,
                implement: [jsava.util.Iterator],

                type: 'abstract',

                /** @protected */
                construct: function (thisHashMap) {
                this.__thisHashMap = thisHashMap;
                this._expectedModCount = this.__thisHashMap._modCount;
                if( this.__thisHashMap._size > 0 ) {
                var table = this.__thisHashMap._table;
                while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                // do nothing
                }
                }
                },

                members: {
                __thisHashMap: null,

                /** @type jsava.util.HashMap.Entry */
                _next: null,
                /** @type Number */
                _expectedModCount: 0,
                /** @type Number */
                _index: 0,
                /** @type jsava.util.HashMap.Entry */
                _current: null,

                hasNext: function () {
                return this._next !== null;
                },

                _nextEntry: function () {
                if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                throw new jsava.lang.ConcurrentModificationException();
                }

                var entry = this._next;
                if( entry === null ) {
                throw new jsava.lang.NoSuchElementException();
                }

                if( (this._next = entry._next) === null ) {
                var table = this.__thisHashMap._table;
                while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                // do nothing
                }
                }

                this._current = entry;
                return entry;
                },

                remove: function () {
                if( this._current === null ) {
                throw new jsava.lang.IllegalStateException();
                }

                if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                throw new jsava.lang.ConcurrentModificationException();
                }

                var key = this._current._key;
                this._current = null;
                this.__thisHashMap._removeEntryForKey( key );
                this._expectedModCount = this.__thisHashMap._modCount;
                }
                }
                } ),

                _newKeyIterator: function () {
                return new this.KeyIterator( this );
                },

                _newValueIterator: function () {
                return new this.ValueIterator( this );
                },

                _newEntryIterator: function () {
                return new this.EntryIterator( this );
                },

                /** @private */
                ValueIterator: qx.Class.define( 'jsava.util.HashMap.ValueIterator', {
                extend: jsava.util.HashMap.HashIterator,

                construct: function (thisHashMap) {
                this.base( arguments, thisHashMap );
                },

                members: {
                next: function () {
                return this._nextEntry()._value;
                }
                }
                } ),

                /** @private */
                KeyIterator: qx.Class.define( 'jsava.util.HashMap.KeyIterator', {
                extend: jsava.util.HashMap.HashIterator,

                construct: function (thisHashMap) {
                this.base( arguments, thisHashMap );
                },

                members: {
                next: function () {
                return this._nextEntry().getKey();
                }
                }
                } ),

                /** @private */
                EntryIterator: qx.Class.define( 'jsava.util.HashMap.EntryIterator', {
                extend: jsava.util.HashMap.HashIterator,

                construct: function (thisHashMap) {
                this.base( arguments, thisHashMap );
                },

                members: {
                next: function () {
                return this._nextEntry();
                }
                }
                } ),

                /** @private */
                KeySet: qx.Class.define( 'jsava.util.HashMap.KeySet', {
                extend: jsava.util.AbstractSet,

                construct: function (thisHashMap) {
                this.base( arguments );
                this.__thisHashMap = thisHashMap;
                },

                members: {
                __thisHashMap: null,

                iterator: function () {
                return this.__thisHashMap._newKeyIterator();
                },

                size: function () {
                return this.__thisHashMap._size;
                },

                contains: function (obj) {
                return this.__thisHashMap.containsKey( obj );
                },

                remove: function (obj) {
                return this.__thisHashMap._removeEntryForKey( obj ) !== null;
                },

                clear: function () {
                this.__thisHashMap.clear();
                }
                }
                } ),

                /** @private */
                Values: qx.Class.define( 'jsava.util.HashMap.Values', {
                extend: jsava.util.AbstractCollection,

                construct: function (thisHashMap) {
                this.base( arguments );
                this.__thisHashMap = thisHashMap;
                },

                members: {
                __thisHashMap: null,

                iterator: function () {
                return this.__thisHashMap._newValueIterator();
                },

                size: function () {
                return this.__thisHashMap._size;
                },

                contains: function (obj) {
                return this.__thisHashMap.containsValue( obj );
                },

                clear: function () {
                this.__thisHashMap.clear();
                }
                }
                } ),

                /** @private */
                EntrySet: qx.Class.define( 'jsava.util.HashMap.EntrySet', {
                extend: jsava.util.AbstractSet,

                construct: function (thisHashMap) {
                this.base( arguments );
                this.__thisHashMap = thisHashMap;
                },

                members: {
                __thisHashMap: null,

                iterator: function () {
                return this.__thisHashMap._newEntryIterator();
                },

                size: function () {
                return this.__thisHashMap._size;
                },

                contains: function (obj) {
                if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                return false;
                }

                /** @implements jsava.util.Map.Entry */
                var entry = obj,
                candidate = this.__thisHashMap._getEntry( entry.getKey() );
                return candidate !== null && candidate.equals( entry );
                },

                remove: function (obj) {
                return this.__thisHashMap._removeMapping( obj ) !== null;
                },

                clear: function () {
                this.__thisHashMap.clear();
                }
                }
                } )
                }
                } );





                share|improve this answer























                • Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                  – Claudiu
                  Aug 19 '13 at 19:24










                • Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                  – Ingo Bürk
                  Aug 19 '13 at 20:00










                • Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                  – Claudiu
                  Aug 19 '13 at 20:33










                • Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                  – Ingo Bürk
                  Aug 19 '13 at 21:09










                • Having fun playing around is the best way to learn =). thanks for sharing
                  – Claudiu
                  Aug 19 '13 at 21:39



















                0














                Yet another map implementation by me. With randomizer, 'generics' and 'iterator' =)



                var HashMap = function (TKey, TValue) {
                var db = ;
                var keyType, valueType;

                (function () {
                keyType = TKey;
                valueType = TValue;
                })();

                var getIndexOfKey = function (key) {
                if (typeof key !== keyType)
                throw new Error('Type of key should be ' + keyType);
                for (var i = 0; i < db.length; i++) {
                if (db[i][0] == key)
                return i;
                }
                return -1;
                }

                this.add = function (key, value) {
                if (typeof key !== keyType) {
                throw new Error('Type of key should be ' + keyType);
                } else if (typeof value !== valueType) {
                throw new Error('Type of value should be ' + valueType);
                }
                var index = getIndexOfKey(key);
                if (index === -1)
                db.push([key, value]);
                else
                db[index][1] = value;
                return this;
                }

                this.get = function (key) {
                if (typeof key !== keyType || db.length === 0)
                return null;
                for (var i = 0; i < db.length; i++) {
                if (db[i][0] == key)
                return db[i][1];
                }
                return null;
                }

                this.size = function () {
                return db.length;
                }

                this.keys = function () {
                if (db.length === 0)
                return ;
                var result = ;
                for (var i = 0; i < db.length; i++) {
                result.push(db[i][0]);
                }
                return result;
                }

                this.values = function () {
                if (db.length === 0)
                return ;
                var result = ;
                for (var i = 0; i < db.length; i++) {
                result.push(db[i][1]);
                }
                return result;
                }

                this.randomize = function () {
                if (db.length === 0)
                return this;
                var currentIndex = db.length, temporaryValue, randomIndex;
                while (0 !== currentIndex) {
                randomIndex = Math.floor(Math.random() * currentIndex);
                currentIndex--;
                temporaryValue = db[currentIndex];
                db[currentIndex] = db[randomIndex];
                db[randomIndex] = temporaryValue;
                }
                return this;
                }

                this.iterate = function (callback) {
                if (db.length === 0)
                return false;
                for (var i = 0; i < db.length; i++) {
                callback(db[i][0], db[i][1]);
                }
                return true;
                }
                }


                Example:



                var a = new HashMap("string", "number");
                a.add('test', 1132)
                .add('test14', 666)
                .add('1421test14', 12312666)
                .iterate(function (key, value) {console.log('a['+key+']='+value)});
                /*
                a[test]=1132
                a[test14]=666
                a[1421test14]=12312666
                */
                a.randomize();
                /*
                a[1421test14]=12312666
                a[test]=1132
                a[test14]=666
                */





                share|improve this answer





















                  Your Answer






                  StackExchange.ifUsing("editor", function () {
                  StackExchange.using("externalEditor", function () {
                  StackExchange.using("snippets", function () {
                  StackExchange.snippets.init();
                  });
                  });
                  }, "code-snippets");

                  StackExchange.ready(function() {
                  var channelOptions = {
                  tags: "".split(" "),
                  id: "1"
                  };
                  initTagRenderer("".split(" "), "".split(" "), channelOptions);

                  StackExchange.using("externalEditor", function() {
                  // Have to fire editor after snippets, if snippets enabled
                  if (StackExchange.settings.snippets.snippetsEnabled) {
                  StackExchange.using("snippets", function() {
                  createEditor();
                  });
                  }
                  else {
                  createEditor();
                  }
                  });

                  function createEditor() {
                  StackExchange.prepareEditor({
                  heartbeatType: 'answer',
                  autoActivateHeartbeat: false,
                  convertImagesToLinks: true,
                  noModals: true,
                  showLowRepImageUploadWarning: true,
                  reputationToPostImages: 10,
                  bindNavPrevention: true,
                  postfix: "",
                  imageUploader: {
                  brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
                  contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
                  allowUrls: true
                  },
                  onDemand: true,
                  discardSelector: ".discard-answer"
                  ,immediatelyShowMarkdownHelp:true
                  });


                  }
                  });














                  draft saved

                  draft discarded


















                  StackExchange.ready(
                  function () {
                  StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f368280%2fjavascript-hashmap-equivalent%23new-answer', 'question_page');
                  }
                  );

                  Post as a guest















                  Required, but never shown

























                  17 Answers
                  17






                  active

                  oldest

                  votes








                  17 Answers
                  17






                  active

                  oldest

                  votes









                  active

                  oldest

                  votes






                  active

                  oldest

                  votes









                  286














                  Why not hash your objects yourself manually, and use the resulting strings as keys for a regular JavaScript dictionary? After all you are in the best position to know what makes your objects unique. That's what I do.



                  Example:



                  var key = function(obj){
                  // some unique object-dependent key
                  return obj.totallyUniqueEmployeeIdKey; // just an example
                  };

                  var dict = {};

                  dict[key(obj1)] = obj1;
                  dict[key(obj2)] = obj2;


                  This way you can control indexing done by JavaScript without heavy lifting of memory allocation, and overflow handling.



                  Of course, if you truly want the "industrial-grade solution", you can build a class parameterized by the key function, and with all necessary API of the container, but … we use JavaScript, and trying to be simple and lightweight, so this functional solution is simple and fast.



                  The key function can be as simple as selecting right attributes of the object, e.g., a key, or a set of keys, which are already unique, a combination of keys, which are unique together, or as complex as using some cryptographic hashes like in DojoX Encoding, or DojoX UUID. While the latter solutions may produce unique keys, personally I try to avoid them at all costs, especially, if I know what makes my objects unique.



                  Update in 2014: Answered back in 2008 this simple solution still requires more explanations. Let me clarify the idea in a Q&A form.



                  Your solution doesn't have a real hash. Where is it???



                  JavaScript is a high-level language. Its basic primitive
                  (Object)
                  includes a hash table to keep properties. This hash table is usually written
                  in a low-level language for efficiency. Using a simple object with string keys we use an efficiently implemented hash table with no efforts on our part.



                  How do you know they use hash?



                  There are three major ways to keep a collection of objects addressable by a key:




                  • Unordered. In this case to retrieve an object by its key we have to go over all keys stopping when we find it. On average it will take n/2 comparisons.

                  • Ordered.


                    • Example #1: a sorted array — doing a binary search we will find our key after ~log2(n) comparisons on average. Much better.

                    • Example #2: a tree. Again it'll be ~log(n) attempts.



                  • Hash table. On average it requires a constant time. Compare: O(n) vs. O(log n) vs. O(1). Boom.


                  Obviously JavaScript objects use hash tables in some form to handle general cases.



                  Do browser vendors really use hash tables???



                  Really.




                  • Chrome/node.js/V8:
                    JSObject. Look for
                    NameDictionary and
                    NameDictionaryShape with
                    pertinent details in objects.cc
                    and objects-inl.h.

                  • Firefox/Gecko:
                    JSObject,
                    NativeObject, and
                    PlainObject with pertinent details in
                    jsobj.cpp and
                    vm/NativeObject.cpp.


                  Do they handle collisions?



                  Yes. See above. If you found a collision on unequal strings, please do not hesitate to file a bug with a vendor.



                  So what is your idea?



                  If you want to hash an object, find what makes it unique and use it as a key. Do not try to calculate real hash or emulate hash tables — it is already efficiently handled by the underlying JavaScript object.



                  Use this key with JavaScript Object to leverage its built-in hash table while steering clear of possible clashes with default properties.



                  Examples to get you started:




                  • If your objects include a unique user name — use it as a key.

                  • If it includes a unique customer number — use it as a key.


                    • If it includes unique government-issued numbers like an SSN, or a passport number, and your system doesn't allow duplicates — use it as a key.



                  • If a combination of fields is unique — use it as a key.


                    • State abbreviation + driver license number makes an excellent key.

                    • Country abbreviation + passport number is an excellent key too.



                  • Some function on fields, or a whole object, can return a unique value — use it as a key.


                  I used your suggestion and cached all objects using a user name. But some wise guy is named "toString", which is a built-in property! What should I do now?



                  Obviously, if it is even remotely possible that the resulting key will exclusively consists of Latin characters, you should do something about it. For example, add any non-Latin Unicode character you like at the beginning or at the end to un-clash with default properties: "#toString", "#MarySmith". If a composite key is used, separate key components using some kind of non-Latin delimiter: "name,city,state".



                  In general this is the place, where we have to be creative, and select the easiest keys with given limitations (uniqueness, potential clashes with default properties).



                  Note: unique keys do not clash by definition, while potential hash clashes will be handled by the underlying Object.



                  Why don't you like industrial solutions?



                  IMHO, the best code is no code at all: it has no errors, requires no maintenance, easy to understand, and executes instantaneously. All "hash tables in JavaScript" I saw were >100 lines of code, and involved multiple objects. Compare it with: dict[key] = value.



                  Another point: is it even possible to beat a performance of a primordial object written in a low-level language, using JavaScript and the very same primordial objects to implement what is already implemented?



                  I still want to hash my objects without any keys!



                  We are in luck: ECMAScript 6 (scheduled for mid 2015 release, give or take 1-2 years after that to become widespread) defines
                  map and
                  set.



                  Judging by the definition they can use object's address as a key, which makes objects instantly distinct without artificial keys. OTOH, two different, yet identical objects, will be mapped as distinct.



                  Comparison breakdown from MDN:




                  Objects are similar to Maps in that both let you set keys to values,
                  retrieve those values, delete keys, and detect whether something is
                  stored at a key. Because of this (and because there were no built-in
                  alternatives), Objects have been used as Maps historically; however,
                  there are important differences that make using a Map preferable in
                  certain cases:




                  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.

                  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of
                    insertion.

                  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.

                  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion
                    and iterating over them.

                  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can
                    be bypassed by using map = Object.create(null), but this is seldom
                    done.

                  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.







                  share|improve this answer



















                  • 11




                    This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
                    – beefeather
                    Dec 2 '12 at 4:14






                  • 28




                    Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
                    – Matt R
                    Feb 11 '13 at 11:05






                  • 26




                    @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
                    – Eugene Lazutkin
                    Feb 13 '13 at 19:23






                  • 4




                    @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
                    – Matt R
                    Feb 22 '13 at 7:53






                  • 5




                    @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
                    – Matt R
                    Feb 24 '13 at 8:39
















                  286














                  Why not hash your objects yourself manually, and use the resulting strings as keys for a regular JavaScript dictionary? After all you are in the best position to know what makes your objects unique. That's what I do.



                  Example:



                  var key = function(obj){
                  // some unique object-dependent key
                  return obj.totallyUniqueEmployeeIdKey; // just an example
                  };

                  var dict = {};

                  dict[key(obj1)] = obj1;
                  dict[key(obj2)] = obj2;


                  This way you can control indexing done by JavaScript without heavy lifting of memory allocation, and overflow handling.



                  Of course, if you truly want the "industrial-grade solution", you can build a class parameterized by the key function, and with all necessary API of the container, but … we use JavaScript, and trying to be simple and lightweight, so this functional solution is simple and fast.



                  The key function can be as simple as selecting right attributes of the object, e.g., a key, or a set of keys, which are already unique, a combination of keys, which are unique together, or as complex as using some cryptographic hashes like in DojoX Encoding, or DojoX UUID. While the latter solutions may produce unique keys, personally I try to avoid them at all costs, especially, if I know what makes my objects unique.



                  Update in 2014: Answered back in 2008 this simple solution still requires more explanations. Let me clarify the idea in a Q&A form.



                  Your solution doesn't have a real hash. Where is it???



                  JavaScript is a high-level language. Its basic primitive
                  (Object)
                  includes a hash table to keep properties. This hash table is usually written
                  in a low-level language for efficiency. Using a simple object with string keys we use an efficiently implemented hash table with no efforts on our part.



                  How do you know they use hash?



                  There are three major ways to keep a collection of objects addressable by a key:




                  • Unordered. In this case to retrieve an object by its key we have to go over all keys stopping when we find it. On average it will take n/2 comparisons.

                  • Ordered.


                    • Example #1: a sorted array — doing a binary search we will find our key after ~log2(n) comparisons on average. Much better.

                    • Example #2: a tree. Again it'll be ~log(n) attempts.



                  • Hash table. On average it requires a constant time. Compare: O(n) vs. O(log n) vs. O(1). Boom.


                  Obviously JavaScript objects use hash tables in some form to handle general cases.



                  Do browser vendors really use hash tables???



                  Really.




                  • Chrome/node.js/V8:
                    JSObject. Look for
                    NameDictionary and
                    NameDictionaryShape with
                    pertinent details in objects.cc
                    and objects-inl.h.

                  • Firefox/Gecko:
                    JSObject,
                    NativeObject, and
                    PlainObject with pertinent details in
                    jsobj.cpp and
                    vm/NativeObject.cpp.


                  Do they handle collisions?



                  Yes. See above. If you found a collision on unequal strings, please do not hesitate to file a bug with a vendor.



                  So what is your idea?



                  If you want to hash an object, find what makes it unique and use it as a key. Do not try to calculate real hash or emulate hash tables — it is already efficiently handled by the underlying JavaScript object.



                  Use this key with JavaScript Object to leverage its built-in hash table while steering clear of possible clashes with default properties.



                  Examples to get you started:




                  • If your objects include a unique user name — use it as a key.

                  • If it includes a unique customer number — use it as a key.


                    • If it includes unique government-issued numbers like an SSN, or a passport number, and your system doesn't allow duplicates — use it as a key.



                  • If a combination of fields is unique — use it as a key.


                    • State abbreviation + driver license number makes an excellent key.

                    • Country abbreviation + passport number is an excellent key too.



                  • Some function on fields, or a whole object, can return a unique value — use it as a key.


                  I used your suggestion and cached all objects using a user name. But some wise guy is named "toString", which is a built-in property! What should I do now?



                  Obviously, if it is even remotely possible that the resulting key will exclusively consists of Latin characters, you should do something about it. For example, add any non-Latin Unicode character you like at the beginning or at the end to un-clash with default properties: "#toString", "#MarySmith". If a composite key is used, separate key components using some kind of non-Latin delimiter: "name,city,state".



                  In general this is the place, where we have to be creative, and select the easiest keys with given limitations (uniqueness, potential clashes with default properties).



                  Note: unique keys do not clash by definition, while potential hash clashes will be handled by the underlying Object.



                  Why don't you like industrial solutions?



                  IMHO, the best code is no code at all: it has no errors, requires no maintenance, easy to understand, and executes instantaneously. All "hash tables in JavaScript" I saw were >100 lines of code, and involved multiple objects. Compare it with: dict[key] = value.



                  Another point: is it even possible to beat a performance of a primordial object written in a low-level language, using JavaScript and the very same primordial objects to implement what is already implemented?



                  I still want to hash my objects without any keys!



                  We are in luck: ECMAScript 6 (scheduled for mid 2015 release, give or take 1-2 years after that to become widespread) defines
                  map and
                  set.



                  Judging by the definition they can use object's address as a key, which makes objects instantly distinct without artificial keys. OTOH, two different, yet identical objects, will be mapped as distinct.



                  Comparison breakdown from MDN:




                  Objects are similar to Maps in that both let you set keys to values,
                  retrieve those values, delete keys, and detect whether something is
                  stored at a key. Because of this (and because there were no built-in
                  alternatives), Objects have been used as Maps historically; however,
                  there are important differences that make using a Map preferable in
                  certain cases:




                  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.

                  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of
                    insertion.

                  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.

                  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion
                    and iterating over them.

                  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can
                    be bypassed by using map = Object.create(null), but this is seldom
                    done.

                  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.







                  share|improve this answer



















                  • 11




                    This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
                    – beefeather
                    Dec 2 '12 at 4:14






                  • 28




                    Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
                    – Matt R
                    Feb 11 '13 at 11:05






                  • 26




                    @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
                    – Eugene Lazutkin
                    Feb 13 '13 at 19:23






                  • 4




                    @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
                    – Matt R
                    Feb 22 '13 at 7:53






                  • 5




                    @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
                    – Matt R
                    Feb 24 '13 at 8:39














                  286












                  286








                  286






                  Why not hash your objects yourself manually, and use the resulting strings as keys for a regular JavaScript dictionary? After all you are in the best position to know what makes your objects unique. That's what I do.



                  Example:



                  var key = function(obj){
                  // some unique object-dependent key
                  return obj.totallyUniqueEmployeeIdKey; // just an example
                  };

                  var dict = {};

                  dict[key(obj1)] = obj1;
                  dict[key(obj2)] = obj2;


                  This way you can control indexing done by JavaScript without heavy lifting of memory allocation, and overflow handling.



                  Of course, if you truly want the "industrial-grade solution", you can build a class parameterized by the key function, and with all necessary API of the container, but … we use JavaScript, and trying to be simple and lightweight, so this functional solution is simple and fast.



                  The key function can be as simple as selecting right attributes of the object, e.g., a key, or a set of keys, which are already unique, a combination of keys, which are unique together, or as complex as using some cryptographic hashes like in DojoX Encoding, or DojoX UUID. While the latter solutions may produce unique keys, personally I try to avoid them at all costs, especially, if I know what makes my objects unique.



                  Update in 2014: Answered back in 2008 this simple solution still requires more explanations. Let me clarify the idea in a Q&A form.



                  Your solution doesn't have a real hash. Where is it???



                  JavaScript is a high-level language. Its basic primitive
                  (Object)
                  includes a hash table to keep properties. This hash table is usually written
                  in a low-level language for efficiency. Using a simple object with string keys we use an efficiently implemented hash table with no efforts on our part.



                  How do you know they use hash?



                  There are three major ways to keep a collection of objects addressable by a key:




                  • Unordered. In this case to retrieve an object by its key we have to go over all keys stopping when we find it. On average it will take n/2 comparisons.

                  • Ordered.


                    • Example #1: a sorted array — doing a binary search we will find our key after ~log2(n) comparisons on average. Much better.

                    • Example #2: a tree. Again it'll be ~log(n) attempts.



                  • Hash table. On average it requires a constant time. Compare: O(n) vs. O(log n) vs. O(1). Boom.


                  Obviously JavaScript objects use hash tables in some form to handle general cases.



                  Do browser vendors really use hash tables???



                  Really.




                  • Chrome/node.js/V8:
                    JSObject. Look for
                    NameDictionary and
                    NameDictionaryShape with
                    pertinent details in objects.cc
                    and objects-inl.h.

                  • Firefox/Gecko:
                    JSObject,
                    NativeObject, and
                    PlainObject with pertinent details in
                    jsobj.cpp and
                    vm/NativeObject.cpp.


                  Do they handle collisions?



                  Yes. See above. If you found a collision on unequal strings, please do not hesitate to file a bug with a vendor.



                  So what is your idea?



                  If you want to hash an object, find what makes it unique and use it as a key. Do not try to calculate real hash or emulate hash tables — it is already efficiently handled by the underlying JavaScript object.



                  Use this key with JavaScript Object to leverage its built-in hash table while steering clear of possible clashes with default properties.



                  Examples to get you started:




                  • If your objects include a unique user name — use it as a key.

                  • If it includes a unique customer number — use it as a key.


                    • If it includes unique government-issued numbers like an SSN, or a passport number, and your system doesn't allow duplicates — use it as a key.



                  • If a combination of fields is unique — use it as a key.


                    • State abbreviation + driver license number makes an excellent key.

                    • Country abbreviation + passport number is an excellent key too.



                  • Some function on fields, or a whole object, can return a unique value — use it as a key.


                  I used your suggestion and cached all objects using a user name. But some wise guy is named "toString", which is a built-in property! What should I do now?



                  Obviously, if it is even remotely possible that the resulting key will exclusively consists of Latin characters, you should do something about it. For example, add any non-Latin Unicode character you like at the beginning or at the end to un-clash with default properties: "#toString", "#MarySmith". If a composite key is used, separate key components using some kind of non-Latin delimiter: "name,city,state".



                  In general this is the place, where we have to be creative, and select the easiest keys with given limitations (uniqueness, potential clashes with default properties).



                  Note: unique keys do not clash by definition, while potential hash clashes will be handled by the underlying Object.



                  Why don't you like industrial solutions?



                  IMHO, the best code is no code at all: it has no errors, requires no maintenance, easy to understand, and executes instantaneously. All "hash tables in JavaScript" I saw were >100 lines of code, and involved multiple objects. Compare it with: dict[key] = value.



                  Another point: is it even possible to beat a performance of a primordial object written in a low-level language, using JavaScript and the very same primordial objects to implement what is already implemented?



                  I still want to hash my objects without any keys!



                  We are in luck: ECMAScript 6 (scheduled for mid 2015 release, give or take 1-2 years after that to become widespread) defines
                  map and
                  set.



                  Judging by the definition they can use object's address as a key, which makes objects instantly distinct without artificial keys. OTOH, two different, yet identical objects, will be mapped as distinct.



                  Comparison breakdown from MDN:




                  Objects are similar to Maps in that both let you set keys to values,
                  retrieve those values, delete keys, and detect whether something is
                  stored at a key. Because of this (and because there were no built-in
                  alternatives), Objects have been used as Maps historically; however,
                  there are important differences that make using a Map preferable in
                  certain cases:




                  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.

                  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of
                    insertion.

                  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.

                  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion
                    and iterating over them.

                  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can
                    be bypassed by using map = Object.create(null), but this is seldom
                    done.

                  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.







                  share|improve this answer














                  Why not hash your objects yourself manually, and use the resulting strings as keys for a regular JavaScript dictionary? After all you are in the best position to know what makes your objects unique. That's what I do.



                  Example:



                  var key = function(obj){
                  // some unique object-dependent key
                  return obj.totallyUniqueEmployeeIdKey; // just an example
                  };

                  var dict = {};

                  dict[key(obj1)] = obj1;
                  dict[key(obj2)] = obj2;


                  This way you can control indexing done by JavaScript without heavy lifting of memory allocation, and overflow handling.



                  Of course, if you truly want the "industrial-grade solution", you can build a class parameterized by the key function, and with all necessary API of the container, but … we use JavaScript, and trying to be simple and lightweight, so this functional solution is simple and fast.



                  The key function can be as simple as selecting right attributes of the object, e.g., a key, or a set of keys, which are already unique, a combination of keys, which are unique together, or as complex as using some cryptographic hashes like in DojoX Encoding, or DojoX UUID. While the latter solutions may produce unique keys, personally I try to avoid them at all costs, especially, if I know what makes my objects unique.



                  Update in 2014: Answered back in 2008 this simple solution still requires more explanations. Let me clarify the idea in a Q&A form.



                  Your solution doesn't have a real hash. Where is it???



                  JavaScript is a high-level language. Its basic primitive
                  (Object)
                  includes a hash table to keep properties. This hash table is usually written
                  in a low-level language for efficiency. Using a simple object with string keys we use an efficiently implemented hash table with no efforts on our part.



                  How do you know they use hash?



                  There are three major ways to keep a collection of objects addressable by a key:




                  • Unordered. In this case to retrieve an object by its key we have to go over all keys stopping when we find it. On average it will take n/2 comparisons.

                  • Ordered.


                    • Example #1: a sorted array — doing a binary search we will find our key after ~log2(n) comparisons on average. Much better.

                    • Example #2: a tree. Again it'll be ~log(n) attempts.



                  • Hash table. On average it requires a constant time. Compare: O(n) vs. O(log n) vs. O(1). Boom.


                  Obviously JavaScript objects use hash tables in some form to handle general cases.



                  Do browser vendors really use hash tables???



                  Really.




                  • Chrome/node.js/V8:
                    JSObject. Look for
                    NameDictionary and
                    NameDictionaryShape with
                    pertinent details in objects.cc
                    and objects-inl.h.

                  • Firefox/Gecko:
                    JSObject,
                    NativeObject, and
                    PlainObject with pertinent details in
                    jsobj.cpp and
                    vm/NativeObject.cpp.


                  Do they handle collisions?



                  Yes. See above. If you found a collision on unequal strings, please do not hesitate to file a bug with a vendor.



                  So what is your idea?



                  If you want to hash an object, find what makes it unique and use it as a key. Do not try to calculate real hash or emulate hash tables — it is already efficiently handled by the underlying JavaScript object.



                  Use this key with JavaScript Object to leverage its built-in hash table while steering clear of possible clashes with default properties.



                  Examples to get you started:




                  • If your objects include a unique user name — use it as a key.

                  • If it includes a unique customer number — use it as a key.


                    • If it includes unique government-issued numbers like an SSN, or a passport number, and your system doesn't allow duplicates — use it as a key.



                  • If a combination of fields is unique — use it as a key.


                    • State abbreviation + driver license number makes an excellent key.

                    • Country abbreviation + passport number is an excellent key too.



                  • Some function on fields, or a whole object, can return a unique value — use it as a key.


                  I used your suggestion and cached all objects using a user name. But some wise guy is named "toString", which is a built-in property! What should I do now?



                  Obviously, if it is even remotely possible that the resulting key will exclusively consists of Latin characters, you should do something about it. For example, add any non-Latin Unicode character you like at the beginning or at the end to un-clash with default properties: "#toString", "#MarySmith". If a composite key is used, separate key components using some kind of non-Latin delimiter: "name,city,state".



                  In general this is the place, where we have to be creative, and select the easiest keys with given limitations (uniqueness, potential clashes with default properties).



                  Note: unique keys do not clash by definition, while potential hash clashes will be handled by the underlying Object.



                  Why don't you like industrial solutions?



                  IMHO, the best code is no code at all: it has no errors, requires no maintenance, easy to understand, and executes instantaneously. All "hash tables in JavaScript" I saw were >100 lines of code, and involved multiple objects. Compare it with: dict[key] = value.



                  Another point: is it even possible to beat a performance of a primordial object written in a low-level language, using JavaScript and the very same primordial objects to implement what is already implemented?



                  I still want to hash my objects without any keys!



                  We are in luck: ECMAScript 6 (scheduled for mid 2015 release, give or take 1-2 years after that to become widespread) defines
                  map and
                  set.



                  Judging by the definition they can use object's address as a key, which makes objects instantly distinct without artificial keys. OTOH, two different, yet identical objects, will be mapped as distinct.



                  Comparison breakdown from MDN:




                  Objects are similar to Maps in that both let you set keys to values,
                  retrieve those values, delete keys, and detect whether something is
                  stored at a key. Because of this (and because there were no built-in
                  alternatives), Objects have been used as Maps historically; however,
                  there are important differences that make using a Map preferable in
                  certain cases:




                  • The keys of an Object are Strings and Symbols, whereas they can be any value for a Map, including functions, objects, and any primitive.

                  • The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of
                    insertion.

                  • You can get the size of a Map easily with the size property, while the number of properties in an Object must be determined manually.

                  • A Map is an iterable and can thus be directly iterated, whereas iterating over an Object requires obtaining its keys in some fashion
                    and iterating over them.

                  • An Object has a prototype, so there are default keys in the map that could collide with your keys if you're not careful. As of ES5 this can
                    be bypassed by using map = Object.create(null), but this is seldom
                    done.

                  • A Map may perform better in scenarios involving frequent addition and removal of key pairs.








                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Dec 27 at 13:32









                  Andrei

                  1,3381418




                  1,3381418










                  answered Dec 15 '08 at 14:21









                  Eugene Lazutkin

                  38.6k84453




                  38.6k84453








                  • 11




                    This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
                    – beefeather
                    Dec 2 '12 at 4:14






                  • 28




                    Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
                    – Matt R
                    Feb 11 '13 at 11:05






                  • 26




                    @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
                    – Eugene Lazutkin
                    Feb 13 '13 at 19:23






                  • 4




                    @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
                    – Matt R
                    Feb 22 '13 at 7:53






                  • 5




                    @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
                    – Matt R
                    Feb 24 '13 at 8:39














                  • 11




                    This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
                    – beefeather
                    Dec 2 '12 at 4:14






                  • 28




                    Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
                    – Matt R
                    Feb 11 '13 at 11:05






                  • 26




                    @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
                    – Eugene Lazutkin
                    Feb 13 '13 at 19:23






                  • 4




                    @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
                    – Matt R
                    Feb 22 '13 at 7:53






                  • 5




                    @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
                    – Matt R
                    Feb 24 '13 at 8:39








                  11




                  11




                  This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
                  – beefeather
                  Dec 2 '12 at 4:14




                  This doesn't look like a proper map, because you don't handle collisions. If this happens to be true: hash(obj1) == hash(obj2), you is going to lose your data.
                  – beefeather
                  Dec 2 '12 at 4:14




                  28




                  28




                  Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
                  – Matt R
                  Feb 11 '13 at 11:05




                  Heaven help you when both "PAUL AINLEY" and "PAULA INLEY" register in your system...
                  – Matt R
                  Feb 11 '13 at 11:05




                  26




                  26




                  @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
                  – Eugene Lazutkin
                  Feb 13 '13 at 19:23




                  @MattR Actually your example will work properly without the heaven help even with a mock hash function. I hope that other readers will realize that an over-simplified non-realistic hash function was used as a placeholder to demonstrate a different technique. Both code comments, and the answer itself stress that it is not real. Selection of proper keys are discussed in the last paragraph of the answer.
                  – Eugene Lazutkin
                  Feb 13 '13 at 19:23




                  4




                  4




                  @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
                  – Matt R
                  Feb 22 '13 at 7:53




                  @EugeneLazutkin I don't have any misunderstandings. I was trying to alert you to your mistake, but you can only lead a horse to water, you can't make it drink.
                  – Matt R
                  Feb 22 '13 at 7:53




                  5




                  5




                  @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
                  – Matt R
                  Feb 24 '13 at 8:39




                  @EugeneLazutkin -- you are still mistaken, I'm afraid. Your example is still prone to hash collisions. Don't think that just putting the last name first will somehow help you!
                  – Matt R
                  Feb 24 '13 at 8:39













                  167














                  Problem description



                  JavaScript has no built-in general map type (sometimes called associative array or dictionary) which allows to access arbitrary values by arbitrary keys. JavaScript's fundamental data structure is the object, a special type of map which only accepts strings as keys and has special semantics like prototypical inheritance, getters and setters and some further voodoo.



                  When usings objects as maps, you have to remember that the key will be converted to a string value via toString(), which results in mapping 5 and '5' to the same value and all objects which don't overwrite the toString() method to the value indexed by '[object Object]'. You might also involuntarily access its inherited properties if you don't check hasOwnProperty().



                  JavaScript's built-in array type does not help one bit: JavaScript arrays are not associative arrays, but just objects with a few more special properties. If you want to know why they can't be used as maps, look here.



                  Eugene's Solution



                  Eugene Lazutkin already described the basic idea of using a custom hash function to generate unique strings which can be used to look up the associated values as properties of a dictionary object. This will most likely be the fastest solution, because objects are internally implemented as hash tables.





                  • Note: Hash tables (sometimes called hash maps) are a particular implementation of the map concept using a backing array and lookup via numeric hash values. The runtime environment might use other structures (such as search trees or skip lists) to implement JavaScript objects, but as objects are the fundamental data structure, they should be sufficiently optimised.


                  In order to get a unique hash value for arbitrary objects, one possibility is to use a global counter and cache the hash value in the object itself (eg in a property named __hash).



                  A hash function which does this is and works for both primitive values and objects is:



                  function hash(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  }

                  hash.current = 0;


                  This function can be used as described by Eugene. For convenience, we will further wrap it in a Map class.



                  My Map implementation



                  The following implementation will additionally store the key-value-pairs in a doubly linked list in order to allow fast iteration over both keys and values. To supply your own hash function, you can overwrite the instance's hash() method after creation.



                  // linking the key-value-pairs is optional
                  // if no argument is provided, linkItems === undefined, i.e. !== false
                  // --> linking will be enabled
                  function Map(linkItems) {
                  this.current = undefined;
                  this.size = 0;

                  if(linkItems === false)
                  this.disableLinking();
                  }

                  Map.noop = function() {
                  return this;
                  };

                  Map.illegal = function() {
                  throw new Error("illegal operation for maps without linking");
                  };

                  // map initialisation from existing object
                  // doesn't add inherited properties if not explicitly instructed to:
                  // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                  // --> inherited properties won't be added
                  Map.from = function(obj, foreignKeys) {
                  var map = new Map;

                  for(var prop in obj) {
                  if(foreignKeys || obj.hasOwnProperty(prop))
                  map.put(prop, obj[prop]);
                  }

                  return map;
                  };

                  Map.prototype.disableLinking = function() {
                  this.link = Map.noop;
                  this.unlink = Map.noop;
                  this.disableLinking = Map.noop;
                  this.next = Map.illegal;
                  this.key = Map.illegal;
                  this.value = Map.illegal;
                  this.removeAll = Map.illegal;

                  return this;
                  };

                  // overwrite in Map instance if necessary
                  Map.prototype.hash = function(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  };

                  Map.prototype.hash.current = 0;

                  // --- mapping functions

                  Map.prototype.get = function(key) {
                  var item = this[this.hash(key)];
                  return item === undefined ? undefined : item.value;
                  };

                  Map.prototype.put = function(key, value) {
                  var hash = this.hash(key);

                  if(this[hash] === undefined) {
                  var item = { key : key, value : value };
                  this[hash] = item;

                  this.link(item);
                  ++this.size;
                  }
                  else this[hash].value = value;

                  return this;
                  };

                  Map.prototype.remove = function(key) {
                  var hash = this.hash(key);
                  var item = this[hash];

                  if(item !== undefined) {
                  --this.size;
                  this.unlink(item);

                  delete this[hash];
                  }

                  return this;
                  };

                  // only works if linked
                  Map.prototype.removeAll = function() {
                  while(this.size)
                  this.remove(this.key());

                  return this;
                  };

                  // --- linked list helper functions

                  Map.prototype.link = function(item) {
                  if(this.size == 0) {
                  item.prev = item;
                  item.next = item;
                  this.current = item;
                  }
                  else {
                  item.prev = this.current.prev;
                  item.prev.next = item;
                  item.next = this.current;
                  this.current.prev = item;
                  }
                  };

                  Map.prototype.unlink = function(item) {
                  if(this.size == 0)
                  this.current = undefined;
                  else {
                  item.prev.next = item.next;
                  item.next.prev = item.prev;
                  if(item === this.current)
                  this.current = item.next;
                  }
                  };

                  // --- iterator functions - only work if map is linked

                  Map.prototype.next = function() {
                  this.current = this.current.next;
                  };

                  Map.prototype.key = function() {
                  return this.current.key;
                  };

                  Map.prototype.value = function() {
                  return this.current.value;
                  };


                  Example



                  The following script



                  var map = new Map;

                  map.put('spam', 'eggs').
                  put('foo', 'bar').
                  put('foo', 'baz').
                  put({}, 'an object').
                  put({}, 'another object').
                  put(5, 'five').
                  put(5, 'five again').
                  put('5', 'another five');

                  for(var i = 0; i++ < map.size; map.next())
                  document.writeln(map.hash(map.key()) + ' : ' + map.value());


                  generates this output:



                  string spam : eggs
                  string foo : baz
                  object 1 : an object
                  object 2 : another object
                  number 5 : five again
                  string 5 : another five


                  Further considerations



                  PEZ suggested to overwrite the toString() method, presumably with our hash function. This is not feasible because it doesn't work for primitive values (changing toString() for primitives is a very bad idea). If we want toString() to return meaningful values for arbitrary objects, we would have to modify Object.prototype, which some people (myself not included) consider verboten.





                  Edit: The current version of my Map implementation as well as other JavaScript goodies can be obtained from here.






                  share|improve this answer























                  • ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
                    – broofa
                    Jan 18 '12 at 11:43










                  • Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
                    – ahcox
                    Apr 9 '13 at 13:38










                  • hi @Christoph, could you update your link to where I can find your Map implementation?
                    – NumenorForLife
                    Jul 30 '13 at 18:16






                  • 2




                    @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
                    – Christoph
                    Jul 30 '13 at 20:16


















                  167














                  Problem description



                  JavaScript has no built-in general map type (sometimes called associative array or dictionary) which allows to access arbitrary values by arbitrary keys. JavaScript's fundamental data structure is the object, a special type of map which only accepts strings as keys and has special semantics like prototypical inheritance, getters and setters and some further voodoo.



                  When usings objects as maps, you have to remember that the key will be converted to a string value via toString(), which results in mapping 5 and '5' to the same value and all objects which don't overwrite the toString() method to the value indexed by '[object Object]'. You might also involuntarily access its inherited properties if you don't check hasOwnProperty().



                  JavaScript's built-in array type does not help one bit: JavaScript arrays are not associative arrays, but just objects with a few more special properties. If you want to know why they can't be used as maps, look here.



                  Eugene's Solution



                  Eugene Lazutkin already described the basic idea of using a custom hash function to generate unique strings which can be used to look up the associated values as properties of a dictionary object. This will most likely be the fastest solution, because objects are internally implemented as hash tables.





                  • Note: Hash tables (sometimes called hash maps) are a particular implementation of the map concept using a backing array and lookup via numeric hash values. The runtime environment might use other structures (such as search trees or skip lists) to implement JavaScript objects, but as objects are the fundamental data structure, they should be sufficiently optimised.


                  In order to get a unique hash value for arbitrary objects, one possibility is to use a global counter and cache the hash value in the object itself (eg in a property named __hash).



                  A hash function which does this is and works for both primitive values and objects is:



                  function hash(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  }

                  hash.current = 0;


                  This function can be used as described by Eugene. For convenience, we will further wrap it in a Map class.



                  My Map implementation



                  The following implementation will additionally store the key-value-pairs in a doubly linked list in order to allow fast iteration over both keys and values. To supply your own hash function, you can overwrite the instance's hash() method after creation.



                  // linking the key-value-pairs is optional
                  // if no argument is provided, linkItems === undefined, i.e. !== false
                  // --> linking will be enabled
                  function Map(linkItems) {
                  this.current = undefined;
                  this.size = 0;

                  if(linkItems === false)
                  this.disableLinking();
                  }

                  Map.noop = function() {
                  return this;
                  };

                  Map.illegal = function() {
                  throw new Error("illegal operation for maps without linking");
                  };

                  // map initialisation from existing object
                  // doesn't add inherited properties if not explicitly instructed to:
                  // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                  // --> inherited properties won't be added
                  Map.from = function(obj, foreignKeys) {
                  var map = new Map;

                  for(var prop in obj) {
                  if(foreignKeys || obj.hasOwnProperty(prop))
                  map.put(prop, obj[prop]);
                  }

                  return map;
                  };

                  Map.prototype.disableLinking = function() {
                  this.link = Map.noop;
                  this.unlink = Map.noop;
                  this.disableLinking = Map.noop;
                  this.next = Map.illegal;
                  this.key = Map.illegal;
                  this.value = Map.illegal;
                  this.removeAll = Map.illegal;

                  return this;
                  };

                  // overwrite in Map instance if necessary
                  Map.prototype.hash = function(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  };

                  Map.prototype.hash.current = 0;

                  // --- mapping functions

                  Map.prototype.get = function(key) {
                  var item = this[this.hash(key)];
                  return item === undefined ? undefined : item.value;
                  };

                  Map.prototype.put = function(key, value) {
                  var hash = this.hash(key);

                  if(this[hash] === undefined) {
                  var item = { key : key, value : value };
                  this[hash] = item;

                  this.link(item);
                  ++this.size;
                  }
                  else this[hash].value = value;

                  return this;
                  };

                  Map.prototype.remove = function(key) {
                  var hash = this.hash(key);
                  var item = this[hash];

                  if(item !== undefined) {
                  --this.size;
                  this.unlink(item);

                  delete this[hash];
                  }

                  return this;
                  };

                  // only works if linked
                  Map.prototype.removeAll = function() {
                  while(this.size)
                  this.remove(this.key());

                  return this;
                  };

                  // --- linked list helper functions

                  Map.prototype.link = function(item) {
                  if(this.size == 0) {
                  item.prev = item;
                  item.next = item;
                  this.current = item;
                  }
                  else {
                  item.prev = this.current.prev;
                  item.prev.next = item;
                  item.next = this.current;
                  this.current.prev = item;
                  }
                  };

                  Map.prototype.unlink = function(item) {
                  if(this.size == 0)
                  this.current = undefined;
                  else {
                  item.prev.next = item.next;
                  item.next.prev = item.prev;
                  if(item === this.current)
                  this.current = item.next;
                  }
                  };

                  // --- iterator functions - only work if map is linked

                  Map.prototype.next = function() {
                  this.current = this.current.next;
                  };

                  Map.prototype.key = function() {
                  return this.current.key;
                  };

                  Map.prototype.value = function() {
                  return this.current.value;
                  };


                  Example



                  The following script



                  var map = new Map;

                  map.put('spam', 'eggs').
                  put('foo', 'bar').
                  put('foo', 'baz').
                  put({}, 'an object').
                  put({}, 'another object').
                  put(5, 'five').
                  put(5, 'five again').
                  put('5', 'another five');

                  for(var i = 0; i++ < map.size; map.next())
                  document.writeln(map.hash(map.key()) + ' : ' + map.value());


                  generates this output:



                  string spam : eggs
                  string foo : baz
                  object 1 : an object
                  object 2 : another object
                  number 5 : five again
                  string 5 : another five


                  Further considerations



                  PEZ suggested to overwrite the toString() method, presumably with our hash function. This is not feasible because it doesn't work for primitive values (changing toString() for primitives is a very bad idea). If we want toString() to return meaningful values for arbitrary objects, we would have to modify Object.prototype, which some people (myself not included) consider verboten.





                  Edit: The current version of my Map implementation as well as other JavaScript goodies can be obtained from here.






                  share|improve this answer























                  • ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
                    – broofa
                    Jan 18 '12 at 11:43










                  • Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
                    – ahcox
                    Apr 9 '13 at 13:38










                  • hi @Christoph, could you update your link to where I can find your Map implementation?
                    – NumenorForLife
                    Jul 30 '13 at 18:16






                  • 2




                    @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
                    – Christoph
                    Jul 30 '13 at 20:16
















                  167












                  167








                  167






                  Problem description



                  JavaScript has no built-in general map type (sometimes called associative array or dictionary) which allows to access arbitrary values by arbitrary keys. JavaScript's fundamental data structure is the object, a special type of map which only accepts strings as keys and has special semantics like prototypical inheritance, getters and setters and some further voodoo.



                  When usings objects as maps, you have to remember that the key will be converted to a string value via toString(), which results in mapping 5 and '5' to the same value and all objects which don't overwrite the toString() method to the value indexed by '[object Object]'. You might also involuntarily access its inherited properties if you don't check hasOwnProperty().



                  JavaScript's built-in array type does not help one bit: JavaScript arrays are not associative arrays, but just objects with a few more special properties. If you want to know why they can't be used as maps, look here.



                  Eugene's Solution



                  Eugene Lazutkin already described the basic idea of using a custom hash function to generate unique strings which can be used to look up the associated values as properties of a dictionary object. This will most likely be the fastest solution, because objects are internally implemented as hash tables.





                  • Note: Hash tables (sometimes called hash maps) are a particular implementation of the map concept using a backing array and lookup via numeric hash values. The runtime environment might use other structures (such as search trees or skip lists) to implement JavaScript objects, but as objects are the fundamental data structure, they should be sufficiently optimised.


                  In order to get a unique hash value for arbitrary objects, one possibility is to use a global counter and cache the hash value in the object itself (eg in a property named __hash).



                  A hash function which does this is and works for both primitive values and objects is:



                  function hash(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  }

                  hash.current = 0;


                  This function can be used as described by Eugene. For convenience, we will further wrap it in a Map class.



                  My Map implementation



                  The following implementation will additionally store the key-value-pairs in a doubly linked list in order to allow fast iteration over both keys and values. To supply your own hash function, you can overwrite the instance's hash() method after creation.



                  // linking the key-value-pairs is optional
                  // if no argument is provided, linkItems === undefined, i.e. !== false
                  // --> linking will be enabled
                  function Map(linkItems) {
                  this.current = undefined;
                  this.size = 0;

                  if(linkItems === false)
                  this.disableLinking();
                  }

                  Map.noop = function() {
                  return this;
                  };

                  Map.illegal = function() {
                  throw new Error("illegal operation for maps without linking");
                  };

                  // map initialisation from existing object
                  // doesn't add inherited properties if not explicitly instructed to:
                  // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                  // --> inherited properties won't be added
                  Map.from = function(obj, foreignKeys) {
                  var map = new Map;

                  for(var prop in obj) {
                  if(foreignKeys || obj.hasOwnProperty(prop))
                  map.put(prop, obj[prop]);
                  }

                  return map;
                  };

                  Map.prototype.disableLinking = function() {
                  this.link = Map.noop;
                  this.unlink = Map.noop;
                  this.disableLinking = Map.noop;
                  this.next = Map.illegal;
                  this.key = Map.illegal;
                  this.value = Map.illegal;
                  this.removeAll = Map.illegal;

                  return this;
                  };

                  // overwrite in Map instance if necessary
                  Map.prototype.hash = function(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  };

                  Map.prototype.hash.current = 0;

                  // --- mapping functions

                  Map.prototype.get = function(key) {
                  var item = this[this.hash(key)];
                  return item === undefined ? undefined : item.value;
                  };

                  Map.prototype.put = function(key, value) {
                  var hash = this.hash(key);

                  if(this[hash] === undefined) {
                  var item = { key : key, value : value };
                  this[hash] = item;

                  this.link(item);
                  ++this.size;
                  }
                  else this[hash].value = value;

                  return this;
                  };

                  Map.prototype.remove = function(key) {
                  var hash = this.hash(key);
                  var item = this[hash];

                  if(item !== undefined) {
                  --this.size;
                  this.unlink(item);

                  delete this[hash];
                  }

                  return this;
                  };

                  // only works if linked
                  Map.prototype.removeAll = function() {
                  while(this.size)
                  this.remove(this.key());

                  return this;
                  };

                  // --- linked list helper functions

                  Map.prototype.link = function(item) {
                  if(this.size == 0) {
                  item.prev = item;
                  item.next = item;
                  this.current = item;
                  }
                  else {
                  item.prev = this.current.prev;
                  item.prev.next = item;
                  item.next = this.current;
                  this.current.prev = item;
                  }
                  };

                  Map.prototype.unlink = function(item) {
                  if(this.size == 0)
                  this.current = undefined;
                  else {
                  item.prev.next = item.next;
                  item.next.prev = item.prev;
                  if(item === this.current)
                  this.current = item.next;
                  }
                  };

                  // --- iterator functions - only work if map is linked

                  Map.prototype.next = function() {
                  this.current = this.current.next;
                  };

                  Map.prototype.key = function() {
                  return this.current.key;
                  };

                  Map.prototype.value = function() {
                  return this.current.value;
                  };


                  Example



                  The following script



                  var map = new Map;

                  map.put('spam', 'eggs').
                  put('foo', 'bar').
                  put('foo', 'baz').
                  put({}, 'an object').
                  put({}, 'another object').
                  put(5, 'five').
                  put(5, 'five again').
                  put('5', 'another five');

                  for(var i = 0; i++ < map.size; map.next())
                  document.writeln(map.hash(map.key()) + ' : ' + map.value());


                  generates this output:



                  string spam : eggs
                  string foo : baz
                  object 1 : an object
                  object 2 : another object
                  number 5 : five again
                  string 5 : another five


                  Further considerations



                  PEZ suggested to overwrite the toString() method, presumably with our hash function. This is not feasible because it doesn't work for primitive values (changing toString() for primitives is a very bad idea). If we want toString() to return meaningful values for arbitrary objects, we would have to modify Object.prototype, which some people (myself not included) consider verboten.





                  Edit: The current version of my Map implementation as well as other JavaScript goodies can be obtained from here.






                  share|improve this answer














                  Problem description



                  JavaScript has no built-in general map type (sometimes called associative array or dictionary) which allows to access arbitrary values by arbitrary keys. JavaScript's fundamental data structure is the object, a special type of map which only accepts strings as keys and has special semantics like prototypical inheritance, getters and setters and some further voodoo.



                  When usings objects as maps, you have to remember that the key will be converted to a string value via toString(), which results in mapping 5 and '5' to the same value and all objects which don't overwrite the toString() method to the value indexed by '[object Object]'. You might also involuntarily access its inherited properties if you don't check hasOwnProperty().



                  JavaScript's built-in array type does not help one bit: JavaScript arrays are not associative arrays, but just objects with a few more special properties. If you want to know why they can't be used as maps, look here.



                  Eugene's Solution



                  Eugene Lazutkin already described the basic idea of using a custom hash function to generate unique strings which can be used to look up the associated values as properties of a dictionary object. This will most likely be the fastest solution, because objects are internally implemented as hash tables.





                  • Note: Hash tables (sometimes called hash maps) are a particular implementation of the map concept using a backing array and lookup via numeric hash values. The runtime environment might use other structures (such as search trees or skip lists) to implement JavaScript objects, but as objects are the fundamental data structure, they should be sufficiently optimised.


                  In order to get a unique hash value for arbitrary objects, one possibility is to use a global counter and cache the hash value in the object itself (eg in a property named __hash).



                  A hash function which does this is and works for both primitive values and objects is:



                  function hash(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  }

                  hash.current = 0;


                  This function can be used as described by Eugene. For convenience, we will further wrap it in a Map class.



                  My Map implementation



                  The following implementation will additionally store the key-value-pairs in a doubly linked list in order to allow fast iteration over both keys and values. To supply your own hash function, you can overwrite the instance's hash() method after creation.



                  // linking the key-value-pairs is optional
                  // if no argument is provided, linkItems === undefined, i.e. !== false
                  // --> linking will be enabled
                  function Map(linkItems) {
                  this.current = undefined;
                  this.size = 0;

                  if(linkItems === false)
                  this.disableLinking();
                  }

                  Map.noop = function() {
                  return this;
                  };

                  Map.illegal = function() {
                  throw new Error("illegal operation for maps without linking");
                  };

                  // map initialisation from existing object
                  // doesn't add inherited properties if not explicitly instructed to:
                  // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                  // --> inherited properties won't be added
                  Map.from = function(obj, foreignKeys) {
                  var map = new Map;

                  for(var prop in obj) {
                  if(foreignKeys || obj.hasOwnProperty(prop))
                  map.put(prop, obj[prop]);
                  }

                  return map;
                  };

                  Map.prototype.disableLinking = function() {
                  this.link = Map.noop;
                  this.unlink = Map.noop;
                  this.disableLinking = Map.noop;
                  this.next = Map.illegal;
                  this.key = Map.illegal;
                  this.value = Map.illegal;
                  this.removeAll = Map.illegal;

                  return this;
                  };

                  // overwrite in Map instance if necessary
                  Map.prototype.hash = function(value) {
                  return (typeof value) + ' ' + (value instanceof Object ?
                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                  value.toString());
                  };

                  Map.prototype.hash.current = 0;

                  // --- mapping functions

                  Map.prototype.get = function(key) {
                  var item = this[this.hash(key)];
                  return item === undefined ? undefined : item.value;
                  };

                  Map.prototype.put = function(key, value) {
                  var hash = this.hash(key);

                  if(this[hash] === undefined) {
                  var item = { key : key, value : value };
                  this[hash] = item;

                  this.link(item);
                  ++this.size;
                  }
                  else this[hash].value = value;

                  return this;
                  };

                  Map.prototype.remove = function(key) {
                  var hash = this.hash(key);
                  var item = this[hash];

                  if(item !== undefined) {
                  --this.size;
                  this.unlink(item);

                  delete this[hash];
                  }

                  return this;
                  };

                  // only works if linked
                  Map.prototype.removeAll = function() {
                  while(this.size)
                  this.remove(this.key());

                  return this;
                  };

                  // --- linked list helper functions

                  Map.prototype.link = function(item) {
                  if(this.size == 0) {
                  item.prev = item;
                  item.next = item;
                  this.current = item;
                  }
                  else {
                  item.prev = this.current.prev;
                  item.prev.next = item;
                  item.next = this.current;
                  this.current.prev = item;
                  }
                  };

                  Map.prototype.unlink = function(item) {
                  if(this.size == 0)
                  this.current = undefined;
                  else {
                  item.prev.next = item.next;
                  item.next.prev = item.prev;
                  if(item === this.current)
                  this.current = item.next;
                  }
                  };

                  // --- iterator functions - only work if map is linked

                  Map.prototype.next = function() {
                  this.current = this.current.next;
                  };

                  Map.prototype.key = function() {
                  return this.current.key;
                  };

                  Map.prototype.value = function() {
                  return this.current.value;
                  };


                  Example



                  The following script



                  var map = new Map;

                  map.put('spam', 'eggs').
                  put('foo', 'bar').
                  put('foo', 'baz').
                  put({}, 'an object').
                  put({}, 'another object').
                  put(5, 'five').
                  put(5, 'five again').
                  put('5', 'another five');

                  for(var i = 0; i++ < map.size; map.next())
                  document.writeln(map.hash(map.key()) + ' : ' + map.value());


                  generates this output:



                  string spam : eggs
                  string foo : baz
                  object 1 : an object
                  object 2 : another object
                  number 5 : five again
                  string 5 : another five


                  Further considerations



                  PEZ suggested to overwrite the toString() method, presumably with our hash function. This is not feasible because it doesn't work for primitive values (changing toString() for primitives is a very bad idea). If we want toString() to return meaningful values for arbitrary objects, we would have to modify Object.prototype, which some people (myself not included) consider verboten.





                  Edit: The current version of my Map implementation as well as other JavaScript goodies can be obtained from here.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Feb 4 '09 at 19:52


























                  community wiki





                  19 revs
                  Christoph













                  • ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
                    – broofa
                    Jan 18 '12 at 11:43










                  • Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
                    – ahcox
                    Apr 9 '13 at 13:38










                  • hi @Christoph, could you update your link to where I can find your Map implementation?
                    – NumenorForLife
                    Jul 30 '13 at 18:16






                  • 2




                    @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
                    – Christoph
                    Jul 30 '13 at 20:16




















                  • ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
                    – broofa
                    Jan 18 '12 at 11:43










                  • Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
                    – ahcox
                    Apr 9 '13 at 13:38










                  • hi @Christoph, could you update your link to where I can find your Map implementation?
                    – NumenorForLife
                    Jul 30 '13 at 18:16






                  • 2




                    @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
                    – Christoph
                    Jul 30 '13 at 20:16


















                  ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
                  – broofa
                  Jan 18 '12 at 11:43




                  ES5 deprecates the use of callee (goo.gl/EeStE). Instead, I suggest Map._counter = 0, and in the Map constructor do this._hash = 'object ' + Map._counter++. Then hash() becomes return (value && value._hash) || (typeof(value) + ' ' + String(value));
                  – broofa
                  Jan 18 '12 at 11:43












                  Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
                  – ahcox
                  Apr 9 '13 at 13:38




                  Link to code is broken: mercurial.intuxication.org/hg/js-hacks/raw-file/tip/map.js
                  – ahcox
                  Apr 9 '13 at 13:38












                  hi @Christoph, could you update your link to where I can find your Map implementation?
                  – NumenorForLife
                  Jul 30 '13 at 18:16




                  hi @Christoph, could you update your link to where I can find your Map implementation?
                  – NumenorForLife
                  Jul 30 '13 at 18:16




                  2




                  2




                  @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
                  – Christoph
                  Jul 30 '13 at 20:16






                  @jsc123: I'll look into that - for now you can get a dump of the repository at pikacode.com/mercurial.intuxication.org/js-hacks.tar.gz
                  – Christoph
                  Jul 30 '13 at 20:16













                  39














                  I know this question is pretty old, but there are some really great solutions nowadays with external libraries.




                  • collections.js

                  • immutable


                  JavaScript also has its language provided Map as well.




                  • Map






                  share|improve this answer



















                  • 2




                    This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
                    – Phung D. An
                    Jan 30 '15 at 18:21






                  • 1




                    Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
                    – Code Bling
                    Sep 23 '16 at 5:42












                  • @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
                    – Jamel Toms
                    Oct 25 '16 at 15:12






                  • 2




                    That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
                    – Code Bling
                    Oct 25 '16 at 20:40
















                  39














                  I know this question is pretty old, but there are some really great solutions nowadays with external libraries.




                  • collections.js

                  • immutable


                  JavaScript also has its language provided Map as well.




                  • Map






                  share|improve this answer



















                  • 2




                    This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
                    – Phung D. An
                    Jan 30 '15 at 18:21






                  • 1




                    Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
                    – Code Bling
                    Sep 23 '16 at 5:42












                  • @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
                    – Jamel Toms
                    Oct 25 '16 at 15:12






                  • 2




                    That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
                    – Code Bling
                    Oct 25 '16 at 20:40














                  39












                  39








                  39






                  I know this question is pretty old, but there are some really great solutions nowadays with external libraries.




                  • collections.js

                  • immutable


                  JavaScript also has its language provided Map as well.




                  • Map






                  share|improve this answer














                  I know this question is pretty old, but there are some really great solutions nowadays with external libraries.




                  • collections.js

                  • immutable


                  JavaScript also has its language provided Map as well.




                  • Map







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Dec 22 '16 at 17:31

























                  answered Sep 28 '14 at 17:50









                  Jamel Toms

                  3,13222024




                  3,13222024








                  • 2




                    This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
                    – Phung D. An
                    Jan 30 '15 at 18:21






                  • 1




                    Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
                    – Code Bling
                    Sep 23 '16 at 5:42












                  • @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
                    – Jamel Toms
                    Oct 25 '16 at 15:12






                  • 2




                    That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
                    – Code Bling
                    Oct 25 '16 at 20:40














                  • 2




                    This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
                    – Phung D. An
                    Jan 30 '15 at 18:21






                  • 1




                    Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
                    – Code Bling
                    Sep 23 '16 at 5:42












                  • @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
                    – Jamel Toms
                    Oct 25 '16 at 15:12






                  • 2




                    That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
                    – Code Bling
                    Oct 25 '16 at 20:40








                  2




                  2




                  This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
                  – Phung D. An
                  Jan 30 '15 at 18:21




                  This is the way to move forward to 21th century. Too bad that I found your post after finishing my code with some ugly home made Map. WEEE need more vote for your answer
                  – Phung D. An
                  Jan 30 '15 at 18:21




                  1




                  1




                  Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
                  – Code Bling
                  Sep 23 '16 at 5:42






                  Collections.js has some implementations, but I can't find any in underscore.js or lodash ... what were you referring to in underscore that would be useful?
                  – Code Bling
                  Sep 23 '16 at 5:42














                  @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
                  – Jamel Toms
                  Oct 25 '16 at 15:12




                  @CodeBling no idea. i think i got it confused with the map function. i'm going to remove it from the answer.
                  – Jamel Toms
                  Oct 25 '16 at 15:12




                  2




                  2




                  That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
                  – Code Bling
                  Oct 25 '16 at 20:40




                  That's fair. Anyone considering Collections.js should be aware that it modifies global Array, Function, Object and Regexp prototypes in a problematic fashion (see the issues I encountered here). Although I was initially very pleased with collections.js (and thus this answer), the risks associated with using it were too high, so I dropped it. Only kriskowal's v2 branch of collections.js (specifically, v2.0.2+) eliminates the global prototype modifications and is safe to use.
                  – Code Bling
                  Oct 25 '16 at 20:40











                  19














                  Here is an easy and convenient way of using something similar to the java map:



                  var map= {
                  'map_name_1': map_value_1,
                  'map_name_2': map_value_2,
                  'map_name_3': map_value_3,
                  'map_name_4': map_value_4
                  }


                  And to get the value:



                  alert( map['map_name_1'] );    // fives the value of map_value_1

                  ...... etc .....





                  share|improve this answer



















                  • 3




                    best answer right here +1
                    – marcusshep
                    May 31 '16 at 14:38






                  • 2




                    This works for string keys only. I believe the OP was interested in using keys of any type.
                    – fractor
                    Sep 19 '17 at 14:32
















                  19














                  Here is an easy and convenient way of using something similar to the java map:



                  var map= {
                  'map_name_1': map_value_1,
                  'map_name_2': map_value_2,
                  'map_name_3': map_value_3,
                  'map_name_4': map_value_4
                  }


                  And to get the value:



                  alert( map['map_name_1'] );    // fives the value of map_value_1

                  ...... etc .....





                  share|improve this answer



















                  • 3




                    best answer right here +1
                    – marcusshep
                    May 31 '16 at 14:38






                  • 2




                    This works for string keys only. I believe the OP was interested in using keys of any type.
                    – fractor
                    Sep 19 '17 at 14:32














                  19












                  19








                  19






                  Here is an easy and convenient way of using something similar to the java map:



                  var map= {
                  'map_name_1': map_value_1,
                  'map_name_2': map_value_2,
                  'map_name_3': map_value_3,
                  'map_name_4': map_value_4
                  }


                  And to get the value:



                  alert( map['map_name_1'] );    // fives the value of map_value_1

                  ...... etc .....





                  share|improve this answer














                  Here is an easy and convenient way of using something similar to the java map:



                  var map= {
                  'map_name_1': map_value_1,
                  'map_name_2': map_value_2,
                  'map_name_3': map_value_3,
                  'map_name_4': map_value_4
                  }


                  And to get the value:



                  alert( map['map_name_1'] );    // fives the value of map_value_1

                  ...... etc .....






                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Dec 11 '12 at 22:15









                  Seth Tisue

                  23.2k960125




                  23.2k960125










                  answered Jul 10 '12 at 11:32









                  Miloš

                  8,58737119222




                  8,58737119222








                  • 3




                    best answer right here +1
                    – marcusshep
                    May 31 '16 at 14:38






                  • 2




                    This works for string keys only. I believe the OP was interested in using keys of any type.
                    – fractor
                    Sep 19 '17 at 14:32














                  • 3




                    best answer right here +1
                    – marcusshep
                    May 31 '16 at 14:38






                  • 2




                    This works for string keys only. I believe the OP was interested in using keys of any type.
                    – fractor
                    Sep 19 '17 at 14:32








                  3




                  3




                  best answer right here +1
                  – marcusshep
                  May 31 '16 at 14:38




                  best answer right here +1
                  – marcusshep
                  May 31 '16 at 14:38




                  2




                  2




                  This works for string keys only. I believe the OP was interested in using keys of any type.
                  – fractor
                  Sep 19 '17 at 14:32




                  This works for string keys only. I believe the OP was interested in using keys of any type.
                  – fractor
                  Sep 19 '17 at 14:32











                  18














                  You can use ES6 WeakMap or Map:




                  • WeakMaps are key/value maps in which keys are objects.



                  • Map objects are simple key/value maps. Any value (both objects and primitive values) may be used as either a key or a value.



                  Be aware that neither is widely supported, but you can use ES6 Shim (requires native ES5 or ES5 Shim) to support Map, but not WeakMap (see why).






                  share|improve this answer


























                    18














                    You can use ES6 WeakMap or Map:




                    • WeakMaps are key/value maps in which keys are objects.



                    • Map objects are simple key/value maps. Any value (both objects and primitive values) may be used as either a key or a value.



                    Be aware that neither is widely supported, but you can use ES6 Shim (requires native ES5 or ES5 Shim) to support Map, but not WeakMap (see why).






                    share|improve this answer
























                      18












                      18








                      18






                      You can use ES6 WeakMap or Map:




                      • WeakMaps are key/value maps in which keys are objects.



                      • Map objects are simple key/value maps. Any value (both objects and primitive values) may be used as either a key or a value.



                      Be aware that neither is widely supported, but you can use ES6 Shim (requires native ES5 or ES5 Shim) to support Map, but not WeakMap (see why).






                      share|improve this answer












                      You can use ES6 WeakMap or Map:




                      • WeakMaps are key/value maps in which keys are objects.



                      • Map objects are simple key/value maps. Any value (both objects and primitive values) may be used as either a key or a value.



                      Be aware that neither is widely supported, but you can use ES6 Shim (requires native ES5 or ES5 Shim) to support Map, but not WeakMap (see why).







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Dec 26 '13 at 15:54









                      Oriol

                      155k33259357




                      155k33259357























                          14














                          According to ECMAScript 2015 (ES6) standard javascript has a Map implementation. More about which could be found here



                          Basic usage:



                          var myMap = new Map();
                          var keyString = "a string",
                          keyObj = {},
                          keyFunc = function () {};

                          // setting the values
                          myMap.set(keyString, "value associated with 'a string'");
                          myMap.set(keyObj, "value associated with keyObj");
                          myMap.set(keyFunc, "value associated with keyFunc");

                          myMap.size; // 3

                          // getting the values
                          myMap.get(keyString); // "value associated with 'a string'"
                          myMap.get(keyObj); // "value associated with keyObj"
                          myMap.get(keyFunc); // "value associated with keyFunc"





                          share|improve this answer


























                            14














                            According to ECMAScript 2015 (ES6) standard javascript has a Map implementation. More about which could be found here



                            Basic usage:



                            var myMap = new Map();
                            var keyString = "a string",
                            keyObj = {},
                            keyFunc = function () {};

                            // setting the values
                            myMap.set(keyString, "value associated with 'a string'");
                            myMap.set(keyObj, "value associated with keyObj");
                            myMap.set(keyFunc, "value associated with keyFunc");

                            myMap.size; // 3

                            // getting the values
                            myMap.get(keyString); // "value associated with 'a string'"
                            myMap.get(keyObj); // "value associated with keyObj"
                            myMap.get(keyFunc); // "value associated with keyFunc"





                            share|improve this answer
























                              14












                              14








                              14






                              According to ECMAScript 2015 (ES6) standard javascript has a Map implementation. More about which could be found here



                              Basic usage:



                              var myMap = new Map();
                              var keyString = "a string",
                              keyObj = {},
                              keyFunc = function () {};

                              // setting the values
                              myMap.set(keyString, "value associated with 'a string'");
                              myMap.set(keyObj, "value associated with keyObj");
                              myMap.set(keyFunc, "value associated with keyFunc");

                              myMap.size; // 3

                              // getting the values
                              myMap.get(keyString); // "value associated with 'a string'"
                              myMap.get(keyObj); // "value associated with keyObj"
                              myMap.get(keyFunc); // "value associated with keyFunc"





                              share|improve this answer












                              According to ECMAScript 2015 (ES6) standard javascript has a Map implementation. More about which could be found here



                              Basic usage:



                              var myMap = new Map();
                              var keyString = "a string",
                              keyObj = {},
                              keyFunc = function () {};

                              // setting the values
                              myMap.set(keyString, "value associated with 'a string'");
                              myMap.set(keyObj, "value associated with keyObj");
                              myMap.set(keyFunc, "value associated with keyFunc");

                              myMap.size; // 3

                              // getting the values
                              myMap.get(keyString); // "value associated with 'a string'"
                              myMap.get(keyObj); // "value associated with keyObj"
                              myMap.get(keyFunc); // "value associated with keyFunc"






                              share|improve this answer












                              share|improve this answer



                              share|improve this answer










                              answered Nov 23 '15 at 8:25









                              Riyafa Abdul Hameed

                              2,40622433




                              2,40622433























                                  13














                                  You'd have to store in some internal state couplets of object/value pairs



                                  HashMap = function(){
                                  this._dict = ;
                                  }
                                  HashMap.prototype._get = function(key){
                                  for(var i=0, couplet; couplet = this._dict[i]; i++){
                                  if(couplet[0] === key){
                                  return couplet;
                                  }
                                  }
                                  }
                                  HashMap.prototype.put = function(key, value){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  couplet[1] = value;
                                  }else{
                                  this._dict.push([key, value]);
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function(key){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  return couplet[1];
                                  }
                                  }


                                  And use it as such:



                                  var color = {}; // unique object instance
                                  var shape = {}; // unique object instance
                                  var map = new HashMap();
                                  map.put(color, "blue");
                                  map.put(shape, "round");
                                  console.log("Item is", map.get(color), "and", map.get(shape));


                                  Of course, this implementation is also somewhere along the lines of O(n). Eugene's examples above are the only way to get a hash that works with any sort of speed you'd expect from a real hash.



                                  Update:



                                  Another approach, along the lines of Eugene's answer is to somehow attach a unique ID to all objects. One of my favorite approaches is to take one of the built-in methods inherited from the Object superclass, replace it with a custom function passthrough and attach properties to that function object. If you were to rewrite my HashMap method to do this, it would look like:



                                  HashMap = function(){
                                  this._dict = {};
                                  }
                                  HashMap.prototype._shared = {id: 1};
                                  HashMap.prototype.put = function put(key, value){
                                  if(typeof key == "object"){
                                  if(!key.hasOwnProperty._id){
                                  key.hasOwnProperty = function(key){
                                  return Object.prototype.hasOwnProperty.call(this, key);
                                  }
                                  key.hasOwnProperty._id = this._shared.id++;
                                  }
                                  this._dict[key.hasOwnProperty._id] = value;
                                  }else{
                                  this._dict[key] = value;
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function get(key){
                                  if(typeof key == "object"){
                                  return this._dict[key.hasOwnProperty._id];
                                  }
                                  return this._dict[key];
                                  }


                                  This version appears to be only slightly faster, but in theory it will be significantly faster for large data sets.






                                  share|improve this answer























                                  • An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
                                    – Erik Allik
                                    Nov 26 '11 at 14:06










                                  • True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
                                    – pottedmeat
                                    Jan 4 '12 at 18:46


















                                  13














                                  You'd have to store in some internal state couplets of object/value pairs



                                  HashMap = function(){
                                  this._dict = ;
                                  }
                                  HashMap.prototype._get = function(key){
                                  for(var i=0, couplet; couplet = this._dict[i]; i++){
                                  if(couplet[0] === key){
                                  return couplet;
                                  }
                                  }
                                  }
                                  HashMap.prototype.put = function(key, value){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  couplet[1] = value;
                                  }else{
                                  this._dict.push([key, value]);
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function(key){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  return couplet[1];
                                  }
                                  }


                                  And use it as such:



                                  var color = {}; // unique object instance
                                  var shape = {}; // unique object instance
                                  var map = new HashMap();
                                  map.put(color, "blue");
                                  map.put(shape, "round");
                                  console.log("Item is", map.get(color), "and", map.get(shape));


                                  Of course, this implementation is also somewhere along the lines of O(n). Eugene's examples above are the only way to get a hash that works with any sort of speed you'd expect from a real hash.



                                  Update:



                                  Another approach, along the lines of Eugene's answer is to somehow attach a unique ID to all objects. One of my favorite approaches is to take one of the built-in methods inherited from the Object superclass, replace it with a custom function passthrough and attach properties to that function object. If you were to rewrite my HashMap method to do this, it would look like:



                                  HashMap = function(){
                                  this._dict = {};
                                  }
                                  HashMap.prototype._shared = {id: 1};
                                  HashMap.prototype.put = function put(key, value){
                                  if(typeof key == "object"){
                                  if(!key.hasOwnProperty._id){
                                  key.hasOwnProperty = function(key){
                                  return Object.prototype.hasOwnProperty.call(this, key);
                                  }
                                  key.hasOwnProperty._id = this._shared.id++;
                                  }
                                  this._dict[key.hasOwnProperty._id] = value;
                                  }else{
                                  this._dict[key] = value;
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function get(key){
                                  if(typeof key == "object"){
                                  return this._dict[key.hasOwnProperty._id];
                                  }
                                  return this._dict[key];
                                  }


                                  This version appears to be only slightly faster, but in theory it will be significantly faster for large data sets.






                                  share|improve this answer























                                  • An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
                                    – Erik Allik
                                    Nov 26 '11 at 14:06










                                  • True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
                                    – pottedmeat
                                    Jan 4 '12 at 18:46
















                                  13












                                  13








                                  13






                                  You'd have to store in some internal state couplets of object/value pairs



                                  HashMap = function(){
                                  this._dict = ;
                                  }
                                  HashMap.prototype._get = function(key){
                                  for(var i=0, couplet; couplet = this._dict[i]; i++){
                                  if(couplet[0] === key){
                                  return couplet;
                                  }
                                  }
                                  }
                                  HashMap.prototype.put = function(key, value){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  couplet[1] = value;
                                  }else{
                                  this._dict.push([key, value]);
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function(key){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  return couplet[1];
                                  }
                                  }


                                  And use it as such:



                                  var color = {}; // unique object instance
                                  var shape = {}; // unique object instance
                                  var map = new HashMap();
                                  map.put(color, "blue");
                                  map.put(shape, "round");
                                  console.log("Item is", map.get(color), "and", map.get(shape));


                                  Of course, this implementation is also somewhere along the lines of O(n). Eugene's examples above are the only way to get a hash that works with any sort of speed you'd expect from a real hash.



                                  Update:



                                  Another approach, along the lines of Eugene's answer is to somehow attach a unique ID to all objects. One of my favorite approaches is to take one of the built-in methods inherited from the Object superclass, replace it with a custom function passthrough and attach properties to that function object. If you were to rewrite my HashMap method to do this, it would look like:



                                  HashMap = function(){
                                  this._dict = {};
                                  }
                                  HashMap.prototype._shared = {id: 1};
                                  HashMap.prototype.put = function put(key, value){
                                  if(typeof key == "object"){
                                  if(!key.hasOwnProperty._id){
                                  key.hasOwnProperty = function(key){
                                  return Object.prototype.hasOwnProperty.call(this, key);
                                  }
                                  key.hasOwnProperty._id = this._shared.id++;
                                  }
                                  this._dict[key.hasOwnProperty._id] = value;
                                  }else{
                                  this._dict[key] = value;
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function get(key){
                                  if(typeof key == "object"){
                                  return this._dict[key.hasOwnProperty._id];
                                  }
                                  return this._dict[key];
                                  }


                                  This version appears to be only slightly faster, but in theory it will be significantly faster for large data sets.






                                  share|improve this answer














                                  You'd have to store in some internal state couplets of object/value pairs



                                  HashMap = function(){
                                  this._dict = ;
                                  }
                                  HashMap.prototype._get = function(key){
                                  for(var i=0, couplet; couplet = this._dict[i]; i++){
                                  if(couplet[0] === key){
                                  return couplet;
                                  }
                                  }
                                  }
                                  HashMap.prototype.put = function(key, value){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  couplet[1] = value;
                                  }else{
                                  this._dict.push([key, value]);
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function(key){
                                  var couplet = this._get(key);
                                  if(couplet){
                                  return couplet[1];
                                  }
                                  }


                                  And use it as such:



                                  var color = {}; // unique object instance
                                  var shape = {}; // unique object instance
                                  var map = new HashMap();
                                  map.put(color, "blue");
                                  map.put(shape, "round");
                                  console.log("Item is", map.get(color), "and", map.get(shape));


                                  Of course, this implementation is also somewhere along the lines of O(n). Eugene's examples above are the only way to get a hash that works with any sort of speed you'd expect from a real hash.



                                  Update:



                                  Another approach, along the lines of Eugene's answer is to somehow attach a unique ID to all objects. One of my favorite approaches is to take one of the built-in methods inherited from the Object superclass, replace it with a custom function passthrough and attach properties to that function object. If you were to rewrite my HashMap method to do this, it would look like:



                                  HashMap = function(){
                                  this._dict = {};
                                  }
                                  HashMap.prototype._shared = {id: 1};
                                  HashMap.prototype.put = function put(key, value){
                                  if(typeof key == "object"){
                                  if(!key.hasOwnProperty._id){
                                  key.hasOwnProperty = function(key){
                                  return Object.prototype.hasOwnProperty.call(this, key);
                                  }
                                  key.hasOwnProperty._id = this._shared.id++;
                                  }
                                  this._dict[key.hasOwnProperty._id] = value;
                                  }else{
                                  this._dict[key] = value;
                                  }
                                  return this; // for chaining
                                  }
                                  HashMap.prototype.get = function get(key){
                                  if(typeof key == "object"){
                                  return this._dict[key.hasOwnProperty._id];
                                  }
                                  return this._dict[key];
                                  }


                                  This version appears to be only slightly faster, but in theory it will be significantly faster for large data sets.







                                  share|improve this answer














                                  share|improve this answer



                                  share|improve this answer








                                  edited Dec 15 '08 at 17:57

























                                  answered Dec 15 '08 at 16:54









                                  pottedmeat

                                  3,0401139




                                  3,0401139












                                  • An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
                                    – Erik Allik
                                    Nov 26 '11 at 14:06










                                  • True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
                                    – pottedmeat
                                    Jan 4 '12 at 18:46




















                                  • An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
                                    – Erik Allik
                                    Nov 26 '11 at 14:06










                                  • True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
                                    – pottedmeat
                                    Jan 4 '12 at 18:46


















                                  An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
                                  – Erik Allik
                                  Nov 26 '11 at 14:06




                                  An associative array, i.e. an array of 2-tuples, is a Map, not a HashMap; a HashMap is a Map that uses hashes for better performance.
                                  – Erik Allik
                                  Nov 26 '11 at 14:06












                                  True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
                                  – pottedmeat
                                  Jan 4 '12 at 18:46






                                  True, but why split hairs on the topic? There's no way to create a true hash map in JavaScript since you can't get object memory addresses. And JavaScript's built-in object key/value pairs (used in my second example) may act as HashMaps, but not necessarily, as it is up to the runtime used in the browser as to how the lookup is implemented.
                                  – pottedmeat
                                  Jan 4 '12 at 18:46













                                  10














                                  Unfortunately, none of the above answers were good for my case: different key objects may have the same hash code. Therefore, I wrote a simple Java-like HashMap version:



                                  function HashMap() {
                                  this.buckets = {};
                                  }

                                  HashMap.prototype.put = function(key, value) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  bucket = new Array();
                                  this.buckets[hashCode] = bucket;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  bucket[i].value = value;
                                  return;
                                  }
                                  }
                                  bucket.push({ key: key, value: value });
                                  }

                                  HashMap.prototype.get = function(key) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  return null;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  return bucket[i].value;
                                  }
                                  }
                                  }

                                  HashMap.prototype.keys = function() {
                                  var keys = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  keys.push(bucket[i].key);
                                  }
                                  }
                                  return keys;
                                  }

                                  HashMap.prototype.values = function() {
                                  var values = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  values.push(bucket[i].value);
                                  }
                                  }
                                  return values;
                                  }


                                  Note: key objects must "implement" hashCode() and equals() methods.






                                  share|improve this answer



















                                  • 7




                                    The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
                                    – Erik Allik
                                    Nov 27 '11 at 19:15
















                                  10














                                  Unfortunately, none of the above answers were good for my case: different key objects may have the same hash code. Therefore, I wrote a simple Java-like HashMap version:



                                  function HashMap() {
                                  this.buckets = {};
                                  }

                                  HashMap.prototype.put = function(key, value) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  bucket = new Array();
                                  this.buckets[hashCode] = bucket;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  bucket[i].value = value;
                                  return;
                                  }
                                  }
                                  bucket.push({ key: key, value: value });
                                  }

                                  HashMap.prototype.get = function(key) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  return null;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  return bucket[i].value;
                                  }
                                  }
                                  }

                                  HashMap.prototype.keys = function() {
                                  var keys = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  keys.push(bucket[i].key);
                                  }
                                  }
                                  return keys;
                                  }

                                  HashMap.prototype.values = function() {
                                  var values = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  values.push(bucket[i].value);
                                  }
                                  }
                                  return values;
                                  }


                                  Note: key objects must "implement" hashCode() and equals() methods.






                                  share|improve this answer



















                                  • 7




                                    The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
                                    – Erik Allik
                                    Nov 27 '11 at 19:15














                                  10












                                  10








                                  10






                                  Unfortunately, none of the above answers were good for my case: different key objects may have the same hash code. Therefore, I wrote a simple Java-like HashMap version:



                                  function HashMap() {
                                  this.buckets = {};
                                  }

                                  HashMap.prototype.put = function(key, value) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  bucket = new Array();
                                  this.buckets[hashCode] = bucket;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  bucket[i].value = value;
                                  return;
                                  }
                                  }
                                  bucket.push({ key: key, value: value });
                                  }

                                  HashMap.prototype.get = function(key) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  return null;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  return bucket[i].value;
                                  }
                                  }
                                  }

                                  HashMap.prototype.keys = function() {
                                  var keys = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  keys.push(bucket[i].key);
                                  }
                                  }
                                  return keys;
                                  }

                                  HashMap.prototype.values = function() {
                                  var values = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  values.push(bucket[i].value);
                                  }
                                  }
                                  return values;
                                  }


                                  Note: key objects must "implement" hashCode() and equals() methods.






                                  share|improve this answer














                                  Unfortunately, none of the above answers were good for my case: different key objects may have the same hash code. Therefore, I wrote a simple Java-like HashMap version:



                                  function HashMap() {
                                  this.buckets = {};
                                  }

                                  HashMap.prototype.put = function(key, value) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  bucket = new Array();
                                  this.buckets[hashCode] = bucket;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  bucket[i].value = value;
                                  return;
                                  }
                                  }
                                  bucket.push({ key: key, value: value });
                                  }

                                  HashMap.prototype.get = function(key) {
                                  var hashCode = key.hashCode();
                                  var bucket = this.buckets[hashCode];
                                  if (!bucket) {
                                  return null;
                                  }
                                  for (var i = 0; i < bucket.length; ++i) {
                                  if (bucket[i].key.equals(key)) {
                                  return bucket[i].value;
                                  }
                                  }
                                  }

                                  HashMap.prototype.keys = function() {
                                  var keys = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  keys.push(bucket[i].key);
                                  }
                                  }
                                  return keys;
                                  }

                                  HashMap.prototype.values = function() {
                                  var values = new Array();
                                  for (var hashKey in this.buckets) {
                                  var bucket = this.buckets[hashKey];
                                  for (var i = 0; i < bucket.length; ++i) {
                                  values.push(bucket[i].value);
                                  }
                                  }
                                  return values;
                                  }


                                  Note: key objects must "implement" hashCode() and equals() methods.







                                  share|improve this answer














                                  share|improve this answer



                                  share|improve this answer








                                  edited Oct 25 '13 at 13:32









                                  scunliffe

                                  47.6k19104145




                                  47.6k19104145










                                  answered Apr 7 '11 at 7:25









                                  spektom

                                  28.8k24174




                                  28.8k24174








                                  • 7




                                    The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
                                    – Erik Allik
                                    Nov 27 '11 at 19:15














                                  • 7




                                    The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
                                    – Erik Allik
                                    Nov 27 '11 at 19:15








                                  7




                                  7




                                  The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
                                  – Erik Allik
                                  Nov 27 '11 at 19:15




                                  The preference of new Array() over is to ensure the absolute Java-likeness of your code? :)
                                  – Erik Allik
                                  Nov 27 '11 at 19:15











                                  5














                                  I've implemented a JavaScript HashMap which code can be obtained from http://github.com/lambder/HashMapJS/tree/master



                                  Here is the code:



                                  /*
                                  =====================================================================
                                  @license MIT
                                  @author Lambder
                                  @copyright 2009 Lambder.
                                  @end
                                  =====================================================================
                                  */
                                  var HashMap = function() {
                                  this.initialize();
                                  }

                                  HashMap.prototype = {
                                  hashkey_prefix: "<#HashMapHashkeyPerfix>",
                                  hashcode_field: "<#HashMapHashkeyPerfix>",

                                  initialize: function() {
                                  this.backing_hash = {};
                                  this.code = 0;
                                  },
                                  /*
                                  maps value to key returning previous assocciation
                                  */
                                  put: function(key, value) {
                                  var prev;
                                  if (key && value) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  prev = this.backing_hash[hashCode];
                                  } else {
                                  this.code += 1;
                                  hashCode = this.hashkey_prefix + this.code;
                                  key[this.hashcode_field] = hashCode;
                                  }
                                  this.backing_hash[hashCode] = value;
                                  }
                                  return prev;
                                  },
                                  /*
                                  returns value associated with given key
                                  */
                                  get: function(key) {
                                  var value;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  value = this.backing_hash[hashCode];
                                  }
                                  }
                                  return value;
                                  },
                                  /*
                                  deletes association by given key.
                                  Returns true if the assocciation existed, false otherwise
                                  */
                                  del: function(key) {
                                  var success = false;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  var prev = this.backing_hash[hashCode];
                                  this.backing_hash[hashCode] = undefined;
                                  if(prev !== undefined)
                                  success = true;
                                  }
                                  }
                                  return success;
                                  }
                                  }

                                  //// Usage

                                  // creation

                                  var my_map = new HashMap();

                                  // insertion

                                  var a_key = {};
                                  var a_value = {struct: "structA"};
                                  var b_key = {};
                                  var b_value = {struct: "structB"};
                                  var c_key = {};
                                  var c_value = {struct: "structC"};

                                  my_map.put(a_key, a_value);
                                  my_map.put(b_key, b_value);
                                  var prev_b = my_map.put(b_key, c_value);

                                  // retrieval

                                  if(my_map.get(a_key) !== a_value){
                                  throw("fail1")
                                  }
                                  if(my_map.get(b_key) !== c_value){
                                  throw("fail2")
                                  }
                                  if(prev_b !== b_value){
                                  throw("fail3")
                                  }

                                  // deletion

                                  var a_existed = my_map.del(a_key);
                                  var c_existed = my_map.del(c_key);
                                  var a2_existed = my_map.del(a_key);

                                  if(a_existed !== true){
                                  throw("fail4")
                                  }
                                  if(c_existed !== false){
                                  throw("fail5")
                                  }
                                  if(a2_existed !== false){
                                  throw("fail6")
                                  }





                                  share|improve this answer



















                                  • 1




                                    You code does not seem to work with putting the same object in multiple HashMaps.
                                    – Erik Allik
                                    Nov 27 '11 at 19:13


















                                  5














                                  I've implemented a JavaScript HashMap which code can be obtained from http://github.com/lambder/HashMapJS/tree/master



                                  Here is the code:



                                  /*
                                  =====================================================================
                                  @license MIT
                                  @author Lambder
                                  @copyright 2009 Lambder.
                                  @end
                                  =====================================================================
                                  */
                                  var HashMap = function() {
                                  this.initialize();
                                  }

                                  HashMap.prototype = {
                                  hashkey_prefix: "<#HashMapHashkeyPerfix>",
                                  hashcode_field: "<#HashMapHashkeyPerfix>",

                                  initialize: function() {
                                  this.backing_hash = {};
                                  this.code = 0;
                                  },
                                  /*
                                  maps value to key returning previous assocciation
                                  */
                                  put: function(key, value) {
                                  var prev;
                                  if (key && value) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  prev = this.backing_hash[hashCode];
                                  } else {
                                  this.code += 1;
                                  hashCode = this.hashkey_prefix + this.code;
                                  key[this.hashcode_field] = hashCode;
                                  }
                                  this.backing_hash[hashCode] = value;
                                  }
                                  return prev;
                                  },
                                  /*
                                  returns value associated with given key
                                  */
                                  get: function(key) {
                                  var value;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  value = this.backing_hash[hashCode];
                                  }
                                  }
                                  return value;
                                  },
                                  /*
                                  deletes association by given key.
                                  Returns true if the assocciation existed, false otherwise
                                  */
                                  del: function(key) {
                                  var success = false;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  var prev = this.backing_hash[hashCode];
                                  this.backing_hash[hashCode] = undefined;
                                  if(prev !== undefined)
                                  success = true;
                                  }
                                  }
                                  return success;
                                  }
                                  }

                                  //// Usage

                                  // creation

                                  var my_map = new HashMap();

                                  // insertion

                                  var a_key = {};
                                  var a_value = {struct: "structA"};
                                  var b_key = {};
                                  var b_value = {struct: "structB"};
                                  var c_key = {};
                                  var c_value = {struct: "structC"};

                                  my_map.put(a_key, a_value);
                                  my_map.put(b_key, b_value);
                                  var prev_b = my_map.put(b_key, c_value);

                                  // retrieval

                                  if(my_map.get(a_key) !== a_value){
                                  throw("fail1")
                                  }
                                  if(my_map.get(b_key) !== c_value){
                                  throw("fail2")
                                  }
                                  if(prev_b !== b_value){
                                  throw("fail3")
                                  }

                                  // deletion

                                  var a_existed = my_map.del(a_key);
                                  var c_existed = my_map.del(c_key);
                                  var a2_existed = my_map.del(a_key);

                                  if(a_existed !== true){
                                  throw("fail4")
                                  }
                                  if(c_existed !== false){
                                  throw("fail5")
                                  }
                                  if(a2_existed !== false){
                                  throw("fail6")
                                  }





                                  share|improve this answer



















                                  • 1




                                    You code does not seem to work with putting the same object in multiple HashMaps.
                                    – Erik Allik
                                    Nov 27 '11 at 19:13
















                                  5












                                  5








                                  5






                                  I've implemented a JavaScript HashMap which code can be obtained from http://github.com/lambder/HashMapJS/tree/master



                                  Here is the code:



                                  /*
                                  =====================================================================
                                  @license MIT
                                  @author Lambder
                                  @copyright 2009 Lambder.
                                  @end
                                  =====================================================================
                                  */
                                  var HashMap = function() {
                                  this.initialize();
                                  }

                                  HashMap.prototype = {
                                  hashkey_prefix: "<#HashMapHashkeyPerfix>",
                                  hashcode_field: "<#HashMapHashkeyPerfix>",

                                  initialize: function() {
                                  this.backing_hash = {};
                                  this.code = 0;
                                  },
                                  /*
                                  maps value to key returning previous assocciation
                                  */
                                  put: function(key, value) {
                                  var prev;
                                  if (key && value) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  prev = this.backing_hash[hashCode];
                                  } else {
                                  this.code += 1;
                                  hashCode = this.hashkey_prefix + this.code;
                                  key[this.hashcode_field] = hashCode;
                                  }
                                  this.backing_hash[hashCode] = value;
                                  }
                                  return prev;
                                  },
                                  /*
                                  returns value associated with given key
                                  */
                                  get: function(key) {
                                  var value;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  value = this.backing_hash[hashCode];
                                  }
                                  }
                                  return value;
                                  },
                                  /*
                                  deletes association by given key.
                                  Returns true if the assocciation existed, false otherwise
                                  */
                                  del: function(key) {
                                  var success = false;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  var prev = this.backing_hash[hashCode];
                                  this.backing_hash[hashCode] = undefined;
                                  if(prev !== undefined)
                                  success = true;
                                  }
                                  }
                                  return success;
                                  }
                                  }

                                  //// Usage

                                  // creation

                                  var my_map = new HashMap();

                                  // insertion

                                  var a_key = {};
                                  var a_value = {struct: "structA"};
                                  var b_key = {};
                                  var b_value = {struct: "structB"};
                                  var c_key = {};
                                  var c_value = {struct: "structC"};

                                  my_map.put(a_key, a_value);
                                  my_map.put(b_key, b_value);
                                  var prev_b = my_map.put(b_key, c_value);

                                  // retrieval

                                  if(my_map.get(a_key) !== a_value){
                                  throw("fail1")
                                  }
                                  if(my_map.get(b_key) !== c_value){
                                  throw("fail2")
                                  }
                                  if(prev_b !== b_value){
                                  throw("fail3")
                                  }

                                  // deletion

                                  var a_existed = my_map.del(a_key);
                                  var c_existed = my_map.del(c_key);
                                  var a2_existed = my_map.del(a_key);

                                  if(a_existed !== true){
                                  throw("fail4")
                                  }
                                  if(c_existed !== false){
                                  throw("fail5")
                                  }
                                  if(a2_existed !== false){
                                  throw("fail6")
                                  }





                                  share|improve this answer














                                  I've implemented a JavaScript HashMap which code can be obtained from http://github.com/lambder/HashMapJS/tree/master



                                  Here is the code:



                                  /*
                                  =====================================================================
                                  @license MIT
                                  @author Lambder
                                  @copyright 2009 Lambder.
                                  @end
                                  =====================================================================
                                  */
                                  var HashMap = function() {
                                  this.initialize();
                                  }

                                  HashMap.prototype = {
                                  hashkey_prefix: "<#HashMapHashkeyPerfix>",
                                  hashcode_field: "<#HashMapHashkeyPerfix>",

                                  initialize: function() {
                                  this.backing_hash = {};
                                  this.code = 0;
                                  },
                                  /*
                                  maps value to key returning previous assocciation
                                  */
                                  put: function(key, value) {
                                  var prev;
                                  if (key && value) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  prev = this.backing_hash[hashCode];
                                  } else {
                                  this.code += 1;
                                  hashCode = this.hashkey_prefix + this.code;
                                  key[this.hashcode_field] = hashCode;
                                  }
                                  this.backing_hash[hashCode] = value;
                                  }
                                  return prev;
                                  },
                                  /*
                                  returns value associated with given key
                                  */
                                  get: function(key) {
                                  var value;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  value = this.backing_hash[hashCode];
                                  }
                                  }
                                  return value;
                                  },
                                  /*
                                  deletes association by given key.
                                  Returns true if the assocciation existed, false otherwise
                                  */
                                  del: function(key) {
                                  var success = false;
                                  if (key) {
                                  var hashCode = key[this.hashcode_field];
                                  if (hashCode) {
                                  var prev = this.backing_hash[hashCode];
                                  this.backing_hash[hashCode] = undefined;
                                  if(prev !== undefined)
                                  success = true;
                                  }
                                  }
                                  return success;
                                  }
                                  }

                                  //// Usage

                                  // creation

                                  var my_map = new HashMap();

                                  // insertion

                                  var a_key = {};
                                  var a_value = {struct: "structA"};
                                  var b_key = {};
                                  var b_value = {struct: "structB"};
                                  var c_key = {};
                                  var c_value = {struct: "structC"};

                                  my_map.put(a_key, a_value);
                                  my_map.put(b_key, b_value);
                                  var prev_b = my_map.put(b_key, c_value);

                                  // retrieval

                                  if(my_map.get(a_key) !== a_value){
                                  throw("fail1")
                                  }
                                  if(my_map.get(b_key) !== c_value){
                                  throw("fail2")
                                  }
                                  if(prev_b !== b_value){
                                  throw("fail3")
                                  }

                                  // deletion

                                  var a_existed = my_map.del(a_key);
                                  var c_existed = my_map.del(c_key);
                                  var a2_existed = my_map.del(a_key);

                                  if(a_existed !== true){
                                  throw("fail4")
                                  }
                                  if(c_existed !== false){
                                  throw("fail5")
                                  }
                                  if(a2_existed !== false){
                                  throw("fail6")
                                  }






                                  share|improve this answer














                                  share|improve this answer



                                  share|improve this answer








                                  edited Oct 29 at 10:24

























                                  answered Jul 9 '09 at 20:20









                                  Lambder

                                  2,11511918




                                  2,11511918








                                  • 1




                                    You code does not seem to work with putting the same object in multiple HashMaps.
                                    – Erik Allik
                                    Nov 27 '11 at 19:13
















                                  • 1




                                    You code does not seem to work with putting the same object in multiple HashMaps.
                                    – Erik Allik
                                    Nov 27 '11 at 19:13










                                  1




                                  1




                                  You code does not seem to work with putting the same object in multiple HashMaps.
                                  – Erik Allik
                                  Nov 27 '11 at 19:13






                                  You code does not seem to work with putting the same object in multiple HashMaps.
                                  – Erik Allik
                                  Nov 27 '11 at 19:13













                                  4














                                  In ECMA6 you can use WeakMap



                                  Example:



                                  var wm1 = new WeakMap(),
                                  wm2 = new WeakMap(),
                                  wm3 = new WeakMap();
                                  var o1 = {},
                                  o2 = function(){},
                                  o3 = window;

                                  wm1.set(o1, 37);
                                  wm1.set(o2, "azerty");
                                  wm2.set(o1, o2); // a value can be anything, including an object or a function
                                  wm2.set(o3, undefined);
                                  wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!

                                  wm1.get(o2); // "azerty"
                                  wm2.get(o2); // undefined, because there is no value for o2 on wm2
                                  wm2.get(o3); // undefined, because that is the set value

                                  wm1.has(o2); // true
                                  wm2.has(o2); // false
                                  wm2.has(o3); // true (even if the value itself is 'undefined')

                                  wm3.set(o1, 37);
                                  wm3.get(o1); // 37
                                  wm3.clear();
                                  wm3.get(o1); // undefined, because wm3 was cleared and there is no value for o1 anymore

                                  wm1.has(o1); // true
                                  wm1.delete(o1);
                                  wm1.has(o1); // false


                                  But:



                                  Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). 





                                  share|improve this answer





















                                  • oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
                                    – Claudiu
                                    Nov 12 '13 at 16:56


















                                  4














                                  In ECMA6 you can use WeakMap



                                  Example:



                                  var wm1 = new WeakMap(),
                                  wm2 = new WeakMap(),
                                  wm3 = new WeakMap();
                                  var o1 = {},
                                  o2 = function(){},
                                  o3 = window;

                                  wm1.set(o1, 37);
                                  wm1.set(o2, "azerty");
                                  wm2.set(o1, o2); // a value can be anything, including an object or a function
                                  wm2.set(o3, undefined);
                                  wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!

                                  wm1.get(o2); // "azerty"
                                  wm2.get(o2); // undefined, because there is no value for o2 on wm2
                                  wm2.get(o3); // undefined, because that is the set value

                                  wm1.has(o2); // true
                                  wm2.has(o2); // false
                                  wm2.has(o3); // true (even if the value itself is 'undefined')

                                  wm3.set(o1, 37);
                                  wm3.get(o1); // 37
                                  wm3.clear();
                                  wm3.get(o1); // undefined, because wm3 was cleared and there is no value for o1 anymore

                                  wm1.has(o1); // true
                                  wm1.delete(o1);
                                  wm1.has(o1); // false


                                  But:



                                  Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). 





                                  share|improve this answer





















                                  • oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
                                    – Claudiu
                                    Nov 12 '13 at 16:56
















                                  4












                                  4








                                  4






                                  In ECMA6 you can use WeakMap



                                  Example:



                                  var wm1 = new WeakMap(),
                                  wm2 = new WeakMap(),
                                  wm3 = new WeakMap();
                                  var o1 = {},
                                  o2 = function(){},
                                  o3 = window;

                                  wm1.set(o1, 37);
                                  wm1.set(o2, "azerty");
                                  wm2.set(o1, o2); // a value can be anything, including an object or a function
                                  wm2.set(o3, undefined);
                                  wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!

                                  wm1.get(o2); // "azerty"
                                  wm2.get(o2); // undefined, because there is no value for o2 on wm2
                                  wm2.get(o3); // undefined, because that is the set value

                                  wm1.has(o2); // true
                                  wm2.has(o2); // false
                                  wm2.has(o3); // true (even if the value itself is 'undefined')

                                  wm3.set(o1, 37);
                                  wm3.get(o1); // 37
                                  wm3.clear();
                                  wm3.get(o1); // undefined, because wm3 was cleared and there is no value for o1 anymore

                                  wm1.has(o1); // true
                                  wm1.delete(o1);
                                  wm1.has(o1); // false


                                  But:



                                  Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). 





                                  share|improve this answer












                                  In ECMA6 you can use WeakMap



                                  Example:



                                  var wm1 = new WeakMap(),
                                  wm2 = new WeakMap(),
                                  wm3 = new WeakMap();
                                  var o1 = {},
                                  o2 = function(){},
                                  o3 = window;

                                  wm1.set(o1, 37);
                                  wm1.set(o2, "azerty");
                                  wm2.set(o1, o2); // a value can be anything, including an object or a function
                                  wm2.set(o3, undefined);
                                  wm2.set(wm1, wm2); // keys and values can be any objects. Even WeakMaps!

                                  wm1.get(o2); // "azerty"
                                  wm2.get(o2); // undefined, because there is no value for o2 on wm2
                                  wm2.get(o3); // undefined, because that is the set value

                                  wm1.has(o2); // true
                                  wm2.has(o2); // false
                                  wm2.has(o3); // true (even if the value itself is 'undefined')

                                  wm3.set(o1, 37);
                                  wm3.get(o1); // 37
                                  wm3.clear();
                                  wm3.get(o1); // undefined, because wm3 was cleared and there is no value for o1 anymore

                                  wm1.has(o1); // true
                                  wm1.delete(o1);
                                  wm1.has(o1); // false


                                  But:



                                  Because of references being weak, WeakMap keys are not enumerable (i.e. there is no method giving you a list of the keys). 






                                  share|improve this answer












                                  share|improve this answer



                                  share|improve this answer










                                  answered Nov 12 '13 at 7:33









                                  Nox73

                                  14113




                                  14113












                                  • oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
                                    – Claudiu
                                    Nov 12 '13 at 16:56




















                                  • oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
                                    – Claudiu
                                    Nov 12 '13 at 16:56


















                                  oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
                                  – Claudiu
                                  Nov 12 '13 at 16:56






                                  oh praise jesus they're finally adding weak references to javascript. it's about time... +1 for that, but this would actually be awful to use because the references are weak
                                  – Claudiu
                                  Nov 12 '13 at 16:56













                                  2














                                  Javascript does not build-in Map/hashmap. It should be called associative array.



                                  hash["X"] is equals to hash.X, but allow "X" as a string variable.
                                  In other words, hash[x] is functionally equals to eval("hash."+x.toString())



                                  It is more similar as object.properties rather then key-value mapping.
                                  If you are looking for a better Key/value mapping in Javascript, please use Map object which you can find on the web.






                                  share|improve this answer


























                                    2














                                    Javascript does not build-in Map/hashmap. It should be called associative array.



                                    hash["X"] is equals to hash.X, but allow "X" as a string variable.
                                    In other words, hash[x] is functionally equals to eval("hash."+x.toString())



                                    It is more similar as object.properties rather then key-value mapping.
                                    If you are looking for a better Key/value mapping in Javascript, please use Map object which you can find on the web.






                                    share|improve this answer
























                                      2












                                      2








                                      2






                                      Javascript does not build-in Map/hashmap. It should be called associative array.



                                      hash["X"] is equals to hash.X, but allow "X" as a string variable.
                                      In other words, hash[x] is functionally equals to eval("hash."+x.toString())



                                      It is more similar as object.properties rather then key-value mapping.
                                      If you are looking for a better Key/value mapping in Javascript, please use Map object which you can find on the web.






                                      share|improve this answer












                                      Javascript does not build-in Map/hashmap. It should be called associative array.



                                      hash["X"] is equals to hash.X, but allow "X" as a string variable.
                                      In other words, hash[x] is functionally equals to eval("hash."+x.toString())



                                      It is more similar as object.properties rather then key-value mapping.
                                      If you are looking for a better Key/value mapping in Javascript, please use Map object which you can find on the web.







                                      share|improve this answer












                                      share|improve this answer



                                      share|improve this answer










                                      answered Dec 21 '08 at 15:07









                                      Dennis C

                                      19.6k126392




                                      19.6k126392























                                          2














                                          Try my JavaScript hash table implementation: http://www.timdown.co.uk/jshashtable



                                          It looks for a hashCode() method of key objects, or you can supply a hashing function when creating a Hashtable object.






                                          share|improve this answer


























                                            2














                                            Try my JavaScript hash table implementation: http://www.timdown.co.uk/jshashtable



                                            It looks for a hashCode() method of key objects, or you can supply a hashing function when creating a Hashtable object.






                                            share|improve this answer
























                                              2












                                              2








                                              2






                                              Try my JavaScript hash table implementation: http://www.timdown.co.uk/jshashtable



                                              It looks for a hashCode() method of key objects, or you can supply a hashing function when creating a Hashtable object.






                                              share|improve this answer












                                              Try my JavaScript hash table implementation: http://www.timdown.co.uk/jshashtable



                                              It looks for a hashCode() method of key objects, or you can supply a hashing function when creating a Hashtable object.







                                              share|improve this answer












                                              share|improve this answer



                                              share|improve this answer










                                              answered May 27 '09 at 10:40









                                              Tim Down

                                              248k56373460




                                              248k56373460























                                                  2














                                                  This looks like a pretty robust solution: https://github.com/flesler/hashmap . It will even work well for functions and objects that look identical. The only hack it uses is adding an obscure member to an object to identify it. If your program doesn't overwrite that obscure variable (its something like hashid), you're golden.






                                                  share|improve this answer


























                                                    2














                                                    This looks like a pretty robust solution: https://github.com/flesler/hashmap . It will even work well for functions and objects that look identical. The only hack it uses is adding an obscure member to an object to identify it. If your program doesn't overwrite that obscure variable (its something like hashid), you're golden.






                                                    share|improve this answer
























                                                      2












                                                      2








                                                      2






                                                      This looks like a pretty robust solution: https://github.com/flesler/hashmap . It will even work well for functions and objects that look identical. The only hack it uses is adding an obscure member to an object to identify it. If your program doesn't overwrite that obscure variable (its something like hashid), you're golden.






                                                      share|improve this answer












                                                      This looks like a pretty robust solution: https://github.com/flesler/hashmap . It will even work well for functions and objects that look identical. The only hack it uses is adding an obscure member to an object to identify it. If your program doesn't overwrite that obscure variable (its something like hashid), you're golden.







                                                      share|improve this answer












                                                      share|improve this answer



                                                      share|improve this answer










                                                      answered Aug 26 '13 at 5:16









                                                      B T

                                                      27.2k25132161




                                                      27.2k25132161























                                                          2














                                                          If performance is not critical (e.g. the amount of keys is relatively small) and you don't want to pollute your (or maybe not your) objects with additional fields like _hash, _id, etc., then you can make use of the fact that Array.prototype.indexOf employs strict equality. Here is a simple implementation:



                                                          var Dict = (function(){
                                                          // IE 8 and earlier has no Array.prototype.indexOf
                                                          function indexOfPolyfill(val) {
                                                          for (var i = 0, l = this.length; i < l; ++i) {
                                                          if (this[i] === val) {
                                                          return i;
                                                          }
                                                          }
                                                          return -1;
                                                          }

                                                          function Dict(){
                                                          this.keys = ;
                                                          this.values = ;
                                                          if (!this.keys.indexOf) {
                                                          this.keys.indexOf = indexOfPolyfill;
                                                          }
                                                          };

                                                          Dict.prototype.has = function(key){
                                                          return this.keys.indexOf(key) != -1;
                                                          };

                                                          Dict.prototype.get = function(key, defaultValue){
                                                          var index = this.keys.indexOf(key);
                                                          return index == -1 ? defaultValue : this.values[index];
                                                          };

                                                          Dict.prototype.set = function(key, value){
                                                          var index = this.keys.indexOf(key);
                                                          if (index == -1) {
                                                          this.keys.push(key);
                                                          this.values.push(value);
                                                          } else {
                                                          var prevValue = this.values[index];
                                                          this.values[index] = value;
                                                          return prevValue;
                                                          }
                                                          };

                                                          Dict.prototype.delete = function(key){
                                                          var index = this.keys.indexOf(key);
                                                          if (index != -1) {
                                                          this.keys.splice(index, 1);
                                                          return this.values.splice(index, 1)[0];
                                                          }
                                                          };

                                                          Dict.prototype.clear = function(){
                                                          this.keys.splice(0, this.keys.length);
                                                          this.values.splice(0, this.values.length);
                                                          };

                                                          return Dict;
                                                          })();


                                                          Example of usage:



                                                          var a = {}, b = {},
                                                          c = { toString: function(){ return '1'; } },
                                                          d = 1, s = '1', u = undefined, n = null,
                                                          dict = new Dict();

                                                          // keys and values can be anything
                                                          dict.set(a, 'a');
                                                          dict.set(b, 'b');
                                                          dict.set(c, 'c');
                                                          dict.set(d, 'd');
                                                          dict.set(s, 's');
                                                          dict.set(u, 'u');
                                                          dict.set(n, 'n');

                                                          dict.get(a); // 'a'
                                                          dict.get(b); // 'b'
                                                          dict.get(s); // 's'
                                                          dict.get(u); // 'u'
                                                          dict.get(n); // 'n'
                                                          // etc.


                                                          Comparing to ES6 WeakMap it has two issues: O(n) search time and non-weakness (i.e. it will cause memory leak if you don't use delete or clear to release keys).






                                                          share|improve this answer




























                                                            2














                                                            If performance is not critical (e.g. the amount of keys is relatively small) and you don't want to pollute your (or maybe not your) objects with additional fields like _hash, _id, etc., then you can make use of the fact that Array.prototype.indexOf employs strict equality. Here is a simple implementation:



                                                            var Dict = (function(){
                                                            // IE 8 and earlier has no Array.prototype.indexOf
                                                            function indexOfPolyfill(val) {
                                                            for (var i = 0, l = this.length; i < l; ++i) {
                                                            if (this[i] === val) {
                                                            return i;
                                                            }
                                                            }
                                                            return -1;
                                                            }

                                                            function Dict(){
                                                            this.keys = ;
                                                            this.values = ;
                                                            if (!this.keys.indexOf) {
                                                            this.keys.indexOf = indexOfPolyfill;
                                                            }
                                                            };

                                                            Dict.prototype.has = function(key){
                                                            return this.keys.indexOf(key) != -1;
                                                            };

                                                            Dict.prototype.get = function(key, defaultValue){
                                                            var index = this.keys.indexOf(key);
                                                            return index == -1 ? defaultValue : this.values[index];
                                                            };

                                                            Dict.prototype.set = function(key, value){
                                                            var index = this.keys.indexOf(key);
                                                            if (index == -1) {
                                                            this.keys.push(key);
                                                            this.values.push(value);
                                                            } else {
                                                            var prevValue = this.values[index];
                                                            this.values[index] = value;
                                                            return prevValue;
                                                            }
                                                            };

                                                            Dict.prototype.delete = function(key){
                                                            var index = this.keys.indexOf(key);
                                                            if (index != -1) {
                                                            this.keys.splice(index, 1);
                                                            return this.values.splice(index, 1)[0];
                                                            }
                                                            };

                                                            Dict.prototype.clear = function(){
                                                            this.keys.splice(0, this.keys.length);
                                                            this.values.splice(0, this.values.length);
                                                            };

                                                            return Dict;
                                                            })();


                                                            Example of usage:



                                                            var a = {}, b = {},
                                                            c = { toString: function(){ return '1'; } },
                                                            d = 1, s = '1', u = undefined, n = null,
                                                            dict = new Dict();

                                                            // keys and values can be anything
                                                            dict.set(a, 'a');
                                                            dict.set(b, 'b');
                                                            dict.set(c, 'c');
                                                            dict.set(d, 'd');
                                                            dict.set(s, 's');
                                                            dict.set(u, 'u');
                                                            dict.set(n, 'n');

                                                            dict.get(a); // 'a'
                                                            dict.get(b); // 'b'
                                                            dict.get(s); // 's'
                                                            dict.get(u); // 'u'
                                                            dict.get(n); // 'n'
                                                            // etc.


                                                            Comparing to ES6 WeakMap it has two issues: O(n) search time and non-weakness (i.e. it will cause memory leak if you don't use delete or clear to release keys).






                                                            share|improve this answer


























                                                              2












                                                              2








                                                              2






                                                              If performance is not critical (e.g. the amount of keys is relatively small) and you don't want to pollute your (or maybe not your) objects with additional fields like _hash, _id, etc., then you can make use of the fact that Array.prototype.indexOf employs strict equality. Here is a simple implementation:



                                                              var Dict = (function(){
                                                              // IE 8 and earlier has no Array.prototype.indexOf
                                                              function indexOfPolyfill(val) {
                                                              for (var i = 0, l = this.length; i < l; ++i) {
                                                              if (this[i] === val) {
                                                              return i;
                                                              }
                                                              }
                                                              return -1;
                                                              }

                                                              function Dict(){
                                                              this.keys = ;
                                                              this.values = ;
                                                              if (!this.keys.indexOf) {
                                                              this.keys.indexOf = indexOfPolyfill;
                                                              }
                                                              };

                                                              Dict.prototype.has = function(key){
                                                              return this.keys.indexOf(key) != -1;
                                                              };

                                                              Dict.prototype.get = function(key, defaultValue){
                                                              var index = this.keys.indexOf(key);
                                                              return index == -1 ? defaultValue : this.values[index];
                                                              };

                                                              Dict.prototype.set = function(key, value){
                                                              var index = this.keys.indexOf(key);
                                                              if (index == -1) {
                                                              this.keys.push(key);
                                                              this.values.push(value);
                                                              } else {
                                                              var prevValue = this.values[index];
                                                              this.values[index] = value;
                                                              return prevValue;
                                                              }
                                                              };

                                                              Dict.prototype.delete = function(key){
                                                              var index = this.keys.indexOf(key);
                                                              if (index != -1) {
                                                              this.keys.splice(index, 1);
                                                              return this.values.splice(index, 1)[0];
                                                              }
                                                              };

                                                              Dict.prototype.clear = function(){
                                                              this.keys.splice(0, this.keys.length);
                                                              this.values.splice(0, this.values.length);
                                                              };

                                                              return Dict;
                                                              })();


                                                              Example of usage:



                                                              var a = {}, b = {},
                                                              c = { toString: function(){ return '1'; } },
                                                              d = 1, s = '1', u = undefined, n = null,
                                                              dict = new Dict();

                                                              // keys and values can be anything
                                                              dict.set(a, 'a');
                                                              dict.set(b, 'b');
                                                              dict.set(c, 'c');
                                                              dict.set(d, 'd');
                                                              dict.set(s, 's');
                                                              dict.set(u, 'u');
                                                              dict.set(n, 'n');

                                                              dict.get(a); // 'a'
                                                              dict.get(b); // 'b'
                                                              dict.get(s); // 's'
                                                              dict.get(u); // 'u'
                                                              dict.get(n); // 'n'
                                                              // etc.


                                                              Comparing to ES6 WeakMap it has two issues: O(n) search time and non-weakness (i.e. it will cause memory leak if you don't use delete or clear to release keys).






                                                              share|improve this answer














                                                              If performance is not critical (e.g. the amount of keys is relatively small) and you don't want to pollute your (or maybe not your) objects with additional fields like _hash, _id, etc., then you can make use of the fact that Array.prototype.indexOf employs strict equality. Here is a simple implementation:



                                                              var Dict = (function(){
                                                              // IE 8 and earlier has no Array.prototype.indexOf
                                                              function indexOfPolyfill(val) {
                                                              for (var i = 0, l = this.length; i < l; ++i) {
                                                              if (this[i] === val) {
                                                              return i;
                                                              }
                                                              }
                                                              return -1;
                                                              }

                                                              function Dict(){
                                                              this.keys = ;
                                                              this.values = ;
                                                              if (!this.keys.indexOf) {
                                                              this.keys.indexOf = indexOfPolyfill;
                                                              }
                                                              };

                                                              Dict.prototype.has = function(key){
                                                              return this.keys.indexOf(key) != -1;
                                                              };

                                                              Dict.prototype.get = function(key, defaultValue){
                                                              var index = this.keys.indexOf(key);
                                                              return index == -1 ? defaultValue : this.values[index];
                                                              };

                                                              Dict.prototype.set = function(key, value){
                                                              var index = this.keys.indexOf(key);
                                                              if (index == -1) {
                                                              this.keys.push(key);
                                                              this.values.push(value);
                                                              } else {
                                                              var prevValue = this.values[index];
                                                              this.values[index] = value;
                                                              return prevValue;
                                                              }
                                                              };

                                                              Dict.prototype.delete = function(key){
                                                              var index = this.keys.indexOf(key);
                                                              if (index != -1) {
                                                              this.keys.splice(index, 1);
                                                              return this.values.splice(index, 1)[0];
                                                              }
                                                              };

                                                              Dict.prototype.clear = function(){
                                                              this.keys.splice(0, this.keys.length);
                                                              this.values.splice(0, this.values.length);
                                                              };

                                                              return Dict;
                                                              })();


                                                              Example of usage:



                                                              var a = {}, b = {},
                                                              c = { toString: function(){ return '1'; } },
                                                              d = 1, s = '1', u = undefined, n = null,
                                                              dict = new Dict();

                                                              // keys and values can be anything
                                                              dict.set(a, 'a');
                                                              dict.set(b, 'b');
                                                              dict.set(c, 'c');
                                                              dict.set(d, 'd');
                                                              dict.set(s, 's');
                                                              dict.set(u, 'u');
                                                              dict.set(n, 'n');

                                                              dict.get(a); // 'a'
                                                              dict.get(b); // 'b'
                                                              dict.get(s); // 's'
                                                              dict.get(u); // 'u'
                                                              dict.get(n); // 'n'
                                                              // etc.


                                                              Comparing to ES6 WeakMap it has two issues: O(n) search time and non-weakness (i.e. it will cause memory leak if you don't use delete or clear to release keys).







                                                              share|improve this answer














                                                              share|improve this answer



                                                              share|improve this answer








                                                              edited Nov 20 '13 at 23:51

























                                                              answered Nov 20 '13 at 23:11









                                                              skozin

                                                              2,09611522




                                                              2,09611522























                                                                  2














                                                                  My Map Implementation, derived from Christoph's example:



                                                                  Example Usage:



                                                                  var map = new Map();  //creates an "in-memory" map
                                                                  var map = new Map("storageId"); //creates a map that is loaded/persisted using html5 storage




                                                                  function Map(storageId) {
                                                                  this.current = undefined;
                                                                  this.size = 0;
                                                                  this.storageId = storageId;
                                                                  if (this.storageId) {
                                                                  this.keys = new Array();
                                                                  this.disableLinking();
                                                                  }
                                                                  }

                                                                  Map.noop = function() {
                                                                  return this;
                                                                  };

                                                                  Map.illegal = function() {
                                                                  throw new Error("illegal operation for maps without linking");
                                                                  };

                                                                  // map initialisation from existing object
                                                                  // doesn't add inherited properties if not explicitly instructed to:
                                                                  // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                                                                  // --> inherited properties won't be added
                                                                  Map.from = function(obj, foreignKeys) {
                                                                  var map = new Map;
                                                                  for(var prop in obj) {
                                                                  if(foreignKeys || obj.hasOwnProperty(prop))
                                                                  map.put(prop, obj[prop]);
                                                                  }
                                                                  return map;
                                                                  };

                                                                  Map.prototype.disableLinking = function() {
                                                                  this.link = Map.noop;
                                                                  this.unlink = Map.noop;
                                                                  this.disableLinking = Map.noop;

                                                                  this.next = Map.illegal;
                                                                  this.key = Map.illegal;
                                                                  this.value = Map.illegal;
                                                                  // this.removeAll = Map.illegal;


                                                                  return this;
                                                                  };

                                                                  // overwrite in Map instance if necessary
                                                                  Map.prototype.hash = function(value) {
                                                                  return (typeof value) + ' ' + (value instanceof Object ?
                                                                  (value.__hash || (value.__hash = ++arguments.callee.current)) :
                                                                  value.toString());
                                                                  };

                                                                  Map.prototype.hash.current = 0;

                                                                  // --- mapping functions

                                                                  Map.prototype.get = function(key) {
                                                                  var item = this[this.hash(key)];
                                                                  if (item === undefined) {
                                                                  if (this.storageId) {
                                                                  try {
                                                                  var itemStr = localStorage.getItem(this.storageId + key);
                                                                  if (itemStr && itemStr !== 'undefined') {
                                                                  item = JSON.parse(itemStr);
                                                                  this[this.hash(key)] = item;
                                                                  this.keys.push(key);
                                                                  ++this.size;
                                                                  }
                                                                  } catch (e) {
                                                                  console.log(e);
                                                                  }
                                                                  }
                                                                  }
                                                                  return item === undefined ? undefined : item.value;
                                                                  };

                                                                  Map.prototype.put = function(key, value) {
                                                                  var hash = this.hash(key);

                                                                  if(this[hash] === undefined) {
                                                                  var item = { key : key, value : value };
                                                                  this[hash] = item;

                                                                  this.link(item);
                                                                  ++this.size;
                                                                  }
                                                                  else this[hash].value = value;
                                                                  if (this.storageId) {
                                                                  this.keys.push(key);
                                                                  try {
                                                                  localStorage.setItem(this.storageId + key, JSON.stringify(this[hash]));
                                                                  } catch (e) {
                                                                  console.log(e);
                                                                  }
                                                                  }
                                                                  return this;
                                                                  };

                                                                  Map.prototype.remove = function(key) {
                                                                  var hash = this.hash(key);
                                                                  var item = this[hash];
                                                                  if(item !== undefined) {
                                                                  --this.size;
                                                                  this.unlink(item);

                                                                  delete this[hash];
                                                                  }
                                                                  if (this.storageId) {
                                                                  try {
                                                                  localStorage.setItem(this.storageId + key, undefined);
                                                                  } catch (e) {
                                                                  console.log(e);
                                                                  }
                                                                  }
                                                                  return this;
                                                                  };

                                                                  // only works if linked
                                                                  Map.prototype.removeAll = function() {
                                                                  if (this.storageId) {
                                                                  for (var i=0; i<this.keys.length; i++) {
                                                                  this.remove(this.keys[i]);
                                                                  }
                                                                  this.keys.length = 0;
                                                                  } else {
                                                                  while(this.size)
                                                                  this.remove(this.key());
                                                                  }
                                                                  return this;
                                                                  };

                                                                  // --- linked list helper functions

                                                                  Map.prototype.link = function(item) {
                                                                  if (this.storageId) {
                                                                  return;
                                                                  }
                                                                  if(this.size == 0) {
                                                                  item.prev = item;
                                                                  item.next = item;
                                                                  this.current = item;
                                                                  }
                                                                  else {
                                                                  item.prev = this.current.prev;
                                                                  item.prev.next = item;
                                                                  item.next = this.current;
                                                                  this.current.prev = item;
                                                                  }
                                                                  };

                                                                  Map.prototype.unlink = function(item) {
                                                                  if (this.storageId) {
                                                                  return;
                                                                  }
                                                                  if(this.size == 0)
                                                                  this.current = undefined;
                                                                  else {
                                                                  item.prev.next = item.next;
                                                                  item.next.prev = item.prev;
                                                                  if(item === this.current)
                                                                  this.current = item.next;
                                                                  }
                                                                  };

                                                                  // --- iterator functions - only work if map is linked

                                                                  Map.prototype.next = function() {
                                                                  this.current = this.current.next;
                                                                  };

                                                                  Map.prototype.key = function() {
                                                                  if (this.storageId) {
                                                                  return undefined;
                                                                  } else {
                                                                  return this.current.key;
                                                                  }
                                                                  };

                                                                  Map.prototype.value = function() {
                                                                  if (this.storageId) {
                                                                  return undefined;
                                                                  }
                                                                  return this.current.value;
                                                                  };





                                                                  share|improve this answer




























                                                                    2














                                                                    My Map Implementation, derived from Christoph's example:



                                                                    Example Usage:



                                                                    var map = new Map();  //creates an "in-memory" map
                                                                    var map = new Map("storageId"); //creates a map that is loaded/persisted using html5 storage




                                                                    function Map(storageId) {
                                                                    this.current = undefined;
                                                                    this.size = 0;
                                                                    this.storageId = storageId;
                                                                    if (this.storageId) {
                                                                    this.keys = new Array();
                                                                    this.disableLinking();
                                                                    }
                                                                    }

                                                                    Map.noop = function() {
                                                                    return this;
                                                                    };

                                                                    Map.illegal = function() {
                                                                    throw new Error("illegal operation for maps without linking");
                                                                    };

                                                                    // map initialisation from existing object
                                                                    // doesn't add inherited properties if not explicitly instructed to:
                                                                    // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                                                                    // --> inherited properties won't be added
                                                                    Map.from = function(obj, foreignKeys) {
                                                                    var map = new Map;
                                                                    for(var prop in obj) {
                                                                    if(foreignKeys || obj.hasOwnProperty(prop))
                                                                    map.put(prop, obj[prop]);
                                                                    }
                                                                    return map;
                                                                    };

                                                                    Map.prototype.disableLinking = function() {
                                                                    this.link = Map.noop;
                                                                    this.unlink = Map.noop;
                                                                    this.disableLinking = Map.noop;

                                                                    this.next = Map.illegal;
                                                                    this.key = Map.illegal;
                                                                    this.value = Map.illegal;
                                                                    // this.removeAll = Map.illegal;


                                                                    return this;
                                                                    };

                                                                    // overwrite in Map instance if necessary
                                                                    Map.prototype.hash = function(value) {
                                                                    return (typeof value) + ' ' + (value instanceof Object ?
                                                                    (value.__hash || (value.__hash = ++arguments.callee.current)) :
                                                                    value.toString());
                                                                    };

                                                                    Map.prototype.hash.current = 0;

                                                                    // --- mapping functions

                                                                    Map.prototype.get = function(key) {
                                                                    var item = this[this.hash(key)];
                                                                    if (item === undefined) {
                                                                    if (this.storageId) {
                                                                    try {
                                                                    var itemStr = localStorage.getItem(this.storageId + key);
                                                                    if (itemStr && itemStr !== 'undefined') {
                                                                    item = JSON.parse(itemStr);
                                                                    this[this.hash(key)] = item;
                                                                    this.keys.push(key);
                                                                    ++this.size;
                                                                    }
                                                                    } catch (e) {
                                                                    console.log(e);
                                                                    }
                                                                    }
                                                                    }
                                                                    return item === undefined ? undefined : item.value;
                                                                    };

                                                                    Map.prototype.put = function(key, value) {
                                                                    var hash = this.hash(key);

                                                                    if(this[hash] === undefined) {
                                                                    var item = { key : key, value : value };
                                                                    this[hash] = item;

                                                                    this.link(item);
                                                                    ++this.size;
                                                                    }
                                                                    else this[hash].value = value;
                                                                    if (this.storageId) {
                                                                    this.keys.push(key);
                                                                    try {
                                                                    localStorage.setItem(this.storageId + key, JSON.stringify(this[hash]));
                                                                    } catch (e) {
                                                                    console.log(e);
                                                                    }
                                                                    }
                                                                    return this;
                                                                    };

                                                                    Map.prototype.remove = function(key) {
                                                                    var hash = this.hash(key);
                                                                    var item = this[hash];
                                                                    if(item !== undefined) {
                                                                    --this.size;
                                                                    this.unlink(item);

                                                                    delete this[hash];
                                                                    }
                                                                    if (this.storageId) {
                                                                    try {
                                                                    localStorage.setItem(this.storageId + key, undefined);
                                                                    } catch (e) {
                                                                    console.log(e);
                                                                    }
                                                                    }
                                                                    return this;
                                                                    };

                                                                    // only works if linked
                                                                    Map.prototype.removeAll = function() {
                                                                    if (this.storageId) {
                                                                    for (var i=0; i<this.keys.length; i++) {
                                                                    this.remove(this.keys[i]);
                                                                    }
                                                                    this.keys.length = 0;
                                                                    } else {
                                                                    while(this.size)
                                                                    this.remove(this.key());
                                                                    }
                                                                    return this;
                                                                    };

                                                                    // --- linked list helper functions

                                                                    Map.prototype.link = function(item) {
                                                                    if (this.storageId) {
                                                                    return;
                                                                    }
                                                                    if(this.size == 0) {
                                                                    item.prev = item;
                                                                    item.next = item;
                                                                    this.current = item;
                                                                    }
                                                                    else {
                                                                    item.prev = this.current.prev;
                                                                    item.prev.next = item;
                                                                    item.next = this.current;
                                                                    this.current.prev = item;
                                                                    }
                                                                    };

                                                                    Map.prototype.unlink = function(item) {
                                                                    if (this.storageId) {
                                                                    return;
                                                                    }
                                                                    if(this.size == 0)
                                                                    this.current = undefined;
                                                                    else {
                                                                    item.prev.next = item.next;
                                                                    item.next.prev = item.prev;
                                                                    if(item === this.current)
                                                                    this.current = item.next;
                                                                    }
                                                                    };

                                                                    // --- iterator functions - only work if map is linked

                                                                    Map.prototype.next = function() {
                                                                    this.current = this.current.next;
                                                                    };

                                                                    Map.prototype.key = function() {
                                                                    if (this.storageId) {
                                                                    return undefined;
                                                                    } else {
                                                                    return this.current.key;
                                                                    }
                                                                    };

                                                                    Map.prototype.value = function() {
                                                                    if (this.storageId) {
                                                                    return undefined;
                                                                    }
                                                                    return this.current.value;
                                                                    };





                                                                    share|improve this answer


























                                                                      2












                                                                      2








                                                                      2






                                                                      My Map Implementation, derived from Christoph's example:



                                                                      Example Usage:



                                                                      var map = new Map();  //creates an "in-memory" map
                                                                      var map = new Map("storageId"); //creates a map that is loaded/persisted using html5 storage




                                                                      function Map(storageId) {
                                                                      this.current = undefined;
                                                                      this.size = 0;
                                                                      this.storageId = storageId;
                                                                      if (this.storageId) {
                                                                      this.keys = new Array();
                                                                      this.disableLinking();
                                                                      }
                                                                      }

                                                                      Map.noop = function() {
                                                                      return this;
                                                                      };

                                                                      Map.illegal = function() {
                                                                      throw new Error("illegal operation for maps without linking");
                                                                      };

                                                                      // map initialisation from existing object
                                                                      // doesn't add inherited properties if not explicitly instructed to:
                                                                      // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                                                                      // --> inherited properties won't be added
                                                                      Map.from = function(obj, foreignKeys) {
                                                                      var map = new Map;
                                                                      for(var prop in obj) {
                                                                      if(foreignKeys || obj.hasOwnProperty(prop))
                                                                      map.put(prop, obj[prop]);
                                                                      }
                                                                      return map;
                                                                      };

                                                                      Map.prototype.disableLinking = function() {
                                                                      this.link = Map.noop;
                                                                      this.unlink = Map.noop;
                                                                      this.disableLinking = Map.noop;

                                                                      this.next = Map.illegal;
                                                                      this.key = Map.illegal;
                                                                      this.value = Map.illegal;
                                                                      // this.removeAll = Map.illegal;


                                                                      return this;
                                                                      };

                                                                      // overwrite in Map instance if necessary
                                                                      Map.prototype.hash = function(value) {
                                                                      return (typeof value) + ' ' + (value instanceof Object ?
                                                                      (value.__hash || (value.__hash = ++arguments.callee.current)) :
                                                                      value.toString());
                                                                      };

                                                                      Map.prototype.hash.current = 0;

                                                                      // --- mapping functions

                                                                      Map.prototype.get = function(key) {
                                                                      var item = this[this.hash(key)];
                                                                      if (item === undefined) {
                                                                      if (this.storageId) {
                                                                      try {
                                                                      var itemStr = localStorage.getItem(this.storageId + key);
                                                                      if (itemStr && itemStr !== 'undefined') {
                                                                      item = JSON.parse(itemStr);
                                                                      this[this.hash(key)] = item;
                                                                      this.keys.push(key);
                                                                      ++this.size;
                                                                      }
                                                                      } catch (e) {
                                                                      console.log(e);
                                                                      }
                                                                      }
                                                                      }
                                                                      return item === undefined ? undefined : item.value;
                                                                      };

                                                                      Map.prototype.put = function(key, value) {
                                                                      var hash = this.hash(key);

                                                                      if(this[hash] === undefined) {
                                                                      var item = { key : key, value : value };
                                                                      this[hash] = item;

                                                                      this.link(item);
                                                                      ++this.size;
                                                                      }
                                                                      else this[hash].value = value;
                                                                      if (this.storageId) {
                                                                      this.keys.push(key);
                                                                      try {
                                                                      localStorage.setItem(this.storageId + key, JSON.stringify(this[hash]));
                                                                      } catch (e) {
                                                                      console.log(e);
                                                                      }
                                                                      }
                                                                      return this;
                                                                      };

                                                                      Map.prototype.remove = function(key) {
                                                                      var hash = this.hash(key);
                                                                      var item = this[hash];
                                                                      if(item !== undefined) {
                                                                      --this.size;
                                                                      this.unlink(item);

                                                                      delete this[hash];
                                                                      }
                                                                      if (this.storageId) {
                                                                      try {
                                                                      localStorage.setItem(this.storageId + key, undefined);
                                                                      } catch (e) {
                                                                      console.log(e);
                                                                      }
                                                                      }
                                                                      return this;
                                                                      };

                                                                      // only works if linked
                                                                      Map.prototype.removeAll = function() {
                                                                      if (this.storageId) {
                                                                      for (var i=0; i<this.keys.length; i++) {
                                                                      this.remove(this.keys[i]);
                                                                      }
                                                                      this.keys.length = 0;
                                                                      } else {
                                                                      while(this.size)
                                                                      this.remove(this.key());
                                                                      }
                                                                      return this;
                                                                      };

                                                                      // --- linked list helper functions

                                                                      Map.prototype.link = function(item) {
                                                                      if (this.storageId) {
                                                                      return;
                                                                      }
                                                                      if(this.size == 0) {
                                                                      item.prev = item;
                                                                      item.next = item;
                                                                      this.current = item;
                                                                      }
                                                                      else {
                                                                      item.prev = this.current.prev;
                                                                      item.prev.next = item;
                                                                      item.next = this.current;
                                                                      this.current.prev = item;
                                                                      }
                                                                      };

                                                                      Map.prototype.unlink = function(item) {
                                                                      if (this.storageId) {
                                                                      return;
                                                                      }
                                                                      if(this.size == 0)
                                                                      this.current = undefined;
                                                                      else {
                                                                      item.prev.next = item.next;
                                                                      item.next.prev = item.prev;
                                                                      if(item === this.current)
                                                                      this.current = item.next;
                                                                      }
                                                                      };

                                                                      // --- iterator functions - only work if map is linked

                                                                      Map.prototype.next = function() {
                                                                      this.current = this.current.next;
                                                                      };

                                                                      Map.prototype.key = function() {
                                                                      if (this.storageId) {
                                                                      return undefined;
                                                                      } else {
                                                                      return this.current.key;
                                                                      }
                                                                      };

                                                                      Map.prototype.value = function() {
                                                                      if (this.storageId) {
                                                                      return undefined;
                                                                      }
                                                                      return this.current.value;
                                                                      };





                                                                      share|improve this answer














                                                                      My Map Implementation, derived from Christoph's example:



                                                                      Example Usage:



                                                                      var map = new Map();  //creates an "in-memory" map
                                                                      var map = new Map("storageId"); //creates a map that is loaded/persisted using html5 storage




                                                                      function Map(storageId) {
                                                                      this.current = undefined;
                                                                      this.size = 0;
                                                                      this.storageId = storageId;
                                                                      if (this.storageId) {
                                                                      this.keys = new Array();
                                                                      this.disableLinking();
                                                                      }
                                                                      }

                                                                      Map.noop = function() {
                                                                      return this;
                                                                      };

                                                                      Map.illegal = function() {
                                                                      throw new Error("illegal operation for maps without linking");
                                                                      };

                                                                      // map initialisation from existing object
                                                                      // doesn't add inherited properties if not explicitly instructed to:
                                                                      // omitting foreignKeys means foreignKeys === undefined, i.e. == false
                                                                      // --> inherited properties won't be added
                                                                      Map.from = function(obj, foreignKeys) {
                                                                      var map = new Map;
                                                                      for(var prop in obj) {
                                                                      if(foreignKeys || obj.hasOwnProperty(prop))
                                                                      map.put(prop, obj[prop]);
                                                                      }
                                                                      return map;
                                                                      };

                                                                      Map.prototype.disableLinking = function() {
                                                                      this.link = Map.noop;
                                                                      this.unlink = Map.noop;
                                                                      this.disableLinking = Map.noop;

                                                                      this.next = Map.illegal;
                                                                      this.key = Map.illegal;
                                                                      this.value = Map.illegal;
                                                                      // this.removeAll = Map.illegal;


                                                                      return this;
                                                                      };

                                                                      // overwrite in Map instance if necessary
                                                                      Map.prototype.hash = function(value) {
                                                                      return (typeof value) + ' ' + (value instanceof Object ?
                                                                      (value.__hash || (value.__hash = ++arguments.callee.current)) :
                                                                      value.toString());
                                                                      };

                                                                      Map.prototype.hash.current = 0;

                                                                      // --- mapping functions

                                                                      Map.prototype.get = function(key) {
                                                                      var item = this[this.hash(key)];
                                                                      if (item === undefined) {
                                                                      if (this.storageId) {
                                                                      try {
                                                                      var itemStr = localStorage.getItem(this.storageId + key);
                                                                      if (itemStr && itemStr !== 'undefined') {
                                                                      item = JSON.parse(itemStr);
                                                                      this[this.hash(key)] = item;
                                                                      this.keys.push(key);
                                                                      ++this.size;
                                                                      }
                                                                      } catch (e) {
                                                                      console.log(e);
                                                                      }
                                                                      }
                                                                      }
                                                                      return item === undefined ? undefined : item.value;
                                                                      };

                                                                      Map.prototype.put = function(key, value) {
                                                                      var hash = this.hash(key);

                                                                      if(this[hash] === undefined) {
                                                                      var item = { key : key, value : value };
                                                                      this[hash] = item;

                                                                      this.link(item);
                                                                      ++this.size;
                                                                      }
                                                                      else this[hash].value = value;
                                                                      if (this.storageId) {
                                                                      this.keys.push(key);
                                                                      try {
                                                                      localStorage.setItem(this.storageId + key, JSON.stringify(this[hash]));
                                                                      } catch (e) {
                                                                      console.log(e);
                                                                      }
                                                                      }
                                                                      return this;
                                                                      };

                                                                      Map.prototype.remove = function(key) {
                                                                      var hash = this.hash(key);
                                                                      var item = this[hash];
                                                                      if(item !== undefined) {
                                                                      --this.size;
                                                                      this.unlink(item);

                                                                      delete this[hash];
                                                                      }
                                                                      if (this.storageId) {
                                                                      try {
                                                                      localStorage.setItem(this.storageId + key, undefined);
                                                                      } catch (e) {
                                                                      console.log(e);
                                                                      }
                                                                      }
                                                                      return this;
                                                                      };

                                                                      // only works if linked
                                                                      Map.prototype.removeAll = function() {
                                                                      if (this.storageId) {
                                                                      for (var i=0; i<this.keys.length; i++) {
                                                                      this.remove(this.keys[i]);
                                                                      }
                                                                      this.keys.length = 0;
                                                                      } else {
                                                                      while(this.size)
                                                                      this.remove(this.key());
                                                                      }
                                                                      return this;
                                                                      };

                                                                      // --- linked list helper functions

                                                                      Map.prototype.link = function(item) {
                                                                      if (this.storageId) {
                                                                      return;
                                                                      }
                                                                      if(this.size == 0) {
                                                                      item.prev = item;
                                                                      item.next = item;
                                                                      this.current = item;
                                                                      }
                                                                      else {
                                                                      item.prev = this.current.prev;
                                                                      item.prev.next = item;
                                                                      item.next = this.current;
                                                                      this.current.prev = item;
                                                                      }
                                                                      };

                                                                      Map.prototype.unlink = function(item) {
                                                                      if (this.storageId) {
                                                                      return;
                                                                      }
                                                                      if(this.size == 0)
                                                                      this.current = undefined;
                                                                      else {
                                                                      item.prev.next = item.next;
                                                                      item.next.prev = item.prev;
                                                                      if(item === this.current)
                                                                      this.current = item.next;
                                                                      }
                                                                      };

                                                                      // --- iterator functions - only work if map is linked

                                                                      Map.prototype.next = function() {
                                                                      this.current = this.current.next;
                                                                      };

                                                                      Map.prototype.key = function() {
                                                                      if (this.storageId) {
                                                                      return undefined;
                                                                      } else {
                                                                      return this.current.key;
                                                                      }
                                                                      };

                                                                      Map.prototype.value = function() {
                                                                      if (this.storageId) {
                                                                      return undefined;
                                                                      }
                                                                      return this.current.value;
                                                                      };






                                                                      share|improve this answer














                                                                      share|improve this answer



                                                                      share|improve this answer








                                                                      edited Jul 20 '14 at 1:44

























                                                                      answered Jul 4 '12 at 0:19









                                                                      g00dnatur3

                                                                      828613




                                                                      828613























                                                                          1














                                                                          Adding yet another solution: HashMap is pretty much the first class I ported from Java to Javascript. You could say there is a lot of overhead, but the implementation is almost 100% equal to Java's implementation and includes all interfaces and subclasses.



                                                                          The project can be found here: https://github.com/Airblader/jsava
                                                                          I'll also attach the (current) source code for the HashMap class, but as stated it also depends on the super class etc. The OOP framework used is qooxdoo.



                                                                          Edit: Please note that this code is already out-dated and refer to the github project for the current work. As of writing this, there is also an ArrayList implementation.



                                                                          qx.Class.define( 'jsava.util.HashMap', {
                                                                          extend: jsava.util.AbstractMap,
                                                                          implement: [jsava.util.Map, jsava.io.Serializable, jsava.lang.Cloneable],

                                                                          construct: function () {
                                                                          var args = Array.prototype.slice.call( arguments ),
                                                                          initialCapacity = this.self( arguments ).DEFAULT_INITIAL_CAPACITY,
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;

                                                                          switch( args.length ) {
                                                                          case 1:
                                                                          if( qx.Class.implementsInterface( args[0], jsava.util.Map ) ) {
                                                                          initialCapacity = Math.max( ((args[0].size() / this.self( arguments ).DEFAULT_LOAD_FACTOR) | 0) + 1,
                                                                          this.self( arguments ).DEFAULT_INITIAL_CAPACITY );
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;
                                                                          } else {
                                                                          initialCapacity = args[0];
                                                                          }
                                                                          break;
                                                                          case 2:
                                                                          initialCapacity = args[0];
                                                                          loadFactor = args[1];
                                                                          break;
                                                                          }

                                                                          if( initialCapacity < 0 ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal initial capacity: ' + initialCapacity );
                                                                          }
                                                                          if( initialCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          initialCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }
                                                                          if( loadFactor <= 0 || isNaN( loadFactor ) ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal load factor: ' + loadFactor );
                                                                          }

                                                                          var capacity = 1;
                                                                          while( capacity < initialCapacity ) {
                                                                          capacity <<= 1;
                                                                          }

                                                                          this._loadFactor = loadFactor;
                                                                          this._threshold = (capacity * loadFactor) | 0;
                                                                          this._table = jsava.JsavaUtils.emptyArrayOfGivenSize( capacity, null );
                                                                          this._init();
                                                                          },

                                                                          statics: {
                                                                          serialVersionUID: 1,

                                                                          DEFAULT_INITIAL_CAPACITY: 16,
                                                                          MAXIMUM_CAPACITY: 1 << 30,
                                                                          DEFAULT_LOAD_FACTOR: 0.75,

                                                                          _hash: function (hash) {
                                                                          hash ^= (hash >>> 20) ^ (hash >>> 12);
                                                                          return hash ^ (hash >>> 7) ^ (hash >>> 4);
                                                                          },

                                                                          _indexFor: function (hashCode, length) {
                                                                          return hashCode & (length - 1);
                                                                          },

                                                                          Entry: qx.Class.define( 'jsava.util.HashMap.Entry', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Map.Entry],

                                                                          construct: function (hash, key, value, nextEntry) {
                                                                          this._value = value;
                                                                          this._next = nextEntry;
                                                                          this._key = key;
                                                                          this._hash = hash;
                                                                          },

                                                                          members: {
                                                                          _key: null,
                                                                          _value: null,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _hash: 0,

                                                                          getKey: function () {
                                                                          return this._key;
                                                                          },

                                                                          getValue: function () {
                                                                          return this._value;
                                                                          },

                                                                          setValue: function (newValue) {
                                                                          var oldValue = this._value;
                                                                          this._value = newValue;
                                                                          return oldValue;
                                                                          },

                                                                          equals: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.HashMap.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          var entry = obj,
                                                                          key1 = this.getKey(),
                                                                          key2 = entry.getKey();
                                                                          if( key1 === key2 || (key1 !== null && key1.equals( key2 )) ) {
                                                                          var value1 = this.getValue(),
                                                                          value2 = entry.getValue();
                                                                          if( value1 === value2 || (value1 !== null && value1.equals( value2 )) ) {
                                                                          return true;
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          hashCode: function () {
                                                                          return (this._key === null ? 0 : this._key.hashCode()) ^
                                                                          (this._value === null ? 0 : this._value.hashCode());
                                                                          },

                                                                          toString: function () {
                                                                          return this.getKey() + '=' + this.getValue();
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the value in an entry is
                                                                          * overwritten by an invocation of put(k,v) for a key k that's already
                                                                          * in the HashMap.
                                                                          */
                                                                          _recordAccess: function (map) {
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the entry is
                                                                          * removed from the table.
                                                                          */
                                                                          _recordRemoval: function (map) {
                                                                          }
                                                                          }
                                                                          } )
                                                                          },

                                                                          members: {
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _table: null,
                                                                          /** @type Number */
                                                                          _size: 0,
                                                                          /** @type Number */
                                                                          _threshold: 0,
                                                                          /** @type Number */
                                                                          _loadFactor: 0,
                                                                          /** @type Number */
                                                                          _modCount: 0,
                                                                          /** @implements jsava.util.Set */
                                                                          __entrySet: null,

                                                                          /**
                                                                          * Initialization hook for subclasses. This method is called
                                                                          * in all constructors and pseudo-constructors (clone, readObject)
                                                                          * after HashMap has been initialized but before any entries have
                                                                          * been inserted. (In the absence of this method, readObject would
                                                                          * require explicit knowledge of subclasses.)
                                                                          */
                                                                          _init: function () {
                                                                          },

                                                                          size: function () {
                                                                          return this._size;
                                                                          },

                                                                          isEmpty: function () {
                                                                          return this._size === 0;
                                                                          },

                                                                          get: function (key) {
                                                                          if( key === null ) {
                                                                          return this.__getForNullKey();
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ((k = entry._key) === key || key.equals( k )) ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          __getForNullKey: function () {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          containsKey: function (key) {
                                                                          return this._getEntry( key ) !== null;
                                                                          },

                                                                          _getEntry: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( ( k = entry._key ) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          return entry;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          put: function (key, value) {
                                                                          if( key === null ) {
                                                                          return this.__putForNullKey( value );
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ( (k = entry._key) === key || key.equals( k ) ) ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( hash, key, value, i );
                                                                          return null;
                                                                          },

                                                                          __putForNullKey: function (value) {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( 0, null, value, 0 );
                                                                          return null;
                                                                          },

                                                                          __putForCreate: function (key, value) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          entry._value = value;
                                                                          return;
                                                                          }
                                                                          }

                                                                          this._createEntry( hash, key, value, i );
                                                                          },

                                                                          __putAllForCreate: function (map) {
                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.__putForCreate( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          _resize: function (newCapacity) {
                                                                          var oldTable = this._table,
                                                                          oldCapacity = oldTable.length;
                                                                          if( oldCapacity === this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          this._threshold = Number.MAX_VALUE;
                                                                          return;
                                                                          }

                                                                          var newTable = jsava.JsavaUtils.emptyArrayOfGivenSize( newCapacity, null );
                                                                          this._transfer( newTable );
                                                                          this._table = newTable;
                                                                          this._threshold = (newCapacity * this._loadFactor) | 0;
                                                                          },

                                                                          _transfer: function (newTable) {
                                                                          var src = this._table,
                                                                          newCapacity = newTable.length;
                                                                          for( var j = 0; j < src.length; j++ ) {
                                                                          var entry = src[j];
                                                                          if( entry !== null ) {
                                                                          src[j] = null;
                                                                          do {
                                                                          var next = entry._next,
                                                                          i = this.self( arguments )._indexFor( entry._hash, newCapacity );
                                                                          entry._next = newTable[i];
                                                                          newTable[i] = entry;
                                                                          entry = next;
                                                                          } while( entry !== null );
                                                                          }
                                                                          }
                                                                          },

                                                                          putAll: function (map) {
                                                                          var numKeyToBeAdded = map.size();
                                                                          if( numKeyToBeAdded === 0 ) {
                                                                          return;
                                                                          }

                                                                          if( numKeyToBeAdded > this._threshold ) {
                                                                          var targetCapacity = (numKeyToBeAdded / this._loadFactor + 1) | 0;
                                                                          if( targetCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          targetCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }

                                                                          var newCapacity = this._table.length;
                                                                          while( newCapacity < targetCapacity ) {
                                                                          newCapacity <<= 1;
                                                                          }
                                                                          if( newCapacity > this._table.length ) {
                                                                          this._resize( newCapacity );
                                                                          }
                                                                          }

                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.put( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          remove: function (key) {
                                                                          var entry = this._removeEntryForKey( key );
                                                                          return entry === null ? null : entry._value;
                                                                          },

                                                                          _removeEntryForKey: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          entry = prev;

                                                                          while( entry !== null ) {
                                                                          var next = entry._next,
                                                                          /** @type jsava.lang.Object */
                                                                          k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === entry ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          entry._recordRemoval( this );
                                                                          return entry;
                                                                          }
                                                                          prev = entry;
                                                                          entry = next;
                                                                          }

                                                                          return entry;
                                                                          },

                                                                          _removeMapping: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return null;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          key = entry.getKey(),
                                                                          hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          e = prev;

                                                                          while( e !== null ) {
                                                                          var next = e._next;
                                                                          if( e._hash === hash && e.equals( entry ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === e ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          e._recordRemoval( this );
                                                                          return e;
                                                                          }
                                                                          prev = e;
                                                                          e = next;
                                                                          }

                                                                          return e;
                                                                          },

                                                                          clear: function () {
                                                                          this._modCount++;
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          table[i] = null;
                                                                          }
                                                                          this._size = 0;
                                                                          },

                                                                          containsValue: function (value) {
                                                                          if( value === null ) {
                                                                          return this.__containsNullValue();
                                                                          }

                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( value.equals( entry._value ) ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          __containsNullValue: function () {
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( entry._value === null ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          clone: function () {
                                                                          /** @type jsava.util.HashMap */
                                                                          var result = null;
                                                                          try {
                                                                          result = this.base( arguments );
                                                                          } catch( e ) {
                                                                          if( !qx.Class.isSubClassOf( e.constructor, jsava.lang.CloneNotSupportedException ) ) {
                                                                          throw e;
                                                                          }
                                                                          }

                                                                          result._table = jsava.JsavaUtils.emptyArrayOfGivenSize( this._table.length, null );
                                                                          result.__entrySet = null;
                                                                          result._modCount = 0;
                                                                          result._size = 0;
                                                                          result._init();
                                                                          result.__putAllForCreate( this );

                                                                          return result;
                                                                          },

                                                                          _addEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          if( this._size++ >= this._threshold ) {
                                                                          this._resize( 2 * this._table.length );
                                                                          }
                                                                          },

                                                                          _createEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          this._size++;
                                                                          },

                                                                          keySet: function () {
                                                                          var keySet = this._keySet;
                                                                          return keySet !== null ? keySet : ( this._keySet = new this.KeySet( this ) );
                                                                          },

                                                                          values: function () {
                                                                          var values = this._values;
                                                                          return values !== null ? values : ( this._values = new this.Values( this ) );
                                                                          },

                                                                          entrySet: function () {
                                                                          return this.__entrySet0();
                                                                          },

                                                                          __entrySet0: function () {
                                                                          var entrySet = this.__entrySet;
                                                                          return entrySet !== null ? entrySet : ( this.__entrySet = new this.EntrySet( this ) );
                                                                          },

                                                                          /** @private */
                                                                          HashIterator: qx.Class.define( 'jsava.util.HashMap.HashIterator', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Iterator],

                                                                          type: 'abstract',

                                                                          /** @protected */
                                                                          construct: function (thisHashMap) {
                                                                          this.__thisHashMap = thisHashMap;
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          if( this.__thisHashMap._size > 0 ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _expectedModCount: 0,
                                                                          /** @type Number */
                                                                          _index: 0,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _current: null,

                                                                          hasNext: function () {
                                                                          return this._next !== null;
                                                                          },

                                                                          _nextEntry: function () {
                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var entry = this._next;
                                                                          if( entry === null ) {
                                                                          throw new jsava.lang.NoSuchElementException();
                                                                          }

                                                                          if( (this._next = entry._next) === null ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }

                                                                          this._current = entry;
                                                                          return entry;
                                                                          },

                                                                          remove: function () {
                                                                          if( this._current === null ) {
                                                                          throw new jsava.lang.IllegalStateException();
                                                                          }

                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var key = this._current._key;
                                                                          this._current = null;
                                                                          this.__thisHashMap._removeEntryForKey( key );
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          _newKeyIterator: function () {
                                                                          return new this.KeyIterator( this );
                                                                          },

                                                                          _newValueIterator: function () {
                                                                          return new this.ValueIterator( this );
                                                                          },

                                                                          _newEntryIterator: function () {
                                                                          return new this.EntryIterator( this );
                                                                          },

                                                                          /** @private */
                                                                          ValueIterator: qx.Class.define( 'jsava.util.HashMap.ValueIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry()._value;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeyIterator: qx.Class.define( 'jsava.util.HashMap.KeyIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry().getKey();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntryIterator: qx.Class.define( 'jsava.util.HashMap.EntryIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeySet: qx.Class.define( 'jsava.util.HashMap.KeySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newKeyIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsKey( obj );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeEntryForKey( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          Values: qx.Class.define( 'jsava.util.HashMap.Values', {
                                                                          extend: jsava.util.AbstractCollection,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newValueIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsValue( obj );
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntrySet: qx.Class.define( 'jsava.util.HashMap.EntrySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newEntryIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          candidate = this.__thisHashMap._getEntry( entry.getKey() );
                                                                          return candidate !== null && candidate.equals( entry );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeMapping( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } )
                                                                          }
                                                                          } );





                                                                          share|improve this answer























                                                                          • Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                                                                            – Claudiu
                                                                            Aug 19 '13 at 19:24










                                                                          • Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 20:00










                                                                          • Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                                                                            – Claudiu
                                                                            Aug 19 '13 at 20:33










                                                                          • Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 21:09










                                                                          • Having fun playing around is the best way to learn =). thanks for sharing
                                                                            – Claudiu
                                                                            Aug 19 '13 at 21:39
















                                                                          1














                                                                          Adding yet another solution: HashMap is pretty much the first class I ported from Java to Javascript. You could say there is a lot of overhead, but the implementation is almost 100% equal to Java's implementation and includes all interfaces and subclasses.



                                                                          The project can be found here: https://github.com/Airblader/jsava
                                                                          I'll also attach the (current) source code for the HashMap class, but as stated it also depends on the super class etc. The OOP framework used is qooxdoo.



                                                                          Edit: Please note that this code is already out-dated and refer to the github project for the current work. As of writing this, there is also an ArrayList implementation.



                                                                          qx.Class.define( 'jsava.util.HashMap', {
                                                                          extend: jsava.util.AbstractMap,
                                                                          implement: [jsava.util.Map, jsava.io.Serializable, jsava.lang.Cloneable],

                                                                          construct: function () {
                                                                          var args = Array.prototype.slice.call( arguments ),
                                                                          initialCapacity = this.self( arguments ).DEFAULT_INITIAL_CAPACITY,
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;

                                                                          switch( args.length ) {
                                                                          case 1:
                                                                          if( qx.Class.implementsInterface( args[0], jsava.util.Map ) ) {
                                                                          initialCapacity = Math.max( ((args[0].size() / this.self( arguments ).DEFAULT_LOAD_FACTOR) | 0) + 1,
                                                                          this.self( arguments ).DEFAULT_INITIAL_CAPACITY );
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;
                                                                          } else {
                                                                          initialCapacity = args[0];
                                                                          }
                                                                          break;
                                                                          case 2:
                                                                          initialCapacity = args[0];
                                                                          loadFactor = args[1];
                                                                          break;
                                                                          }

                                                                          if( initialCapacity < 0 ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal initial capacity: ' + initialCapacity );
                                                                          }
                                                                          if( initialCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          initialCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }
                                                                          if( loadFactor <= 0 || isNaN( loadFactor ) ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal load factor: ' + loadFactor );
                                                                          }

                                                                          var capacity = 1;
                                                                          while( capacity < initialCapacity ) {
                                                                          capacity <<= 1;
                                                                          }

                                                                          this._loadFactor = loadFactor;
                                                                          this._threshold = (capacity * loadFactor) | 0;
                                                                          this._table = jsava.JsavaUtils.emptyArrayOfGivenSize( capacity, null );
                                                                          this._init();
                                                                          },

                                                                          statics: {
                                                                          serialVersionUID: 1,

                                                                          DEFAULT_INITIAL_CAPACITY: 16,
                                                                          MAXIMUM_CAPACITY: 1 << 30,
                                                                          DEFAULT_LOAD_FACTOR: 0.75,

                                                                          _hash: function (hash) {
                                                                          hash ^= (hash >>> 20) ^ (hash >>> 12);
                                                                          return hash ^ (hash >>> 7) ^ (hash >>> 4);
                                                                          },

                                                                          _indexFor: function (hashCode, length) {
                                                                          return hashCode & (length - 1);
                                                                          },

                                                                          Entry: qx.Class.define( 'jsava.util.HashMap.Entry', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Map.Entry],

                                                                          construct: function (hash, key, value, nextEntry) {
                                                                          this._value = value;
                                                                          this._next = nextEntry;
                                                                          this._key = key;
                                                                          this._hash = hash;
                                                                          },

                                                                          members: {
                                                                          _key: null,
                                                                          _value: null,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _hash: 0,

                                                                          getKey: function () {
                                                                          return this._key;
                                                                          },

                                                                          getValue: function () {
                                                                          return this._value;
                                                                          },

                                                                          setValue: function (newValue) {
                                                                          var oldValue = this._value;
                                                                          this._value = newValue;
                                                                          return oldValue;
                                                                          },

                                                                          equals: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.HashMap.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          var entry = obj,
                                                                          key1 = this.getKey(),
                                                                          key2 = entry.getKey();
                                                                          if( key1 === key2 || (key1 !== null && key1.equals( key2 )) ) {
                                                                          var value1 = this.getValue(),
                                                                          value2 = entry.getValue();
                                                                          if( value1 === value2 || (value1 !== null && value1.equals( value2 )) ) {
                                                                          return true;
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          hashCode: function () {
                                                                          return (this._key === null ? 0 : this._key.hashCode()) ^
                                                                          (this._value === null ? 0 : this._value.hashCode());
                                                                          },

                                                                          toString: function () {
                                                                          return this.getKey() + '=' + this.getValue();
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the value in an entry is
                                                                          * overwritten by an invocation of put(k,v) for a key k that's already
                                                                          * in the HashMap.
                                                                          */
                                                                          _recordAccess: function (map) {
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the entry is
                                                                          * removed from the table.
                                                                          */
                                                                          _recordRemoval: function (map) {
                                                                          }
                                                                          }
                                                                          } )
                                                                          },

                                                                          members: {
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _table: null,
                                                                          /** @type Number */
                                                                          _size: 0,
                                                                          /** @type Number */
                                                                          _threshold: 0,
                                                                          /** @type Number */
                                                                          _loadFactor: 0,
                                                                          /** @type Number */
                                                                          _modCount: 0,
                                                                          /** @implements jsava.util.Set */
                                                                          __entrySet: null,

                                                                          /**
                                                                          * Initialization hook for subclasses. This method is called
                                                                          * in all constructors and pseudo-constructors (clone, readObject)
                                                                          * after HashMap has been initialized but before any entries have
                                                                          * been inserted. (In the absence of this method, readObject would
                                                                          * require explicit knowledge of subclasses.)
                                                                          */
                                                                          _init: function () {
                                                                          },

                                                                          size: function () {
                                                                          return this._size;
                                                                          },

                                                                          isEmpty: function () {
                                                                          return this._size === 0;
                                                                          },

                                                                          get: function (key) {
                                                                          if( key === null ) {
                                                                          return this.__getForNullKey();
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ((k = entry._key) === key || key.equals( k )) ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          __getForNullKey: function () {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          containsKey: function (key) {
                                                                          return this._getEntry( key ) !== null;
                                                                          },

                                                                          _getEntry: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( ( k = entry._key ) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          return entry;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          put: function (key, value) {
                                                                          if( key === null ) {
                                                                          return this.__putForNullKey( value );
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ( (k = entry._key) === key || key.equals( k ) ) ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( hash, key, value, i );
                                                                          return null;
                                                                          },

                                                                          __putForNullKey: function (value) {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( 0, null, value, 0 );
                                                                          return null;
                                                                          },

                                                                          __putForCreate: function (key, value) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          entry._value = value;
                                                                          return;
                                                                          }
                                                                          }

                                                                          this._createEntry( hash, key, value, i );
                                                                          },

                                                                          __putAllForCreate: function (map) {
                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.__putForCreate( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          _resize: function (newCapacity) {
                                                                          var oldTable = this._table,
                                                                          oldCapacity = oldTable.length;
                                                                          if( oldCapacity === this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          this._threshold = Number.MAX_VALUE;
                                                                          return;
                                                                          }

                                                                          var newTable = jsava.JsavaUtils.emptyArrayOfGivenSize( newCapacity, null );
                                                                          this._transfer( newTable );
                                                                          this._table = newTable;
                                                                          this._threshold = (newCapacity * this._loadFactor) | 0;
                                                                          },

                                                                          _transfer: function (newTable) {
                                                                          var src = this._table,
                                                                          newCapacity = newTable.length;
                                                                          for( var j = 0; j < src.length; j++ ) {
                                                                          var entry = src[j];
                                                                          if( entry !== null ) {
                                                                          src[j] = null;
                                                                          do {
                                                                          var next = entry._next,
                                                                          i = this.self( arguments )._indexFor( entry._hash, newCapacity );
                                                                          entry._next = newTable[i];
                                                                          newTable[i] = entry;
                                                                          entry = next;
                                                                          } while( entry !== null );
                                                                          }
                                                                          }
                                                                          },

                                                                          putAll: function (map) {
                                                                          var numKeyToBeAdded = map.size();
                                                                          if( numKeyToBeAdded === 0 ) {
                                                                          return;
                                                                          }

                                                                          if( numKeyToBeAdded > this._threshold ) {
                                                                          var targetCapacity = (numKeyToBeAdded / this._loadFactor + 1) | 0;
                                                                          if( targetCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          targetCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }

                                                                          var newCapacity = this._table.length;
                                                                          while( newCapacity < targetCapacity ) {
                                                                          newCapacity <<= 1;
                                                                          }
                                                                          if( newCapacity > this._table.length ) {
                                                                          this._resize( newCapacity );
                                                                          }
                                                                          }

                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.put( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          remove: function (key) {
                                                                          var entry = this._removeEntryForKey( key );
                                                                          return entry === null ? null : entry._value;
                                                                          },

                                                                          _removeEntryForKey: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          entry = prev;

                                                                          while( entry !== null ) {
                                                                          var next = entry._next,
                                                                          /** @type jsava.lang.Object */
                                                                          k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === entry ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          entry._recordRemoval( this );
                                                                          return entry;
                                                                          }
                                                                          prev = entry;
                                                                          entry = next;
                                                                          }

                                                                          return entry;
                                                                          },

                                                                          _removeMapping: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return null;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          key = entry.getKey(),
                                                                          hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          e = prev;

                                                                          while( e !== null ) {
                                                                          var next = e._next;
                                                                          if( e._hash === hash && e.equals( entry ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === e ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          e._recordRemoval( this );
                                                                          return e;
                                                                          }
                                                                          prev = e;
                                                                          e = next;
                                                                          }

                                                                          return e;
                                                                          },

                                                                          clear: function () {
                                                                          this._modCount++;
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          table[i] = null;
                                                                          }
                                                                          this._size = 0;
                                                                          },

                                                                          containsValue: function (value) {
                                                                          if( value === null ) {
                                                                          return this.__containsNullValue();
                                                                          }

                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( value.equals( entry._value ) ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          __containsNullValue: function () {
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( entry._value === null ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          clone: function () {
                                                                          /** @type jsava.util.HashMap */
                                                                          var result = null;
                                                                          try {
                                                                          result = this.base( arguments );
                                                                          } catch( e ) {
                                                                          if( !qx.Class.isSubClassOf( e.constructor, jsava.lang.CloneNotSupportedException ) ) {
                                                                          throw e;
                                                                          }
                                                                          }

                                                                          result._table = jsava.JsavaUtils.emptyArrayOfGivenSize( this._table.length, null );
                                                                          result.__entrySet = null;
                                                                          result._modCount = 0;
                                                                          result._size = 0;
                                                                          result._init();
                                                                          result.__putAllForCreate( this );

                                                                          return result;
                                                                          },

                                                                          _addEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          if( this._size++ >= this._threshold ) {
                                                                          this._resize( 2 * this._table.length );
                                                                          }
                                                                          },

                                                                          _createEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          this._size++;
                                                                          },

                                                                          keySet: function () {
                                                                          var keySet = this._keySet;
                                                                          return keySet !== null ? keySet : ( this._keySet = new this.KeySet( this ) );
                                                                          },

                                                                          values: function () {
                                                                          var values = this._values;
                                                                          return values !== null ? values : ( this._values = new this.Values( this ) );
                                                                          },

                                                                          entrySet: function () {
                                                                          return this.__entrySet0();
                                                                          },

                                                                          __entrySet0: function () {
                                                                          var entrySet = this.__entrySet;
                                                                          return entrySet !== null ? entrySet : ( this.__entrySet = new this.EntrySet( this ) );
                                                                          },

                                                                          /** @private */
                                                                          HashIterator: qx.Class.define( 'jsava.util.HashMap.HashIterator', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Iterator],

                                                                          type: 'abstract',

                                                                          /** @protected */
                                                                          construct: function (thisHashMap) {
                                                                          this.__thisHashMap = thisHashMap;
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          if( this.__thisHashMap._size > 0 ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _expectedModCount: 0,
                                                                          /** @type Number */
                                                                          _index: 0,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _current: null,

                                                                          hasNext: function () {
                                                                          return this._next !== null;
                                                                          },

                                                                          _nextEntry: function () {
                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var entry = this._next;
                                                                          if( entry === null ) {
                                                                          throw new jsava.lang.NoSuchElementException();
                                                                          }

                                                                          if( (this._next = entry._next) === null ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }

                                                                          this._current = entry;
                                                                          return entry;
                                                                          },

                                                                          remove: function () {
                                                                          if( this._current === null ) {
                                                                          throw new jsava.lang.IllegalStateException();
                                                                          }

                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var key = this._current._key;
                                                                          this._current = null;
                                                                          this.__thisHashMap._removeEntryForKey( key );
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          _newKeyIterator: function () {
                                                                          return new this.KeyIterator( this );
                                                                          },

                                                                          _newValueIterator: function () {
                                                                          return new this.ValueIterator( this );
                                                                          },

                                                                          _newEntryIterator: function () {
                                                                          return new this.EntryIterator( this );
                                                                          },

                                                                          /** @private */
                                                                          ValueIterator: qx.Class.define( 'jsava.util.HashMap.ValueIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry()._value;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeyIterator: qx.Class.define( 'jsava.util.HashMap.KeyIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry().getKey();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntryIterator: qx.Class.define( 'jsava.util.HashMap.EntryIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeySet: qx.Class.define( 'jsava.util.HashMap.KeySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newKeyIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsKey( obj );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeEntryForKey( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          Values: qx.Class.define( 'jsava.util.HashMap.Values', {
                                                                          extend: jsava.util.AbstractCollection,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newValueIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsValue( obj );
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntrySet: qx.Class.define( 'jsava.util.HashMap.EntrySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newEntryIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          candidate = this.__thisHashMap._getEntry( entry.getKey() );
                                                                          return candidate !== null && candidate.equals( entry );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeMapping( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } )
                                                                          }
                                                                          } );





                                                                          share|improve this answer























                                                                          • Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                                                                            – Claudiu
                                                                            Aug 19 '13 at 19:24










                                                                          • Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 20:00










                                                                          • Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                                                                            – Claudiu
                                                                            Aug 19 '13 at 20:33










                                                                          • Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 21:09










                                                                          • Having fun playing around is the best way to learn =). thanks for sharing
                                                                            – Claudiu
                                                                            Aug 19 '13 at 21:39














                                                                          1












                                                                          1








                                                                          1






                                                                          Adding yet another solution: HashMap is pretty much the first class I ported from Java to Javascript. You could say there is a lot of overhead, but the implementation is almost 100% equal to Java's implementation and includes all interfaces and subclasses.



                                                                          The project can be found here: https://github.com/Airblader/jsava
                                                                          I'll also attach the (current) source code for the HashMap class, but as stated it also depends on the super class etc. The OOP framework used is qooxdoo.



                                                                          Edit: Please note that this code is already out-dated and refer to the github project for the current work. As of writing this, there is also an ArrayList implementation.



                                                                          qx.Class.define( 'jsava.util.HashMap', {
                                                                          extend: jsava.util.AbstractMap,
                                                                          implement: [jsava.util.Map, jsava.io.Serializable, jsava.lang.Cloneable],

                                                                          construct: function () {
                                                                          var args = Array.prototype.slice.call( arguments ),
                                                                          initialCapacity = this.self( arguments ).DEFAULT_INITIAL_CAPACITY,
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;

                                                                          switch( args.length ) {
                                                                          case 1:
                                                                          if( qx.Class.implementsInterface( args[0], jsava.util.Map ) ) {
                                                                          initialCapacity = Math.max( ((args[0].size() / this.self( arguments ).DEFAULT_LOAD_FACTOR) | 0) + 1,
                                                                          this.self( arguments ).DEFAULT_INITIAL_CAPACITY );
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;
                                                                          } else {
                                                                          initialCapacity = args[0];
                                                                          }
                                                                          break;
                                                                          case 2:
                                                                          initialCapacity = args[0];
                                                                          loadFactor = args[1];
                                                                          break;
                                                                          }

                                                                          if( initialCapacity < 0 ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal initial capacity: ' + initialCapacity );
                                                                          }
                                                                          if( initialCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          initialCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }
                                                                          if( loadFactor <= 0 || isNaN( loadFactor ) ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal load factor: ' + loadFactor );
                                                                          }

                                                                          var capacity = 1;
                                                                          while( capacity < initialCapacity ) {
                                                                          capacity <<= 1;
                                                                          }

                                                                          this._loadFactor = loadFactor;
                                                                          this._threshold = (capacity * loadFactor) | 0;
                                                                          this._table = jsava.JsavaUtils.emptyArrayOfGivenSize( capacity, null );
                                                                          this._init();
                                                                          },

                                                                          statics: {
                                                                          serialVersionUID: 1,

                                                                          DEFAULT_INITIAL_CAPACITY: 16,
                                                                          MAXIMUM_CAPACITY: 1 << 30,
                                                                          DEFAULT_LOAD_FACTOR: 0.75,

                                                                          _hash: function (hash) {
                                                                          hash ^= (hash >>> 20) ^ (hash >>> 12);
                                                                          return hash ^ (hash >>> 7) ^ (hash >>> 4);
                                                                          },

                                                                          _indexFor: function (hashCode, length) {
                                                                          return hashCode & (length - 1);
                                                                          },

                                                                          Entry: qx.Class.define( 'jsava.util.HashMap.Entry', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Map.Entry],

                                                                          construct: function (hash, key, value, nextEntry) {
                                                                          this._value = value;
                                                                          this._next = nextEntry;
                                                                          this._key = key;
                                                                          this._hash = hash;
                                                                          },

                                                                          members: {
                                                                          _key: null,
                                                                          _value: null,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _hash: 0,

                                                                          getKey: function () {
                                                                          return this._key;
                                                                          },

                                                                          getValue: function () {
                                                                          return this._value;
                                                                          },

                                                                          setValue: function (newValue) {
                                                                          var oldValue = this._value;
                                                                          this._value = newValue;
                                                                          return oldValue;
                                                                          },

                                                                          equals: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.HashMap.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          var entry = obj,
                                                                          key1 = this.getKey(),
                                                                          key2 = entry.getKey();
                                                                          if( key1 === key2 || (key1 !== null && key1.equals( key2 )) ) {
                                                                          var value1 = this.getValue(),
                                                                          value2 = entry.getValue();
                                                                          if( value1 === value2 || (value1 !== null && value1.equals( value2 )) ) {
                                                                          return true;
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          hashCode: function () {
                                                                          return (this._key === null ? 0 : this._key.hashCode()) ^
                                                                          (this._value === null ? 0 : this._value.hashCode());
                                                                          },

                                                                          toString: function () {
                                                                          return this.getKey() + '=' + this.getValue();
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the value in an entry is
                                                                          * overwritten by an invocation of put(k,v) for a key k that's already
                                                                          * in the HashMap.
                                                                          */
                                                                          _recordAccess: function (map) {
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the entry is
                                                                          * removed from the table.
                                                                          */
                                                                          _recordRemoval: function (map) {
                                                                          }
                                                                          }
                                                                          } )
                                                                          },

                                                                          members: {
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _table: null,
                                                                          /** @type Number */
                                                                          _size: 0,
                                                                          /** @type Number */
                                                                          _threshold: 0,
                                                                          /** @type Number */
                                                                          _loadFactor: 0,
                                                                          /** @type Number */
                                                                          _modCount: 0,
                                                                          /** @implements jsava.util.Set */
                                                                          __entrySet: null,

                                                                          /**
                                                                          * Initialization hook for subclasses. This method is called
                                                                          * in all constructors and pseudo-constructors (clone, readObject)
                                                                          * after HashMap has been initialized but before any entries have
                                                                          * been inserted. (In the absence of this method, readObject would
                                                                          * require explicit knowledge of subclasses.)
                                                                          */
                                                                          _init: function () {
                                                                          },

                                                                          size: function () {
                                                                          return this._size;
                                                                          },

                                                                          isEmpty: function () {
                                                                          return this._size === 0;
                                                                          },

                                                                          get: function (key) {
                                                                          if( key === null ) {
                                                                          return this.__getForNullKey();
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ((k = entry._key) === key || key.equals( k )) ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          __getForNullKey: function () {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          containsKey: function (key) {
                                                                          return this._getEntry( key ) !== null;
                                                                          },

                                                                          _getEntry: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( ( k = entry._key ) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          return entry;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          put: function (key, value) {
                                                                          if( key === null ) {
                                                                          return this.__putForNullKey( value );
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ( (k = entry._key) === key || key.equals( k ) ) ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( hash, key, value, i );
                                                                          return null;
                                                                          },

                                                                          __putForNullKey: function (value) {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( 0, null, value, 0 );
                                                                          return null;
                                                                          },

                                                                          __putForCreate: function (key, value) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          entry._value = value;
                                                                          return;
                                                                          }
                                                                          }

                                                                          this._createEntry( hash, key, value, i );
                                                                          },

                                                                          __putAllForCreate: function (map) {
                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.__putForCreate( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          _resize: function (newCapacity) {
                                                                          var oldTable = this._table,
                                                                          oldCapacity = oldTable.length;
                                                                          if( oldCapacity === this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          this._threshold = Number.MAX_VALUE;
                                                                          return;
                                                                          }

                                                                          var newTable = jsava.JsavaUtils.emptyArrayOfGivenSize( newCapacity, null );
                                                                          this._transfer( newTable );
                                                                          this._table = newTable;
                                                                          this._threshold = (newCapacity * this._loadFactor) | 0;
                                                                          },

                                                                          _transfer: function (newTable) {
                                                                          var src = this._table,
                                                                          newCapacity = newTable.length;
                                                                          for( var j = 0; j < src.length; j++ ) {
                                                                          var entry = src[j];
                                                                          if( entry !== null ) {
                                                                          src[j] = null;
                                                                          do {
                                                                          var next = entry._next,
                                                                          i = this.self( arguments )._indexFor( entry._hash, newCapacity );
                                                                          entry._next = newTable[i];
                                                                          newTable[i] = entry;
                                                                          entry = next;
                                                                          } while( entry !== null );
                                                                          }
                                                                          }
                                                                          },

                                                                          putAll: function (map) {
                                                                          var numKeyToBeAdded = map.size();
                                                                          if( numKeyToBeAdded === 0 ) {
                                                                          return;
                                                                          }

                                                                          if( numKeyToBeAdded > this._threshold ) {
                                                                          var targetCapacity = (numKeyToBeAdded / this._loadFactor + 1) | 0;
                                                                          if( targetCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          targetCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }

                                                                          var newCapacity = this._table.length;
                                                                          while( newCapacity < targetCapacity ) {
                                                                          newCapacity <<= 1;
                                                                          }
                                                                          if( newCapacity > this._table.length ) {
                                                                          this._resize( newCapacity );
                                                                          }
                                                                          }

                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.put( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          remove: function (key) {
                                                                          var entry = this._removeEntryForKey( key );
                                                                          return entry === null ? null : entry._value;
                                                                          },

                                                                          _removeEntryForKey: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          entry = prev;

                                                                          while( entry !== null ) {
                                                                          var next = entry._next,
                                                                          /** @type jsava.lang.Object */
                                                                          k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === entry ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          entry._recordRemoval( this );
                                                                          return entry;
                                                                          }
                                                                          prev = entry;
                                                                          entry = next;
                                                                          }

                                                                          return entry;
                                                                          },

                                                                          _removeMapping: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return null;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          key = entry.getKey(),
                                                                          hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          e = prev;

                                                                          while( e !== null ) {
                                                                          var next = e._next;
                                                                          if( e._hash === hash && e.equals( entry ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === e ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          e._recordRemoval( this );
                                                                          return e;
                                                                          }
                                                                          prev = e;
                                                                          e = next;
                                                                          }

                                                                          return e;
                                                                          },

                                                                          clear: function () {
                                                                          this._modCount++;
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          table[i] = null;
                                                                          }
                                                                          this._size = 0;
                                                                          },

                                                                          containsValue: function (value) {
                                                                          if( value === null ) {
                                                                          return this.__containsNullValue();
                                                                          }

                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( value.equals( entry._value ) ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          __containsNullValue: function () {
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( entry._value === null ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          clone: function () {
                                                                          /** @type jsava.util.HashMap */
                                                                          var result = null;
                                                                          try {
                                                                          result = this.base( arguments );
                                                                          } catch( e ) {
                                                                          if( !qx.Class.isSubClassOf( e.constructor, jsava.lang.CloneNotSupportedException ) ) {
                                                                          throw e;
                                                                          }
                                                                          }

                                                                          result._table = jsava.JsavaUtils.emptyArrayOfGivenSize( this._table.length, null );
                                                                          result.__entrySet = null;
                                                                          result._modCount = 0;
                                                                          result._size = 0;
                                                                          result._init();
                                                                          result.__putAllForCreate( this );

                                                                          return result;
                                                                          },

                                                                          _addEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          if( this._size++ >= this._threshold ) {
                                                                          this._resize( 2 * this._table.length );
                                                                          }
                                                                          },

                                                                          _createEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          this._size++;
                                                                          },

                                                                          keySet: function () {
                                                                          var keySet = this._keySet;
                                                                          return keySet !== null ? keySet : ( this._keySet = new this.KeySet( this ) );
                                                                          },

                                                                          values: function () {
                                                                          var values = this._values;
                                                                          return values !== null ? values : ( this._values = new this.Values( this ) );
                                                                          },

                                                                          entrySet: function () {
                                                                          return this.__entrySet0();
                                                                          },

                                                                          __entrySet0: function () {
                                                                          var entrySet = this.__entrySet;
                                                                          return entrySet !== null ? entrySet : ( this.__entrySet = new this.EntrySet( this ) );
                                                                          },

                                                                          /** @private */
                                                                          HashIterator: qx.Class.define( 'jsava.util.HashMap.HashIterator', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Iterator],

                                                                          type: 'abstract',

                                                                          /** @protected */
                                                                          construct: function (thisHashMap) {
                                                                          this.__thisHashMap = thisHashMap;
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          if( this.__thisHashMap._size > 0 ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _expectedModCount: 0,
                                                                          /** @type Number */
                                                                          _index: 0,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _current: null,

                                                                          hasNext: function () {
                                                                          return this._next !== null;
                                                                          },

                                                                          _nextEntry: function () {
                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var entry = this._next;
                                                                          if( entry === null ) {
                                                                          throw new jsava.lang.NoSuchElementException();
                                                                          }

                                                                          if( (this._next = entry._next) === null ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }

                                                                          this._current = entry;
                                                                          return entry;
                                                                          },

                                                                          remove: function () {
                                                                          if( this._current === null ) {
                                                                          throw new jsava.lang.IllegalStateException();
                                                                          }

                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var key = this._current._key;
                                                                          this._current = null;
                                                                          this.__thisHashMap._removeEntryForKey( key );
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          _newKeyIterator: function () {
                                                                          return new this.KeyIterator( this );
                                                                          },

                                                                          _newValueIterator: function () {
                                                                          return new this.ValueIterator( this );
                                                                          },

                                                                          _newEntryIterator: function () {
                                                                          return new this.EntryIterator( this );
                                                                          },

                                                                          /** @private */
                                                                          ValueIterator: qx.Class.define( 'jsava.util.HashMap.ValueIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry()._value;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeyIterator: qx.Class.define( 'jsava.util.HashMap.KeyIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry().getKey();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntryIterator: qx.Class.define( 'jsava.util.HashMap.EntryIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeySet: qx.Class.define( 'jsava.util.HashMap.KeySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newKeyIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsKey( obj );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeEntryForKey( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          Values: qx.Class.define( 'jsava.util.HashMap.Values', {
                                                                          extend: jsava.util.AbstractCollection,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newValueIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsValue( obj );
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntrySet: qx.Class.define( 'jsava.util.HashMap.EntrySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newEntryIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          candidate = this.__thisHashMap._getEntry( entry.getKey() );
                                                                          return candidate !== null && candidate.equals( entry );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeMapping( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } )
                                                                          }
                                                                          } );





                                                                          share|improve this answer














                                                                          Adding yet another solution: HashMap is pretty much the first class I ported from Java to Javascript. You could say there is a lot of overhead, but the implementation is almost 100% equal to Java's implementation and includes all interfaces and subclasses.



                                                                          The project can be found here: https://github.com/Airblader/jsava
                                                                          I'll also attach the (current) source code for the HashMap class, but as stated it also depends on the super class etc. The OOP framework used is qooxdoo.



                                                                          Edit: Please note that this code is already out-dated and refer to the github project for the current work. As of writing this, there is also an ArrayList implementation.



                                                                          qx.Class.define( 'jsava.util.HashMap', {
                                                                          extend: jsava.util.AbstractMap,
                                                                          implement: [jsava.util.Map, jsava.io.Serializable, jsava.lang.Cloneable],

                                                                          construct: function () {
                                                                          var args = Array.prototype.slice.call( arguments ),
                                                                          initialCapacity = this.self( arguments ).DEFAULT_INITIAL_CAPACITY,
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;

                                                                          switch( args.length ) {
                                                                          case 1:
                                                                          if( qx.Class.implementsInterface( args[0], jsava.util.Map ) ) {
                                                                          initialCapacity = Math.max( ((args[0].size() / this.self( arguments ).DEFAULT_LOAD_FACTOR) | 0) + 1,
                                                                          this.self( arguments ).DEFAULT_INITIAL_CAPACITY );
                                                                          loadFactor = this.self( arguments ).DEFAULT_LOAD_FACTOR;
                                                                          } else {
                                                                          initialCapacity = args[0];
                                                                          }
                                                                          break;
                                                                          case 2:
                                                                          initialCapacity = args[0];
                                                                          loadFactor = args[1];
                                                                          break;
                                                                          }

                                                                          if( initialCapacity < 0 ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal initial capacity: ' + initialCapacity );
                                                                          }
                                                                          if( initialCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          initialCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }
                                                                          if( loadFactor <= 0 || isNaN( loadFactor ) ) {
                                                                          throw new jsava.lang.IllegalArgumentException( 'Illegal load factor: ' + loadFactor );
                                                                          }

                                                                          var capacity = 1;
                                                                          while( capacity < initialCapacity ) {
                                                                          capacity <<= 1;
                                                                          }

                                                                          this._loadFactor = loadFactor;
                                                                          this._threshold = (capacity * loadFactor) | 0;
                                                                          this._table = jsava.JsavaUtils.emptyArrayOfGivenSize( capacity, null );
                                                                          this._init();
                                                                          },

                                                                          statics: {
                                                                          serialVersionUID: 1,

                                                                          DEFAULT_INITIAL_CAPACITY: 16,
                                                                          MAXIMUM_CAPACITY: 1 << 30,
                                                                          DEFAULT_LOAD_FACTOR: 0.75,

                                                                          _hash: function (hash) {
                                                                          hash ^= (hash >>> 20) ^ (hash >>> 12);
                                                                          return hash ^ (hash >>> 7) ^ (hash >>> 4);
                                                                          },

                                                                          _indexFor: function (hashCode, length) {
                                                                          return hashCode & (length - 1);
                                                                          },

                                                                          Entry: qx.Class.define( 'jsava.util.HashMap.Entry', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Map.Entry],

                                                                          construct: function (hash, key, value, nextEntry) {
                                                                          this._value = value;
                                                                          this._next = nextEntry;
                                                                          this._key = key;
                                                                          this._hash = hash;
                                                                          },

                                                                          members: {
                                                                          _key: null,
                                                                          _value: null,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _hash: 0,

                                                                          getKey: function () {
                                                                          return this._key;
                                                                          },

                                                                          getValue: function () {
                                                                          return this._value;
                                                                          },

                                                                          setValue: function (newValue) {
                                                                          var oldValue = this._value;
                                                                          this._value = newValue;
                                                                          return oldValue;
                                                                          },

                                                                          equals: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.HashMap.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          var entry = obj,
                                                                          key1 = this.getKey(),
                                                                          key2 = entry.getKey();
                                                                          if( key1 === key2 || (key1 !== null && key1.equals( key2 )) ) {
                                                                          var value1 = this.getValue(),
                                                                          value2 = entry.getValue();
                                                                          if( value1 === value2 || (value1 !== null && value1.equals( value2 )) ) {
                                                                          return true;
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          hashCode: function () {
                                                                          return (this._key === null ? 0 : this._key.hashCode()) ^
                                                                          (this._value === null ? 0 : this._value.hashCode());
                                                                          },

                                                                          toString: function () {
                                                                          return this.getKey() + '=' + this.getValue();
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the value in an entry is
                                                                          * overwritten by an invocation of put(k,v) for a key k that's already
                                                                          * in the HashMap.
                                                                          */
                                                                          _recordAccess: function (map) {
                                                                          },

                                                                          /**
                                                                          * This method is invoked whenever the entry is
                                                                          * removed from the table.
                                                                          */
                                                                          _recordRemoval: function (map) {
                                                                          }
                                                                          }
                                                                          } )
                                                                          },

                                                                          members: {
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _table: null,
                                                                          /** @type Number */
                                                                          _size: 0,
                                                                          /** @type Number */
                                                                          _threshold: 0,
                                                                          /** @type Number */
                                                                          _loadFactor: 0,
                                                                          /** @type Number */
                                                                          _modCount: 0,
                                                                          /** @implements jsava.util.Set */
                                                                          __entrySet: null,

                                                                          /**
                                                                          * Initialization hook for subclasses. This method is called
                                                                          * in all constructors and pseudo-constructors (clone, readObject)
                                                                          * after HashMap has been initialized but before any entries have
                                                                          * been inserted. (In the absence of this method, readObject would
                                                                          * require explicit knowledge of subclasses.)
                                                                          */
                                                                          _init: function () {
                                                                          },

                                                                          size: function () {
                                                                          return this._size;
                                                                          },

                                                                          isEmpty: function () {
                                                                          return this._size === 0;
                                                                          },

                                                                          get: function (key) {
                                                                          if( key === null ) {
                                                                          return this.__getForNullKey();
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ((k = entry._key) === key || key.equals( k )) ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          __getForNullKey: function () {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          return entry._value;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          containsKey: function (key) {
                                                                          return this._getEntry( key ) !== null;
                                                                          },

                                                                          _getEntry: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() );
                                                                          for( var entry = this._table[this.self( arguments )._indexFor( hash, this._table.length )];
                                                                          entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( ( k = entry._key ) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          return entry;
                                                                          }
                                                                          }

                                                                          return null;
                                                                          },

                                                                          put: function (key, value) {
                                                                          if( key === null ) {
                                                                          return this.__putForNullKey( value );
                                                                          }

                                                                          var hash = this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash && ( (k = entry._key) === key || key.equals( k ) ) ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( hash, key, value, i );
                                                                          return null;
                                                                          },

                                                                          __putForNullKey: function (value) {
                                                                          for( var entry = this._table[0]; entry !== null; entry = entry._next ) {
                                                                          if( entry._key === null ) {
                                                                          var oldValue = entry._value;
                                                                          entry._value = value;
                                                                          entry._recordAccess( this );
                                                                          return oldValue;
                                                                          }
                                                                          }

                                                                          this._modCount++;
                                                                          this._addEntry( 0, null, value, 0 );
                                                                          return null;
                                                                          },

                                                                          __putForCreate: function (key, value) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length );
                                                                          for( var entry = this._table[i]; entry !== null; entry = entry._next ) {
                                                                          /** @type jsava.lang.Object */
                                                                          var k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          entry._value = value;
                                                                          return;
                                                                          }
                                                                          }

                                                                          this._createEntry( hash, key, value, i );
                                                                          },

                                                                          __putAllForCreate: function (map) {
                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.__putForCreate( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          _resize: function (newCapacity) {
                                                                          var oldTable = this._table,
                                                                          oldCapacity = oldTable.length;
                                                                          if( oldCapacity === this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          this._threshold = Number.MAX_VALUE;
                                                                          return;
                                                                          }

                                                                          var newTable = jsava.JsavaUtils.emptyArrayOfGivenSize( newCapacity, null );
                                                                          this._transfer( newTable );
                                                                          this._table = newTable;
                                                                          this._threshold = (newCapacity * this._loadFactor) | 0;
                                                                          },

                                                                          _transfer: function (newTable) {
                                                                          var src = this._table,
                                                                          newCapacity = newTable.length;
                                                                          for( var j = 0; j < src.length; j++ ) {
                                                                          var entry = src[j];
                                                                          if( entry !== null ) {
                                                                          src[j] = null;
                                                                          do {
                                                                          var next = entry._next,
                                                                          i = this.self( arguments )._indexFor( entry._hash, newCapacity );
                                                                          entry._next = newTable[i];
                                                                          newTable[i] = entry;
                                                                          entry = next;
                                                                          } while( entry !== null );
                                                                          }
                                                                          }
                                                                          },

                                                                          putAll: function (map) {
                                                                          var numKeyToBeAdded = map.size();
                                                                          if( numKeyToBeAdded === 0 ) {
                                                                          return;
                                                                          }

                                                                          if( numKeyToBeAdded > this._threshold ) {
                                                                          var targetCapacity = (numKeyToBeAdded / this._loadFactor + 1) | 0;
                                                                          if( targetCapacity > this.self( arguments ).MAXIMUM_CAPACITY ) {
                                                                          targetCapacity = this.self( arguments ).MAXIMUM_CAPACITY;
                                                                          }

                                                                          var newCapacity = this._table.length;
                                                                          while( newCapacity < targetCapacity ) {
                                                                          newCapacity <<= 1;
                                                                          }
                                                                          if( newCapacity > this._table.length ) {
                                                                          this._resize( newCapacity );
                                                                          }
                                                                          }

                                                                          var iterator = map.entrySet().iterator();
                                                                          while( iterator.hasNext() ) {
                                                                          var entry = iterator.next();
                                                                          this.put( entry.getKey(), entry.getValue() );
                                                                          }
                                                                          },

                                                                          remove: function (key) {
                                                                          var entry = this._removeEntryForKey( key );
                                                                          return entry === null ? null : entry._value;
                                                                          },

                                                                          _removeEntryForKey: function (key) {
                                                                          var hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          entry = prev;

                                                                          while( entry !== null ) {
                                                                          var next = entry._next,
                                                                          /** @type jsava.lang.Object */
                                                                          k;
                                                                          if( entry._hash === hash
                                                                          && ( (k = entry._key) === key || ( key !== null && key.equals( k ) ) ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === entry ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          entry._recordRemoval( this );
                                                                          return entry;
                                                                          }
                                                                          prev = entry;
                                                                          entry = next;
                                                                          }

                                                                          return entry;
                                                                          },

                                                                          _removeMapping: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return null;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          key = entry.getKey(),
                                                                          hash = (key === null) ? 0 : this.self( arguments )._hash( key.hashCode() ),
                                                                          i = this.self( arguments )._indexFor( hash, this._table.length ),
                                                                          prev = this._table[i],
                                                                          e = prev;

                                                                          while( e !== null ) {
                                                                          var next = e._next;
                                                                          if( e._hash === hash && e.equals( entry ) ) {
                                                                          this._modCount++;
                                                                          this._size--;
                                                                          if( prev === e ) {
                                                                          this._table[i] = next;
                                                                          } else {
                                                                          prev._next = next;
                                                                          }
                                                                          e._recordRemoval( this );
                                                                          return e;
                                                                          }
                                                                          prev = e;
                                                                          e = next;
                                                                          }

                                                                          return e;
                                                                          },

                                                                          clear: function () {
                                                                          this._modCount++;
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          table[i] = null;
                                                                          }
                                                                          this._size = 0;
                                                                          },

                                                                          containsValue: function (value) {
                                                                          if( value === null ) {
                                                                          return this.__containsNullValue();
                                                                          }

                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( value.equals( entry._value ) ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          __containsNullValue: function () {
                                                                          var table = this._table;
                                                                          for( var i = 0; i < table.length; i++ ) {
                                                                          for( var entry = table[i]; entry !== null; entry = entry._next ) {
                                                                          if( entry._value === null ) {
                                                                          return true;
                                                                          }
                                                                          }
                                                                          }

                                                                          return false;
                                                                          },

                                                                          clone: function () {
                                                                          /** @type jsava.util.HashMap */
                                                                          var result = null;
                                                                          try {
                                                                          result = this.base( arguments );
                                                                          } catch( e ) {
                                                                          if( !qx.Class.isSubClassOf( e.constructor, jsava.lang.CloneNotSupportedException ) ) {
                                                                          throw e;
                                                                          }
                                                                          }

                                                                          result._table = jsava.JsavaUtils.emptyArrayOfGivenSize( this._table.length, null );
                                                                          result.__entrySet = null;
                                                                          result._modCount = 0;
                                                                          result._size = 0;
                                                                          result._init();
                                                                          result.__putAllForCreate( this );

                                                                          return result;
                                                                          },

                                                                          _addEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          if( this._size++ >= this._threshold ) {
                                                                          this._resize( 2 * this._table.length );
                                                                          }
                                                                          },

                                                                          _createEntry: function (hash, key, value, bucketIndex) {
                                                                          var entry = this._table[bucketIndex];
                                                                          this._table[bucketIndex] = new (this.self( arguments ).Entry)( hash, key, value, entry );
                                                                          this._size++;
                                                                          },

                                                                          keySet: function () {
                                                                          var keySet = this._keySet;
                                                                          return keySet !== null ? keySet : ( this._keySet = new this.KeySet( this ) );
                                                                          },

                                                                          values: function () {
                                                                          var values = this._values;
                                                                          return values !== null ? values : ( this._values = new this.Values( this ) );
                                                                          },

                                                                          entrySet: function () {
                                                                          return this.__entrySet0();
                                                                          },

                                                                          __entrySet0: function () {
                                                                          var entrySet = this.__entrySet;
                                                                          return entrySet !== null ? entrySet : ( this.__entrySet = new this.EntrySet( this ) );
                                                                          },

                                                                          /** @private */
                                                                          HashIterator: qx.Class.define( 'jsava.util.HashMap.HashIterator', {
                                                                          extend: jsava.lang.Object,
                                                                          implement: [jsava.util.Iterator],

                                                                          type: 'abstract',

                                                                          /** @protected */
                                                                          construct: function (thisHashMap) {
                                                                          this.__thisHashMap = thisHashMap;
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          if( this.__thisHashMap._size > 0 ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _next: null,
                                                                          /** @type Number */
                                                                          _expectedModCount: 0,
                                                                          /** @type Number */
                                                                          _index: 0,
                                                                          /** @type jsava.util.HashMap.Entry */
                                                                          _current: null,

                                                                          hasNext: function () {
                                                                          return this._next !== null;
                                                                          },

                                                                          _nextEntry: function () {
                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var entry = this._next;
                                                                          if( entry === null ) {
                                                                          throw new jsava.lang.NoSuchElementException();
                                                                          }

                                                                          if( (this._next = entry._next) === null ) {
                                                                          var table = this.__thisHashMap._table;
                                                                          while( this._index < table.length && ( this._next = table[this._index++] ) === null ) {
                                                                          // do nothing
                                                                          }
                                                                          }

                                                                          this._current = entry;
                                                                          return entry;
                                                                          },

                                                                          remove: function () {
                                                                          if( this._current === null ) {
                                                                          throw new jsava.lang.IllegalStateException();
                                                                          }

                                                                          if( this.__thisHashMap._modCount !== this._expectedModCount ) {
                                                                          throw new jsava.lang.ConcurrentModificationException();
                                                                          }

                                                                          var key = this._current._key;
                                                                          this._current = null;
                                                                          this.__thisHashMap._removeEntryForKey( key );
                                                                          this._expectedModCount = this.__thisHashMap._modCount;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          _newKeyIterator: function () {
                                                                          return new this.KeyIterator( this );
                                                                          },

                                                                          _newValueIterator: function () {
                                                                          return new this.ValueIterator( this );
                                                                          },

                                                                          _newEntryIterator: function () {
                                                                          return new this.EntryIterator( this );
                                                                          },

                                                                          /** @private */
                                                                          ValueIterator: qx.Class.define( 'jsava.util.HashMap.ValueIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry()._value;
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeyIterator: qx.Class.define( 'jsava.util.HashMap.KeyIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry().getKey();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntryIterator: qx.Class.define( 'jsava.util.HashMap.EntryIterator', {
                                                                          extend: jsava.util.HashMap.HashIterator,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments, thisHashMap );
                                                                          },

                                                                          members: {
                                                                          next: function () {
                                                                          return this._nextEntry();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          KeySet: qx.Class.define( 'jsava.util.HashMap.KeySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newKeyIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsKey( obj );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeEntryForKey( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          Values: qx.Class.define( 'jsava.util.HashMap.Values', {
                                                                          extend: jsava.util.AbstractCollection,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newValueIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          return this.__thisHashMap.containsValue( obj );
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } ),

                                                                          /** @private */
                                                                          EntrySet: qx.Class.define( 'jsava.util.HashMap.EntrySet', {
                                                                          extend: jsava.util.AbstractSet,

                                                                          construct: function (thisHashMap) {
                                                                          this.base( arguments );
                                                                          this.__thisHashMap = thisHashMap;
                                                                          },

                                                                          members: {
                                                                          __thisHashMap: null,

                                                                          iterator: function () {
                                                                          return this.__thisHashMap._newEntryIterator();
                                                                          },

                                                                          size: function () {
                                                                          return this.__thisHashMap._size;
                                                                          },

                                                                          contains: function (obj) {
                                                                          if( obj === null || !qx.Class.implementsInterface( obj, jsava.util.Map.Entry ) ) {
                                                                          return false;
                                                                          }

                                                                          /** @implements jsava.util.Map.Entry */
                                                                          var entry = obj,
                                                                          candidate = this.__thisHashMap._getEntry( entry.getKey() );
                                                                          return candidate !== null && candidate.equals( entry );
                                                                          },

                                                                          remove: function (obj) {
                                                                          return this.__thisHashMap._removeMapping( obj ) !== null;
                                                                          },

                                                                          clear: function () {
                                                                          this.__thisHashMap.clear();
                                                                          }
                                                                          }
                                                                          } )
                                                                          }
                                                                          } );






                                                                          share|improve this answer














                                                                          share|improve this answer



                                                                          share|improve this answer








                                                                          edited Aug 24 '13 at 15:57

























                                                                          answered Aug 19 '13 at 18:15









                                                                          Ingo Bürk

                                                                          10.5k53375




                                                                          10.5k53375












                                                                          • Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                                                                            – Claudiu
                                                                            Aug 19 '13 at 19:24










                                                                          • Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 20:00










                                                                          • Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                                                                            – Claudiu
                                                                            Aug 19 '13 at 20:33










                                                                          • Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 21:09










                                                                          • Having fun playing around is the best way to learn =). thanks for sharing
                                                                            – Claudiu
                                                                            Aug 19 '13 at 21:39


















                                                                          • Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                                                                            – Claudiu
                                                                            Aug 19 '13 at 19:24










                                                                          • Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 20:00










                                                                          • Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                                                                            – Claudiu
                                                                            Aug 19 '13 at 20:33










                                                                          • Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                                                                            – Ingo Bürk
                                                                            Aug 19 '13 at 21:09










                                                                          • Having fun playing around is the best way to learn =). thanks for sharing
                                                                            – Claudiu
                                                                            Aug 19 '13 at 21:39
















                                                                          Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                                                                          – Claudiu
                                                                          Aug 19 '13 at 19:24




                                                                          Hmm interesting approach.. have you considered trying out an automated approach? that is, running a Java-to-javascript compiler on the source code for the current java implementation?
                                                                          – Claudiu
                                                                          Aug 19 '13 at 19:24












                                                                          Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                                                                          – Ingo Bürk
                                                                          Aug 19 '13 at 20:00




                                                                          Nope :) This is just a fun project for me and there were quite a few things were I couldn't simply "copy" code. I'm not aware of Java-to-Javascript compilers, though I would believe they exist. I'm not sure how well they would translate this. I'm fairly certain they wouldn't produce good-quality code in any case, though.
                                                                          – Ingo Bürk
                                                                          Aug 19 '13 at 20:00












                                                                          Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                                                                          – Claudiu
                                                                          Aug 19 '13 at 20:33




                                                                          Ah gotcha. I was thinking of Google Web Toolkit's compiler, but it seems they ended up doing what you are doing here for the core libraries: "The GWT compiler supports the vast majority of the Java language itself. The GWT runtime library emulates a relevant subset of the Java runtime library.". Maybe something to look at to see how others solved the same problem!
                                                                          – Claudiu
                                                                          Aug 19 '13 at 20:33












                                                                          Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                                                                          – Ingo Bürk
                                                                          Aug 19 '13 at 21:09




                                                                          Yeah. I'm sure Google's solution is far beyond mine, but then again, I'm just having some fun playing around. Unfortunately, the source code seems to have been revoked(?), at least I can't browse it and the interesting links seem to be dead. Too bad, I would've loved to look at it.
                                                                          – Ingo Bürk
                                                                          Aug 19 '13 at 21:09












                                                                          Having fun playing around is the best way to learn =). thanks for sharing
                                                                          – Claudiu
                                                                          Aug 19 '13 at 21:39




                                                                          Having fun playing around is the best way to learn =). thanks for sharing
                                                                          – Claudiu
                                                                          Aug 19 '13 at 21:39











                                                                          0














                                                                          Yet another map implementation by me. With randomizer, 'generics' and 'iterator' =)



                                                                          var HashMap = function (TKey, TValue) {
                                                                          var db = ;
                                                                          var keyType, valueType;

                                                                          (function () {
                                                                          keyType = TKey;
                                                                          valueType = TValue;
                                                                          })();

                                                                          var getIndexOfKey = function (key) {
                                                                          if (typeof key !== keyType)
                                                                          throw new Error('Type of key should be ' + keyType);
                                                                          for (var i = 0; i < db.length; i++) {
                                                                          if (db[i][0] == key)
                                                                          return i;
                                                                          }
                                                                          return -1;
                                                                          }

                                                                          this.add = function (key, value) {
                                                                          if (typeof key !== keyType) {
                                                                          throw new Error('Type of key should be ' + keyType);
                                                                          } else if (typeof value !== valueType) {
                                                                          throw new Error('Type of value should be ' + valueType);
                                                                          }
                                                                          var index = getIndexOfKey(key);
                                                                          if (index === -1)
                                                                          db.push([key, value]);
                                                                          else
                                                                          db[index][1] = value;
                                                                          return this;
                                                                          }

                                                                          this.get = function (key) {
                                                                          if (typeof key !== keyType || db.length === 0)
                                                                          return null;
                                                                          for (var i = 0; i < db.length; i++) {
                                                                          if (db[i][0] == key)
                                                                          return db[i][1];
                                                                          }
                                                                          return null;
                                                                          }

                                                                          this.size = function () {
                                                                          return db.length;
                                                                          }

                                                                          this.keys = function () {
                                                                          if (db.length === 0)
                                                                          return ;
                                                                          var result = ;
                                                                          for (var i = 0; i < db.length; i++) {
                                                                          result.push(db[i][0]);
                                                                          }
                                                                          return result;
                                                                          }

                                                                          this.values = function () {
                                                                          if (db.length === 0)
                                                                          return ;
                                                                          var result = ;
                                                                          for (var i = 0; i < db.length; i++) {
                                                                          result.push(db[i][1]);
                                                                          }
                                                                          return result;
                                                                          }

                                                                          this.randomize = function () {
                                                                          if (db.length === 0)
                                                                          return this;
                                                                          var currentIndex = db.length, temporaryValue, randomIndex;
                                                                          while (0 !== currentIndex) {
                                                                          randomIndex = Math.floor(Math.random() * currentIndex);
                                                                          currentIndex--;
                                                                          temporaryValue = db[currentIndex];
                                                                          db[currentIndex] = db[randomIndex];
                                                                          db[randomIndex] = temporaryValue;
                                                                          }
                                                                          return this;
                                                                          }

                                                                          this.iterate = function (callback) {
                                                                          if (db.length === 0)
                                                                          return false;
                                                                          for (var i = 0; i < db.length; i++) {
                                                                          callback(db[i][0], db[i][1]);
                                                                          }
                                                                          return true;
                                                                          }
                                                                          }


                                                                          Example:



                                                                          var a = new HashMap("string", "number");
                                                                          a.add('test', 1132)
                                                                          .add('test14', 666)
                                                                          .add('1421test14', 12312666)
                                                                          .iterate(function (key, value) {console.log('a['+key+']='+value)});
                                                                          /*
                                                                          a[test]=1132
                                                                          a[test14]=666
                                                                          a[1421test14]=12312666
                                                                          */
                                                                          a.randomize();
                                                                          /*
                                                                          a[1421test14]=12312666
                                                                          a[test]=1132
                                                                          a[test14]=666
                                                                          */





                                                                          share|improve this answer


























                                                                            0














                                                                            Yet another map implementation by me. With randomizer, 'generics' and 'iterator' =)



                                                                            var HashMap = function (TKey, TValue) {
                                                                            var db = ;
                                                                            var keyType, valueType;

                                                                            (function () {
                                                                            keyType = TKey;
                                                                            valueType = TValue;
                                                                            })();

                                                                            var getIndexOfKey = function (key) {
                                                                            if (typeof key !== keyType)
                                                                            throw new Error('Type of key should be ' + keyType);
                                                                            for (var i = 0; i < db.length; i++) {
                                                                            if (db[i][0] == key)
                                                                            return i;
                                                                            }
                                                                            return -1;
                                                                            }

                                                                            this.add = function (key, value) {
                                                                            if (typeof key !== keyType) {
                                                                            throw new Error('Type of key should be ' + keyType);
                                                                            } else if (typeof value !== valueType) {
                                                                            throw new Error('Type of value should be ' + valueType);
                                                                            }
                                                                            var index = getIndexOfKey(key);
                                                                            if (index === -1)
                                                                            db.push([key, value]);
                                                                            else
                                                                            db[index][1] = value;
                                                                            return this;
                                                                            }

                                                                            this.get = function (key) {
                                                                            if (typeof key !== keyType || db.length === 0)
                                                                            return null;
                                                                            for (var i = 0; i < db.length; i++) {
                                                                            if (db[i][0] == key)
                                                                            return db[i][1];
                                                                            }
                                                                            return null;
                                                                            }

                                                                            this.size = function () {
                                                                            return db.length;
                                                                            }

                                                                            this.keys = function () {
                                                                            if (db.length === 0)
                                                                            return ;
                                                                            var result = ;
                                                                            for (var i = 0; i < db.length; i++) {
                                                                            result.push(db[i][0]);
                                                                            }
                                                                            return result;
                                                                            }

                                                                            this.values = function () {
                                                                            if (db.length === 0)
                                                                            return ;
                                                                            var result = ;
                                                                            for (var i = 0; i < db.length; i++) {
                                                                            result.push(db[i][1]);
                                                                            }
                                                                            return result;
                                                                            }

                                                                            this.randomize = function () {
                                                                            if (db.length === 0)
                                                                            return this;
                                                                            var currentIndex = db.length, temporaryValue, randomIndex;
                                                                            while (0 !== currentIndex) {
                                                                            randomIndex = Math.floor(Math.random() * currentIndex);
                                                                            currentIndex--;
                                                                            temporaryValue = db[currentIndex];
                                                                            db[currentIndex] = db[randomIndex];
                                                                            db[randomIndex] = temporaryValue;
                                                                            }
                                                                            return this;
                                                                            }

                                                                            this.iterate = function (callback) {
                                                                            if (db.length === 0)
                                                                            return false;
                                                                            for (var i = 0; i < db.length; i++) {
                                                                            callback(db[i][0], db[i][1]);
                                                                            }
                                                                            return true;
                                                                            }
                                                                            }


                                                                            Example:



                                                                            var a = new HashMap("string", "number");
                                                                            a.add('test', 1132)
                                                                            .add('test14', 666)
                                                                            .add('1421test14', 12312666)
                                                                            .iterate(function (key, value) {console.log('a['+key+']='+value)});
                                                                            /*
                                                                            a[test]=1132
                                                                            a[test14]=666
                                                                            a[1421test14]=12312666
                                                                            */
                                                                            a.randomize();
                                                                            /*
                                                                            a[1421test14]=12312666
                                                                            a[test]=1132
                                                                            a[test14]=666
                                                                            */





                                                                            share|improve this answer
























                                                                              0












                                                                              0








                                                                              0






                                                                              Yet another map implementation by me. With randomizer, 'generics' and 'iterator' =)



                                                                              var HashMap = function (TKey, TValue) {
                                                                              var db = ;
                                                                              var keyType, valueType;

                                                                              (function () {
                                                                              keyType = TKey;
                                                                              valueType = TValue;
                                                                              })();

                                                                              var getIndexOfKey = function (key) {
                                                                              if (typeof key !== keyType)
                                                                              throw new Error('Type of key should be ' + keyType);
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              if (db[i][0] == key)
                                                                              return i;
                                                                              }
                                                                              return -1;
                                                                              }

                                                                              this.add = function (key, value) {
                                                                              if (typeof key !== keyType) {
                                                                              throw new Error('Type of key should be ' + keyType);
                                                                              } else if (typeof value !== valueType) {
                                                                              throw new Error('Type of value should be ' + valueType);
                                                                              }
                                                                              var index = getIndexOfKey(key);
                                                                              if (index === -1)
                                                                              db.push([key, value]);
                                                                              else
                                                                              db[index][1] = value;
                                                                              return this;
                                                                              }

                                                                              this.get = function (key) {
                                                                              if (typeof key !== keyType || db.length === 0)
                                                                              return null;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              if (db[i][0] == key)
                                                                              return db[i][1];
                                                                              }
                                                                              return null;
                                                                              }

                                                                              this.size = function () {
                                                                              return db.length;
                                                                              }

                                                                              this.keys = function () {
                                                                              if (db.length === 0)
                                                                              return ;
                                                                              var result = ;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              result.push(db[i][0]);
                                                                              }
                                                                              return result;
                                                                              }

                                                                              this.values = function () {
                                                                              if (db.length === 0)
                                                                              return ;
                                                                              var result = ;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              result.push(db[i][1]);
                                                                              }
                                                                              return result;
                                                                              }

                                                                              this.randomize = function () {
                                                                              if (db.length === 0)
                                                                              return this;
                                                                              var currentIndex = db.length, temporaryValue, randomIndex;
                                                                              while (0 !== currentIndex) {
                                                                              randomIndex = Math.floor(Math.random() * currentIndex);
                                                                              currentIndex--;
                                                                              temporaryValue = db[currentIndex];
                                                                              db[currentIndex] = db[randomIndex];
                                                                              db[randomIndex] = temporaryValue;
                                                                              }
                                                                              return this;
                                                                              }

                                                                              this.iterate = function (callback) {
                                                                              if (db.length === 0)
                                                                              return false;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              callback(db[i][0], db[i][1]);
                                                                              }
                                                                              return true;
                                                                              }
                                                                              }


                                                                              Example:



                                                                              var a = new HashMap("string", "number");
                                                                              a.add('test', 1132)
                                                                              .add('test14', 666)
                                                                              .add('1421test14', 12312666)
                                                                              .iterate(function (key, value) {console.log('a['+key+']='+value)});
                                                                              /*
                                                                              a[test]=1132
                                                                              a[test14]=666
                                                                              a[1421test14]=12312666
                                                                              */
                                                                              a.randomize();
                                                                              /*
                                                                              a[1421test14]=12312666
                                                                              a[test]=1132
                                                                              a[test14]=666
                                                                              */





                                                                              share|improve this answer












                                                                              Yet another map implementation by me. With randomizer, 'generics' and 'iterator' =)



                                                                              var HashMap = function (TKey, TValue) {
                                                                              var db = ;
                                                                              var keyType, valueType;

                                                                              (function () {
                                                                              keyType = TKey;
                                                                              valueType = TValue;
                                                                              })();

                                                                              var getIndexOfKey = function (key) {
                                                                              if (typeof key !== keyType)
                                                                              throw new Error('Type of key should be ' + keyType);
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              if (db[i][0] == key)
                                                                              return i;
                                                                              }
                                                                              return -1;
                                                                              }

                                                                              this.add = function (key, value) {
                                                                              if (typeof key !== keyType) {
                                                                              throw new Error('Type of key should be ' + keyType);
                                                                              } else if (typeof value !== valueType) {
                                                                              throw new Error('Type of value should be ' + valueType);
                                                                              }
                                                                              var index = getIndexOfKey(key);
                                                                              if (index === -1)
                                                                              db.push([key, value]);
                                                                              else
                                                                              db[index][1] = value;
                                                                              return this;
                                                                              }

                                                                              this.get = function (key) {
                                                                              if (typeof key !== keyType || db.length === 0)
                                                                              return null;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              if (db[i][0] == key)
                                                                              return db[i][1];
                                                                              }
                                                                              return null;
                                                                              }

                                                                              this.size = function () {
                                                                              return db.length;
                                                                              }

                                                                              this.keys = function () {
                                                                              if (db.length === 0)
                                                                              return ;
                                                                              var result = ;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              result.push(db[i][0]);
                                                                              }
                                                                              return result;
                                                                              }

                                                                              this.values = function () {
                                                                              if (db.length === 0)
                                                                              return ;
                                                                              var result = ;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              result.push(db[i][1]);
                                                                              }
                                                                              return result;
                                                                              }

                                                                              this.randomize = function () {
                                                                              if (db.length === 0)
                                                                              return this;
                                                                              var currentIndex = db.length, temporaryValue, randomIndex;
                                                                              while (0 !== currentIndex) {
                                                                              randomIndex = Math.floor(Math.random() * currentIndex);
                                                                              currentIndex--;
                                                                              temporaryValue = db[currentIndex];
                                                                              db[currentIndex] = db[randomIndex];
                                                                              db[randomIndex] = temporaryValue;
                                                                              }
                                                                              return this;
                                                                              }

                                                                              this.iterate = function (callback) {
                                                                              if (db.length === 0)
                                                                              return false;
                                                                              for (var i = 0; i < db.length; i++) {
                                                                              callback(db[i][0], db[i][1]);
                                                                              }
                                                                              return true;
                                                                              }
                                                                              }


                                                                              Example:



                                                                              var a = new HashMap("string", "number");
                                                                              a.add('test', 1132)
                                                                              .add('test14', 666)
                                                                              .add('1421test14', 12312666)
                                                                              .iterate(function (key, value) {console.log('a['+key+']='+value)});
                                                                              /*
                                                                              a[test]=1132
                                                                              a[test14]=666
                                                                              a[1421test14]=12312666
                                                                              */
                                                                              a.randomize();
                                                                              /*
                                                                              a[1421test14]=12312666
                                                                              a[test]=1132
                                                                              a[test14]=666
                                                                              */






                                                                              share|improve this answer












                                                                              share|improve this answer



                                                                              share|improve this answer










                                                                              answered Jan 13 '14 at 21:10









                                                                              ovnia

                                                                              1,22911943




                                                                              1,22911943






























                                                                                  draft saved

                                                                                  draft discarded




















































                                                                                  Thanks for contributing an answer to Stack Overflow!


                                                                                  • Please be sure to answer the question. Provide details and share your research!

                                                                                  But avoid



                                                                                  • Asking for help, clarification, or responding to other answers.

                                                                                  • Making statements based on opinion; back them up with references or personal experience.


                                                                                  To learn more, see our tips on writing great answers.





                                                                                  Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                                                                                  Please pay close attention to the following guidance:


                                                                                  • Please be sure to answer the question. Provide details and share your research!

                                                                                  But avoid



                                                                                  • Asking for help, clarification, or responding to other answers.

                                                                                  • Making statements based on opinion; back them up with references or personal experience.


                                                                                  To learn more, see our tips on writing great answers.




                                                                                  draft saved


                                                                                  draft discarded














                                                                                  StackExchange.ready(
                                                                                  function () {
                                                                                  StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f368280%2fjavascript-hashmap-equivalent%23new-answer', 'question_page');
                                                                                  }
                                                                                  );

                                                                                  Post as a guest















                                                                                  Required, but never shown





















































                                                                                  Required, but never shown














                                                                                  Required, but never shown












                                                                                  Required, but never shown







                                                                                  Required, but never shown

































                                                                                  Required, but never shown














                                                                                  Required, but never shown












                                                                                  Required, but never shown







                                                                                  Required, but never shown







                                                                                  9g5UqKoXpcjlDKzoaNEE7,6rA5ExmlYCj9P31OnrINBE
                                                                                  hl3,mjlOcZYHKwBPz9NOnHBl2eot1JW t,eEJvIRG

                                                                                  Popular posts from this blog

                                                                                  Monofisismo

                                                                                  Angular Downloading a file using contenturl with Basic Authentication

                                                                                  Olmecas