1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.db.impl.query;
\r
14 ///////////////////////////////////////////////////////////////////////////////
\r
15 //Copyright (c) 2001, Eric D. Friedman All Rights Reserved.
\r
17 //This library is free software; you can redistribute it and/or
\r
18 //modify it under the terms of the GNU Lesser General Public
\r
19 //License as published by the Free Software Foundation; either
\r
20 //version 2.1 of the License, or (at your option) any later version.
\r
22 //This library is distributed in the hope that it will be useful,
\r
23 //but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
24 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
25 //GNU General Public License for more details.
\r
27 //You should have received a copy of the GNU Lesser General Public
\r
28 //License along with this program; if not, write to the Free Software
\r
29 //Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
\r
30 ///////////////////////////////////////////////////////////////////////////////
\r
32 import gnu.trove.function.TObjectFunction;
\r
33 import gnu.trove.procedure.TObjectObjectProcedure;
\r
34 import gnu.trove.procedure.TObjectProcedure;
\r
36 import java.io.Externalizable;
\r
37 import java.util.ArrayList;
\r
38 import java.util.Arrays;
\r
39 import java.util.Collection;
\r
40 import java.util.Iterator;
\r
41 import java.util.Map;
\r
42 import java.util.Set;
\r
45 * An implementation of the Map interface which uses an open addressed
\r
46 * hash table to store its contents.
\r
48 * Created: Sun Nov 4 08:52:45 2001
\r
50 * @author Eric D. Friedman
\r
51 * @version $Id: THashMap.java,v 1.33 2008/05/08 17:42:55 robeden Exp $
\r
53 public class StableHashMap<K,V> extends StableObjectHash<K> implements Map<K,V>, Externalizable {
\r
54 static final long serialVersionUID = 1L;
\r
56 /** the values of the map */
\r
57 protected transient V[] _values;
\r
60 * Creates a new <code>THashMap</code> instance with the default
\r
61 * capacity and load factor.
\r
63 public StableHashMap() {
\r
68 // * Creates a new <code>THashMap</code> instance with a prime
\r
69 // * capacity equal to or greater than <tt>initialCapacity</tt> and
\r
70 // * with the default load factor.
\r
72 // * @param initialCapacity an <code>int</code> value
\r
74 // public StableHashMap(int initialCapacity) {
\r
75 // super(initialCapacity);
\r
79 // * Creates a new <code>THashMap</code> instance with a prime
\r
80 // * capacity equal to or greater than <tt>initialCapacity</tt> and
\r
81 // * with the default load factor.
\r
83 // * @param initialCapacity an <code>int</code> value
\r
84 // * @param strategy used to compute hash codes and to compare objects.
\r
86 // public StableHashMap(int initialCapacity, TObjectHashingStrategy<K> strategy) {
\r
87 // super(initialCapacity, strategy);
\r
91 // * Creates a new <code>THashMap</code> instance with a prime
\r
92 // * capacity equal to or greater than <tt>initialCapacity</tt> and
\r
93 // * with the specified load factor.
\r
95 // * @param initialCapacity an <code>int</code> value
\r
96 // * @param loadFactor a <code>float</code> value
\r
98 // public StableHashMap(int initialCapacity, float loadFactor) {
\r
99 // super(initialCapacity, loadFactor);
\r
103 // * Creates a new <code>THashMap</code> instance with a prime
\r
104 // * capacity equal to or greater than <tt>initialCapacity</tt> and
\r
105 // * with the specified load factor.
\r
107 // * @param initialCapacity an <code>int</code> value
\r
108 // * @param loadFactor a <code>float</code> value
\r
109 // * @param strategy used to compute hash codes and to compare objects.
\r
111 // public StableHashMap(int initialCapacity, float loadFactor, TObjectHashingStrategy<K> strategy) {
\r
112 // super(initialCapacity, loadFactor, strategy);
\r
116 // * Creates a new <code>THashMap</code> instance which contains the
\r
117 // * key/value pairs in <tt>map</tt>.
\r
119 // * @param map a <code>Map</code> value
\r
121 // public StableHashMap(Map<K,V> map) {
\r
122 // this(map.size());
\r
127 // * Creates a new <code>THashMap</code> instance which contains the
\r
128 // * key/value pairs in <tt>map</tt>.
\r
130 // * @param map a <code>Map</code> value
\r
131 // * @param strategy used to compute hash codes and to compare objects.
\r
133 // public StableHashMap(Map<K,V> map, TObjectHashingStrategy<K> strategy) {
\r
134 // this(map.size(), strategy);
\r
139 * @return a shallow clone of this collection
\r
141 public StableHashMap<K,V> clone() {
\r
142 StableHashMap<K,V> m = (StableHashMap<K,V>)super.clone();
\r
143 m._values = this._values.clone();
\r
148 * initialize the value array of the map.
\r
150 * @param initialCapacity an <code>int</code> value
\r
151 * @return an <code>int</code> value
\r
153 protected int setUp(int initialCapacity) {
\r
156 capacity = super.setUp(initialCapacity);
\r
157 //noinspection unchecked
\r
158 _values = (V[]) new Object[capacity];
\r
163 * Inserts a key/value pair into the map.
\r
165 * @param key an <code>Object</code> value
\r
166 * @param value an <code>Object</code> value
\r
167 * @return the previous value associated with <tt>key</tt>,
\r
168 * or {@code null} if none was found.
\r
170 public V put(K key, V value) {
\r
171 int index = insertionIndex(key);
\r
172 return doPut(key, value, index);
\r
175 public V put(K key, V value, int hash) {
\r
176 int index = insertionIndex(key, hash);
\r
177 return doPut(key, value, index);
\r
181 * Inserts a key/value pair into the map if the specified key is not already
\r
182 * associated with a value.
\r
184 * @param key an <code>Object</code> value
\r
185 * @param value an <code>Object</code> value
\r
186 * @return the previous value associated with <tt>key</tt>,
\r
187 * or {@code null} if none was found.
\r
189 public V putIfAbsent(K key, V value) {
\r
190 int index = insertionIndex(key);
\r
192 return _values[-index - 1];
\r
193 return doPut(key, value, index);
\r
196 private V doPut(K key, V value, int index) {
\r
199 boolean isNewMapping = true;
\r
202 previous = _values[index];
\r
203 isNewMapping = false;
\r
205 oldKey = _set[index];
\r
207 _values[index] = value;
\r
208 if (isNewMapping) {
\r
209 postInsertHook(oldKey == FREE);
\r
216 * Compares this map with another map for equality of their stored
\r
219 * @param other an <code>Object</code> value
\r
220 * @return a <code>boolean</code> value
\r
222 public boolean equals(Object other) {
\r
223 if (! (other instanceof Map)) {
\r
226 Map<K, V> that = (Map<K, V>)other;
\r
227 if (that.size() != this.size()) {
\r
230 return forEachEntry(new EqProcedure<K,V>(that));
\r
233 public int hashCode() {
\r
234 HashProcedure p = new HashProcedure();
\r
236 return p.getHashCode();
\r
239 public String toString() {
\r
240 final StringBuilder buf = new StringBuilder("{");
\r
241 forEachEntry(new TObjectObjectProcedure<K,V>() {
\r
242 private boolean first = true;
\r
243 public boolean execute(K key, V value) {
\r
244 if ( first ) first = false;
\r
245 else buf.append( "," );
\r
254 return buf.toString();
\r
257 private final class HashProcedure implements TObjectObjectProcedure<K,V> {
\r
260 public int getHashCode() {
\r
264 public final boolean execute(K key, V value) {
\r
265 h += _hashingStrategy.computeHashCode(key) ^ (value == null ? 0 : value.hashCode());
\r
270 private static final class EqProcedure<K,V> implements TObjectObjectProcedure<K,V> {
\r
271 private final Map<K,V> _otherMap;
\r
273 EqProcedure(Map<K,V> otherMap) {
\r
274 _otherMap = otherMap;
\r
277 public final boolean execute(K key, V value) {
\r
278 // Check to make sure the key is there. This avoids problems that come up with
\r
279 // null values. Since it is only caused in that cause, only do this when the
\r
280 // value is null (to avoid extra work).
\r
281 if (value == null && !_otherMap.containsKey(key)) return false;
\r
283 V oValue = _otherMap.get(key);
\r
284 return oValue == value || (oValue != null && oValue.equals(value));
\r
289 * Executes <tt>procedure</tt> for each key in the map.
\r
291 * @param procedure a <code>TObjectProcedure</code> value
\r
292 * @return false if the loop over the keys terminated because
\r
293 * the procedure returned false for some key.
\r
295 public boolean forEachKey(TObjectProcedure<K> procedure) {
\r
296 return forEach(procedure);
\r
300 * Executes <tt>procedure</tt> for each value in the map.
\r
302 * @param procedure a <code>TObjectProcedure</code> value
\r
303 * @return false if the loop over the values terminated because
\r
304 * the procedure returned false for some value.
\r
306 public boolean forEachValue(TObjectProcedure<V> procedure) {
\r
307 V[] values = _values;
\r
308 Object[] set = _set;
\r
309 for (int i = values.length; i-- > 0;) {
\r
311 && set[i] != REMOVED
\r
312 && ! procedure.execute(values[i])) {
\r
320 * Executes <tt>procedure</tt> for each key/value entry in the
\r
323 * @param procedure a <code>TObjectObjectProcedure</code> value
\r
324 * @return false if the loop over the entries terminated because
\r
325 * the procedure returned false for some entry.
\r
327 public boolean forEachEntry(TObjectObjectProcedure<K,V> procedure) {
\r
328 Object[] keys = _set;
\r
329 V[] values = _values;
\r
330 for (int i = keys.length; i-- > 0;) {
\r
331 if (keys[i] != FREE
\r
332 && keys[i] != REMOVED
\r
333 && ! procedure.execute((K) keys[i],values[i])) {
\r
341 * Retains only those entries in the map for which the procedure
\r
342 * returns a true value.
\r
344 * @param procedure determines which entries to keep
\r
345 * @return true if the map was modified.
\r
347 public boolean retainEntries(TObjectObjectProcedure<K,V> procedure) {
\r
348 boolean modified = false;
\r
349 Object[] keys = _set;
\r
350 V[] values = _values;
\r
352 // Temporarily disable compaction. This is a fix for bug #1738760
\r
353 tempDisableAutoCompaction();
\r
355 for (int i = keys.length; i-- > 0;) {
\r
356 if (keys[i] != FREE
\r
357 && keys[i] != REMOVED
\r
358 && ! procedure.execute((K) keys[i],values[i])) {
\r
365 reenableAutoCompaction(true);
\r
372 * Transform the values in this map using <tt>function</tt>.
\r
374 * @param function a <code>TObjectFunction</code> value
\r
376 public void transformValues(TObjectFunction<V,V> function) {
\r
377 V[] values = _values;
\r
378 Object[] set = _set;
\r
379 for (int i = values.length; i-- > 0;) {
\r
380 if (set[i] != FREE && set[i] != REMOVED) {
\r
381 values[i] = function.execute(values[i]);
\r
387 * rehashes the map to the new capacity.
\r
389 * @param newCapacity an <code>int</code> value
\r
391 protected void rehash(int newCapacity) {
\r
392 int oldCapacity = _set.length;
\r
393 Object oldKeys[] = _set;
\r
394 V oldVals[] = _values;
\r
396 _set = new Object[newCapacity];
\r
397 Arrays.fill(_set, FREE);
\r
398 _values = (V[]) new Object[newCapacity];
\r
400 for (int i = oldCapacity; i-- > 0;) {
\r
401 if(oldKeys[i] != FREE && oldKeys[i] != REMOVED) {
\r
402 Object o = oldKeys[i];
\r
403 int index = insertionIndex((K) o);
\r
405 throwObjectContractViolation(_set[(-index -1)], o);
\r
408 _values[index] = oldVals[i];
\r
414 * retrieves the value for <tt>key</tt>
\r
416 * @param key an <code>Object</code> value
\r
417 * @return the value of <tt>key</tt> or null if no such mapping exists.
\r
419 public V get(Object key) {
\r
420 Object[] set = _set;
\r
421 V[] values = _values;
\r
422 int index = index((K) key, set);
\r
423 return index < 0 ? null : values[index];
\r
426 public V get(Object key, int hash) {
\r
427 Object[] set = _set;
\r
428 V[] values = _values;
\r
429 int index = index((K) key, hash, set);
\r
430 return index < 0 ? null : values[index];
\r
437 public void clear() {
\r
438 if (size() == 0) return; // optimization
\r
442 Arrays.fill(_set, 0, _set.length, FREE);
\r
443 Arrays.fill(_values, 0, _values.length, null);
\r
447 * Deletes a key/value pair from the map.
\r
449 * @param key an <code>Object</code> value
\r
450 * @return an <code>Object</code> value
\r
452 public V remove(Object key) {
\r
453 Object[] set = _set;
\r
454 V[] values = _values;
\r
456 int index = index((K) key, set);
\r
458 prev = values[index];
\r
459 removeAt(index); // clear key,state; adjust size
\r
464 protected void removeAt(int index) {
\r
465 _values[index] = null;
\r
466 super.removeAt(index); // clear key, state; adjust size
\r
470 * removes the mapping at <tt>index</tt> from the map.
\r
472 * @param index an <code>int</code> value
\r
474 protected void removeAt(int index, V[] values) {
\r
475 values[index] = null;
\r
476 super.removeAt(index); // clear key, state; adjust size
\r
479 public void values(int level, CacheCollectionResult result) {
\r
481 for (int i = _set.length; i-- > 0;) {
\r
482 if(_set[i] != null && _set[i] != REMOVED && _set[i] != FREE) {
\r
483 CacheEntryBase e = (CacheEntryBase)_values[i];
\r
484 if(e.getLevel() <= level)
\r
492 * Returns a view on the values of the map.
\r
494 * @return a <code>Collection</code> value
\r
496 public Collection<V> values() {
\r
498 Collection<V> result = new ArrayList<V>();
\r
500 for (int i = _set.length; i-- > 0;) {
\r
501 if(_set[i] != null && _set[i] != REMOVED && _set[i] != FREE) {
\r
502 result.add((V)_values[i]);
\r
511 * returns a Set view on the keys of the map.
\r
513 * @return a <code>Set</code> value
\r
515 public Set<K> keySet() {
\r
516 throw new UnsupportedOperationException();
\r
520 * Returns a Set view on the entries of the map.
\r
522 * @return a <code>Set</code> value
\r
524 public Set<Map.Entry<K,V>> entrySet() {
\r
525 throw new UnsupportedOperationException();
\r
529 * checks for the presence of <tt>val</tt> in the values of the map.
\r
531 * @param val an <code>Object</code> value
\r
532 * @return a <code>boolean</code> value
\r
534 public boolean containsValue(Object val) {
\r
535 Object[] set = _set;
\r
536 V[] vals = _values;
\r
538 // special case null values so that we don't have to
\r
539 // perform null checks before every call to equals()
\r
541 for (int i = vals.length; i-- > 0;) {
\r
542 if ((set[i] != FREE && set[i] != REMOVED) &&
\r
548 for (int i = vals.length; i-- > 0;) {
\r
549 if ((set[i] != FREE && set[i] != REMOVED) &&
\r
550 (val == vals[i] || val.equals(vals[i]))) {
\r
559 * checks for the present of <tt>key</tt> in the keys of the map.
\r
561 * @param key an <code>Object</code> value
\r
562 * @return a <code>boolean</code> value
\r
564 public boolean containsKey(Object key) {
\r
565 return contains(key);
\r
569 * copies the key/value mappings in <tt>map</tt> into this map.
\r
571 * @param map a <code>Map</code> value
\r
573 public void putAll(Map<? extends K, ? extends V> map) {
\r
574 ensureCapacity(map.size());
\r
575 // could optimize this for cases when map instanceof THashMap
\r
576 for (Iterator<? extends Map.Entry<? extends K,? extends V>> i = map.entrySet().iterator(); i.hasNext();) {
\r
577 Map.Entry<? extends K,? extends V> e = i.next();
\r
578 put(e.getKey(),e.getValue());
\r