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