map.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Datastructure: Hash Map.
  16. *
  17. * @author arv@google.com (Erik Arvidsson)
  18. *
  19. * This file contains an implementation of a Map structure. It implements a lot
  20. * of the methods used in goog.structs so those functions work on hashes. This
  21. * is best suited for complex key types. For simple keys such as numbers and
  22. * strings consider using the lighter-weight utilities in goog.object.
  23. */
  24. goog.provide('goog.structs.Map');
  25. goog.require('goog.iter.Iterator');
  26. goog.require('goog.iter.StopIteration');
  27. goog.require('goog.object');
  28. /**
  29. * Class for Hash Map datastructure.
  30. * @param {*=} opt_map Map or Object to initialize the map with.
  31. * @param {...*} var_args If 2 or more arguments are present then they
  32. * will be used as key-value pairs.
  33. * @constructor
  34. * @template K, V
  35. * @deprecated This type is misleading: use ES6 Map instead.
  36. */
  37. goog.structs.Map = function(opt_map, var_args) {
  38. /**
  39. * Underlying JS object used to implement the map.
  40. * @private {!Object}
  41. */
  42. this.map_ = {};
  43. /**
  44. * An array of keys. This is necessary for two reasons:
  45. * 1. Iterating the keys using for (var key in this.map_) allocates an
  46. * object for every key in IE which is really bad for IE6 GC perf.
  47. * 2. Without a side data structure, we would need to escape all the keys
  48. * as that would be the only way we could tell during iteration if the
  49. * key was an internal key or a property of the object.
  50. *
  51. * This array can contain deleted keys so it's necessary to check the map
  52. * as well to see if the key is still in the map (this doesn't require a
  53. * memory allocation in IE).
  54. * @private {!Array<string>}
  55. */
  56. this.keys_ = [];
  57. /**
  58. * The number of key value pairs in the map.
  59. * @private {number}
  60. */
  61. this.count_ = 0;
  62. /**
  63. * Version used to detect changes while iterating.
  64. * @private {number}
  65. */
  66. this.version_ = 0;
  67. var argLength = arguments.length;
  68. if (argLength > 1) {
  69. if (argLength % 2) {
  70. throw Error('Uneven number of arguments');
  71. }
  72. for (var i = 0; i < argLength; i += 2) {
  73. this.set(arguments[i], arguments[i + 1]);
  74. }
  75. } else if (opt_map) {
  76. this.addAll(/** @type {Object} */ (opt_map));
  77. }
  78. };
  79. /**
  80. * @return {number} The number of key-value pairs in the map.
  81. */
  82. goog.structs.Map.prototype.getCount = function() {
  83. return this.count_;
  84. };
  85. /**
  86. * Returns the values of the map.
  87. * @return {!Array<V>} The values in the map.
  88. */
  89. goog.structs.Map.prototype.getValues = function() {
  90. this.cleanupKeysArray_();
  91. var rv = [];
  92. for (var i = 0; i < this.keys_.length; i++) {
  93. var key = this.keys_[i];
  94. rv.push(this.map_[key]);
  95. }
  96. return rv;
  97. };
  98. /**
  99. * Returns the keys of the map.
  100. * @return {!Array<string>} Array of string values.
  101. */
  102. goog.structs.Map.prototype.getKeys = function() {
  103. this.cleanupKeysArray_();
  104. return /** @type {!Array<string>} */ (this.keys_.concat());
  105. };
  106. /**
  107. * Whether the map contains the given key.
  108. * @param {*} key The key to check for.
  109. * @return {boolean} Whether the map contains the key.
  110. */
  111. goog.structs.Map.prototype.containsKey = function(key) {
  112. return goog.structs.Map.hasKey_(this.map_, key);
  113. };
  114. /**
  115. * Whether the map contains the given value. This is O(n).
  116. * @param {V} val The value to check for.
  117. * @return {boolean} Whether the map contains the value.
  118. */
  119. goog.structs.Map.prototype.containsValue = function(val) {
  120. for (var i = 0; i < this.keys_.length; i++) {
  121. var key = this.keys_[i];
  122. if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) {
  123. return true;
  124. }
  125. }
  126. return false;
  127. };
  128. /**
  129. * Whether this map is equal to the argument map.
  130. * @param {goog.structs.Map} otherMap The map against which to test equality.
  131. * @param {function(V, V): boolean=} opt_equalityFn Optional equality function
  132. * to test equality of values. If not specified, this will test whether
  133. * the values contained in each map are identical objects.
  134. * @return {boolean} Whether the maps are equal.
  135. */
  136. goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) {
  137. if (this === otherMap) {
  138. return true;
  139. }
  140. if (this.count_ != otherMap.getCount()) {
  141. return false;
  142. }
  143. var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals;
  144. this.cleanupKeysArray_();
  145. for (var key, i = 0; key = this.keys_[i]; i++) {
  146. if (!equalityFn(this.get(key), otherMap.get(key))) {
  147. return false;
  148. }
  149. }
  150. return true;
  151. };
  152. /**
  153. * Default equality test for values.
  154. * @param {*} a The first value.
  155. * @param {*} b The second value.
  156. * @return {boolean} Whether a and b reference the same object.
  157. */
  158. goog.structs.Map.defaultEquals = function(a, b) {
  159. return a === b;
  160. };
  161. /**
  162. * @return {boolean} Whether the map is empty.
  163. */
  164. goog.structs.Map.prototype.isEmpty = function() {
  165. return this.count_ == 0;
  166. };
  167. /**
  168. * Removes all key-value pairs from the map.
  169. */
  170. goog.structs.Map.prototype.clear = function() {
  171. this.map_ = {};
  172. this.keys_.length = 0;
  173. this.count_ = 0;
  174. this.version_ = 0;
  175. };
  176. /**
  177. * Removes a key-value pair based on the key. This is O(logN) amortized due to
  178. * updating the keys array whenever the count becomes half the size of the keys
  179. * in the keys array.
  180. * @param {*} key The key to remove.
  181. * @return {boolean} Whether object was removed.
  182. */
  183. goog.structs.Map.prototype.remove = function(key) {
  184. if (goog.structs.Map.hasKey_(this.map_, key)) {
  185. delete this.map_[key];
  186. this.count_--;
  187. this.version_++;
  188. // clean up the keys array if the threshold is hit
  189. if (this.keys_.length > 2 * this.count_) {
  190. this.cleanupKeysArray_();
  191. }
  192. return true;
  193. }
  194. return false;
  195. };
  196. /**
  197. * Cleans up the temp keys array by removing entries that are no longer in the
  198. * map.
  199. * @private
  200. */
  201. goog.structs.Map.prototype.cleanupKeysArray_ = function() {
  202. if (this.count_ != this.keys_.length) {
  203. // First remove keys that are no longer in the map.
  204. var srcIndex = 0;
  205. var destIndex = 0;
  206. while (srcIndex < this.keys_.length) {
  207. var key = this.keys_[srcIndex];
  208. if (goog.structs.Map.hasKey_(this.map_, key)) {
  209. this.keys_[destIndex++] = key;
  210. }
  211. srcIndex++;
  212. }
  213. this.keys_.length = destIndex;
  214. }
  215. if (this.count_ != this.keys_.length) {
  216. // If the count still isn't correct, that means we have duplicates. This can
  217. // happen when the same key is added and removed multiple times. Now we have
  218. // to allocate one extra Object to remove the duplicates. This could have
  219. // been done in the first pass, but in the common case, we can avoid
  220. // allocating an extra object by only doing this when necessary.
  221. var seen = {};
  222. var srcIndex = 0;
  223. var destIndex = 0;
  224. while (srcIndex < this.keys_.length) {
  225. var key = this.keys_[srcIndex];
  226. if (!(goog.structs.Map.hasKey_(seen, key))) {
  227. this.keys_[destIndex++] = key;
  228. seen[key] = 1;
  229. }
  230. srcIndex++;
  231. }
  232. this.keys_.length = destIndex;
  233. }
  234. };
  235. /**
  236. * Returns the value for the given key. If the key is not found and the default
  237. * value is not given this will return {@code undefined}.
  238. * @param {*} key The key to get the value for.
  239. * @param {DEFAULT=} opt_val The value to return if no item is found for the
  240. * given key, defaults to undefined.
  241. * @return {V|DEFAULT} The value for the given key.
  242. * @template DEFAULT
  243. */
  244. goog.structs.Map.prototype.get = function(key, opt_val) {
  245. if (goog.structs.Map.hasKey_(this.map_, key)) {
  246. return this.map_[key];
  247. }
  248. return opt_val;
  249. };
  250. /**
  251. * Adds a key-value pair to the map.
  252. * @param {*} key The key.
  253. * @param {V} value The value to add.
  254. * @return {*} Some subclasses return a value.
  255. */
  256. goog.structs.Map.prototype.set = function(key, value) {
  257. if (!(goog.structs.Map.hasKey_(this.map_, key))) {
  258. this.count_++;
  259. // TODO(johnlenz): This class lies, it claims to return an array of string
  260. // keys, but instead returns the original object used.
  261. this.keys_.push(/** @type {?} */ (key));
  262. // Only change the version if we add a new key.
  263. this.version_++;
  264. }
  265. this.map_[key] = value;
  266. };
  267. /**
  268. * Adds multiple key-value pairs from another goog.structs.Map or Object.
  269. * @param {Object} map Object containing the data to add.
  270. */
  271. goog.structs.Map.prototype.addAll = function(map) {
  272. var keys, values;
  273. if (map instanceof goog.structs.Map) {
  274. keys = map.getKeys();
  275. values = map.getValues();
  276. } else {
  277. keys = goog.object.getKeys(map);
  278. values = goog.object.getValues(map);
  279. }
  280. // we could use goog.array.forEach here but I don't want to introduce that
  281. // dependency just for this.
  282. for (var i = 0; i < keys.length; i++) {
  283. this.set(keys[i], values[i]);
  284. }
  285. };
  286. /**
  287. * Calls the given function on each entry in the map.
  288. * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
  289. * @param {T=} opt_obj The value of "this" inside f.
  290. * @template T
  291. */
  292. goog.structs.Map.prototype.forEach = function(f, opt_obj) {
  293. var keys = this.getKeys();
  294. for (var i = 0; i < keys.length; i++) {
  295. var key = keys[i];
  296. var value = this.get(key);
  297. f.call(opt_obj, value, key, this);
  298. }
  299. };
  300. /**
  301. * Clones a map and returns a new map.
  302. * @return {!goog.structs.Map} A new map with the same key-value pairs.
  303. */
  304. goog.structs.Map.prototype.clone = function() {
  305. return new goog.structs.Map(this);
  306. };
  307. /**
  308. * Returns a new map in which all the keys and values are interchanged
  309. * (keys become values and values become keys). If multiple keys map to the
  310. * same value, the chosen transposed value is implementation-dependent.
  311. *
  312. * It acts very similarly to {goog.object.transpose(Object)}.
  313. *
  314. * @return {!goog.structs.Map} The transposed map.
  315. */
  316. goog.structs.Map.prototype.transpose = function() {
  317. var transposed = new goog.structs.Map();
  318. for (var i = 0; i < this.keys_.length; i++) {
  319. var key = this.keys_[i];
  320. var value = this.map_[key];
  321. transposed.set(value, key);
  322. }
  323. return transposed;
  324. };
  325. /**
  326. * @return {!Object} Object representation of the map.
  327. */
  328. goog.structs.Map.prototype.toObject = function() {
  329. this.cleanupKeysArray_();
  330. var obj = {};
  331. for (var i = 0; i < this.keys_.length; i++) {
  332. var key = this.keys_[i];
  333. obj[key] = this.map_[key];
  334. }
  335. return obj;
  336. };
  337. /**
  338. * Returns an iterator that iterates over the keys in the map. Removal of keys
  339. * while iterating might have undesired side effects.
  340. * @return {!goog.iter.Iterator} An iterator over the keys in the map.
  341. */
  342. goog.structs.Map.prototype.getKeyIterator = function() {
  343. return this.__iterator__(true);
  344. };
  345. /**
  346. * Returns an iterator that iterates over the values in the map. Removal of
  347. * keys while iterating might have undesired side effects.
  348. * @return {!goog.iter.Iterator} An iterator over the values in the map.
  349. */
  350. goog.structs.Map.prototype.getValueIterator = function() {
  351. return this.__iterator__(false);
  352. };
  353. /**
  354. * Returns an iterator that iterates over the values or the keys in the map.
  355. * This throws an exception if the map was mutated since the iterator was
  356. * created.
  357. * @param {boolean=} opt_keys True to iterate over the keys. False to iterate
  358. * over the values. The default value is false.
  359. * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
  360. */
  361. goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
  362. // Clean up keys to minimize the risk of iterating over dead keys.
  363. this.cleanupKeysArray_();
  364. var i = 0;
  365. var version = this.version_;
  366. var selfObj = this;
  367. var newIter = new goog.iter.Iterator;
  368. newIter.next = function() {
  369. if (version != selfObj.version_) {
  370. throw Error('The map has changed since the iterator was created');
  371. }
  372. if (i >= selfObj.keys_.length) {
  373. throw goog.iter.StopIteration;
  374. }
  375. var key = selfObj.keys_[i++];
  376. return opt_keys ? key : selfObj.map_[key];
  377. };
  378. return newIter;
  379. };
  380. /**
  381. * Safe way to test for hasOwnProperty. It even allows testing for
  382. * 'hasOwnProperty'.
  383. * @param {Object} obj The object to test for presence of the given key.
  384. * @param {*} key The key to check for.
  385. * @return {boolean} Whether the object has the key.
  386. * @private
  387. */
  388. goog.structs.Map.hasKey_ = function(obj, key) {
  389. return Object.prototype.hasOwnProperty.call(obj, key);
  390. };