2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE below for additional
4 * information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
18 /******* NOTICE *********
21 Copyright 2006, 2010 The Apache Software Foundation.
23 This product includes software developed at
24 The Apache Software Foundation (http://www.apache.org/).
26 Portions of Apache Harmony were originally developed by
27 Intel Corporation and are licensed to the Apache Software
28 Foundation under the "Software Grant and Corporate Contribution
29 License Agreement" and for which the following copyright notices
31 (C) Copyright 2005 Intel Corporation
32 (C) Copyright 2005-2006 Intel Corporation
33 (C) Copyright 2006 Intel Corporation
36 The following copyright notice(s) were affixed to portions of the code
37 with which this file is now or was at one time distributed
38 and are placed here unaltered.
40 (C) Copyright 1997,2004 International Business Machines Corporation.
43 (C) Copyright IBM Corp. 2003.
46 This software contains code derived from UNIX V7, Copyright(C)
47 Caldera International Inc.
49 ************************/
51 // This code is a manual translation of Apache Harmony's HashMap class to
54 var HashMap = (function() {
55 var DEFAULT_SIZE = 16;
57 function calculateCapacity(x)
72 function computeHashCode(key)
81 return key.hashCode();
87 key = "" + key; // Compute string hash of the double. It's the best we can do.
89 // source: http://en.wikipedia.org/wiki/Java_hashCode()#Sample_implementations_of_the_java.lang.String_algorithm
92 for (var index = 0; index < len; index++) {
93 h = (((31 * h) | 0) + key.charCodeAt(index)) | 0;
97 throw new Error("Internal error: Bad JavaScript value type");
101 function equals(a, b)
103 if (typeof a != typeof b)
122 function Entry(key, hash, value)
126 this._origKeyHash = hash;
133 var result = new Entry(this._key, this._hash, this._value);
135 result._next = this._next.clone();
141 return this._key + "=" + this._value;
155 function AbstractMapIterator(map)
157 this._associatedMap = map;
158 this._expectedModCount = map._modCount;
159 this._futureEntry = null;
160 this._currentEntry = null;
161 this._prevEntry = null;
165 AbstractMapIterator.prototype = {
168 if (this._futureEntry)
170 while (this._position < this._associatedMap._elementData.length) {
171 if (!this._associatedMap._elementData[this._position])
179 _checkConcurrentMod: function()
181 if (this._expectedModCount != this._associatedMap._modCount)
182 throw new Error("Concurrent HashMap modification detected");
185 _makeNext: function()
187 this._checkConcurrentMod();
189 throw new Error("No such element");
190 if (!this._futureEntry) {
191 this._currentEntry = this._associatedMap._elementData[this._position++];
192 this._futureEntry = this._currentEntry._next;
193 this._prevEntry = null;
196 if (this._currentEntry)
197 this._prevEntry = this._currentEntry;
198 this._currentEntry = this._futureEntry;
199 this._futureEntry = this._futureEntry._next;
204 this._checkConcurrentMod();
205 if (!this._currentEntry)
206 throw new Error("Illegal state");
207 if (!this._prevEntry) {
208 var index = this._currentEntry._origKeyHash & (this._associatedMap._elementData.length - 1);
209 this._associatedMap._elementData[index] = this._associatedMap._elementData[index]._next;
211 this._prevEntry._next = this._currentEntry._next;
212 this._currentEntry = null;
213 this._expectedModCount++;
214 this._associatedMap._modCount++;
215 this._associatedMap._elementCount--;
219 function EntryIterator(map)
221 AbstractMapIterator.call(this, map);
224 EntryIterator.prototype = {
228 return this._currentEntry;
231 EntryIterator.prototype.__proto__ = AbstractMapIterator.prototype;
233 function KeyIterator(map)
235 AbstractMapIterator.call(this, map);
238 KeyIterator.prototype = {
242 return this._currentEntry._key;
245 KeyIterator.prototype.__proto__ = AbstractMapIterator.prototype;
247 function ValueIterator(map)
249 AbstractMapIterator.call(this, map);
252 ValueIterator.prototype = {
256 return this._currentEntry._value;
259 ValueIterator.prototype.__proto__ = AbstractMapIterator.prototype;
261 function EntrySet(map)
263 this._associatedMap = map;
266 EntrySet.prototype = {
269 return this._associatedMap._elementCount;
274 this._associatedMap.clear();
277 remove: function(object)
279 var entry = this._associatedMap._getEntry(object.key);
282 if (!equals(entry._value, object.value))
284 this._associatedMap._removeEntry(entry);
288 contains: function(object)
290 var entry = this._associatedMap._getEntry(object.key);
293 return equals(entry._value, object.value);
298 return new EntryIterator(this._associatedMap);
304 this._associatedMap = map;
308 contains: function(object)
310 return this._associatedMap.contains(object);
315 return this._associatedMap._elementCount;
320 this._associatedMap.clear();
323 remove: function(key)
325 return !!this._associatedMap.remove(key);
330 return new KeyIterator(this._associatedMap);
334 function ValueCollection(map)
336 this._associatedMap = map;
339 ValueCollection.prototype = {
340 contains: function(object)
342 return this._associatedMap.containsValue(object);
347 return this._associatedMap._elementCount;
352 this._associatedMap.clear();
357 return new ValueIterator(this._associatedMap);
361 function HashMap(capacity, loadFactor)
363 if (capacity == null)
364 capacity = DEFAULT_SIZE;
365 if (loadFactor == null)
369 throw new Error("Invalid argument to HashMap constructor: capacity is negative");
371 throw new Error("Invalid argument to HashMap constructor: loadFactor is not positive");
373 this._capacity = calculateCapacity(capacity);
374 this._elementCount = 0;
375 this._elementData = new Array(this.capacity);
376 this._loadFactor = loadFactor;
378 this._computeThreshold();
381 HashMap.prototype = {
382 _computeThreshold: function()
384 this._threshold = (this._elementData.length * this._loadFactor) | 0;
389 if (!this._elementCount)
392 this._elementCount = 0;
393 for (var i = this._elementData.length; i--;)
394 this._elementData[i] = null;
400 var result = new HashMap(this._elementData.length, this._loadFactor);
405 containsKey: function(key)
407 return !!this._getEntry(key);
410 containsValue: function(value)
412 for (var i = this._elementData.length; i--;) {
413 for (var entry = this._elementData[i]; entry; entry = entry._next) {
414 if (equals(value, entry._value))
423 return new EntrySet(this);
428 var entry = this._getEntry(key);
434 _getEntry: function(key)
436 var hash = computeHashCode(key);
437 var index = hash & (this._elementData.length - 1);
438 return this._findKeyEntry(key, index, hash);
441 _findKeyEntry: function(key, index, keyHash)
443 var entry = this._elementData[index];
444 while (entry && (entry._origKeyHash != keyHash || !equals(key, entry._key)))
451 return !this._elementCount;
456 return new KeySet(this);
459 put: function(key, value)
461 var hash = computeHashCode(key);
462 var index = hash & (this._elementData.length - 1);
463 var entry = this._findKeyEntry(key, index, hash);
466 entry = this._createHashedEntry(key, index, hash);
467 if (++this._elementCount > this._threshold)
471 var result = entry._value;
472 entry._value = value;
476 _createHashedEntry: function(key, index, hash)
478 var entry = new Entry(key, hash, null);
479 entry._next = this._elementData[index];
480 this._elementData[index] = entry;
484 putAll: function(map)
488 for (var iter = map.entrySet().iterator(); iter.hasNext();) {
489 var entry = iter.next();
490 put(entry.key, entry.value);
494 _rehash: function(capacity)
496 if (capacity == null)
497 capacity = this._elementData.length;
499 var length = calculateCapacity(!capacity ? 1 : capacity << 1);
500 var newData = new Array(length);
501 for (var i = 0; i < this._elementData.length; ++i) {
502 var entry = this._elementData[i];
503 this._elementData[i] = null;
505 var index = entry._origKeyHash & (length - 1);
506 var next = entry._next;
507 entry._next = newData[index];
508 newData[index] = entry;
512 this._elementData = newData;
513 this._computeThreshold();
516 remove: function(key)
518 var entry = this._removeEntryForKey(key);
524 _removeEntry: function(entry)
526 var index = entry._origKeyHash & (this._elementData.length - 1);
527 var current = this._elementData[index];
528 if (current == entry)
529 this._elementData[index] = entry._next;
531 while (current._next != entry)
532 current = current._next;
533 current._next = entry._next;
536 this._elementCount--;
539 _removeEntryForKey: function(key)
541 var hash = computeHashCode(key);
542 var index = hash & (this._elementData.length - 1);
543 var entry = this._elementData[index];
545 while (entry != null && !(entry._origKeyHash == hash && equals(key, entry._key))) {
552 this._elementData[index] = entry._next;
554 last._next = entry._next;
556 this._elementCount--;
562 return this._elementCount;
567 return new ValueCollection(this);
574 var map = new HashMap();
577 for (var i = 0; i < COUNT; ++i)
581 for (var j = 0; j < 5; ++j) {
582 for (var i = 0; i < COUNT; ++i)
583 result += map.get(i);
588 for (var iterator = map.entrySet().iterator(); iterator.hasNext();) {
589 var entry = iterator.next();
591 valueSum += entry.value;
594 if (result != 10500000)
595 throw "Error: result = " + result;
596 if (keySum != 1249975000)
597 throw "Error: keySum = " + keySum;
598 if (valueSum != 2100000)
599 throw "Error: valueSum = " + valueSum;