All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.carrotsearch.hppcrt.maps.ObjectCharOpenIdentityHashMap Maven / Gradle / Ivy

package com.carrotsearch.hppcrt.maps;

import java.util.*;

import com.carrotsearch.hppcrt.*;
import com.carrotsearch.hppcrt.cursors.*;
import com.carrotsearch.hppcrt.predicates.*;
import com.carrotsearch.hppcrt.procedures.*;

  
  
  
//If SA is defined, no allocated array is used but instead default sentinel values
  
/**
 * An identity hash map of KType to char, implemented using open
 * addressing with linear probing for collision resolution.
 *
 * The difference with {@link ObjectCharOpenHashMap} is that it uses direct Object reference equality for comparison and
 * direct "address" {@link System#identityHashCode(Object)} for hashCode(), instead of using
 * the built-in hashCode() /  equals().
 * The internal buffers of this implementation ({@link #keys},{@link #values}, etc...)
 * are always allocated to the nearest size that is a power of two. When
 * the capacity exceeds the given load factor, the buffer size is doubled.
 * 

* *

This implementation supports null keys.

* * @author This code is inspired by the collaboration and implementation in the fastutil project. * */ @javax.annotation.Generated(date = "2014-10-25T20:54:10+0200", value = "HPPC-RT generated from: ObjectCharOpenIdentityHashMap.java") public class ObjectCharOpenIdentityHashMap implements ObjectCharMap, Cloneable { /** * Minimum capacity for the map. */ public final static int MIN_CAPACITY = HashContainerUtils.MIN_CAPACITY; /** * Default capacity. */ public final static int DEFAULT_CAPACITY = HashContainerUtils.DEFAULT_CAPACITY; /** * Default load factor. */ public final static float DEFAULT_LOAD_FACTOR = HashContainerUtils.DEFAULT_LOAD_FACTOR; protected char defaultValue = ('\u0000'); /** * Hash-indexed array holding all keys. *

Important! * The actual value in this field is always an instance of Object[]. * Be warned that javac emits additional casts when keys * are directly accessed; these casts * may result in exceptions at runtime. A workaround is to cast directly to * Object[] before accessing the buffer's elements (although it is highly * recommended to use a {@link #iterator()} instead. *

*

* Direct map iteration: iterate {keys[i], values[i]} for i in [0; keys.length[ where keys[i] != null, then also * {null, {@link #defaultKeyValue} } is in the map if {@link #allocatedDefaultKey} = true. *

* *

Direct iteration warning: * If the iteration goal is to fill another hash container, please iterate {@link #keys} in reverse to prevent performance losses. * @see #values */ public KType[] keys; /** * Hash-indexed array holding all values associated to the keys * stored in {@link #keys}. *

Important! * The actual value in this field is always an instance of Object[]. * Be warned that javac emits additional casts when values * are directly accessed; these casts * may result in exceptions at runtime. A workaround is to cast directly to * Object[] before accessing the buffer's elements (although it is highly * recommended to use a {@link #iterator()} instead. * * * @see #keys */ public char[] values; /** * True if key = null is in the map. */ public boolean allocatedDefaultKey = false; /** * if allocatedDefaultKey = true, contains the associated V to the key = null */ public char defaultKeyValue; /** * Cached number of assigned slots in {@link #keys}. */ protected int assigned; /** * The load factor for this map (fraction of allocated slots * before the buffers must be rehashed or reallocated). */ protected final float loadFactor; /** * Resize buffers when {@link #keys} hits this value. */ protected int resizeAt; /** * The most recent slot accessed in {@link #containsKey} (required for * {@link #lget}). * * @see #containsKey * @see #lget */ protected int lastSlot; /** * Creates a hash map with the default capacity of {@value #DEFAULT_CAPACITY}, * load factor of {@value #DEFAULT_LOAD_FACTOR}. * *

See class notes about hash distribution importance.

*/ public ObjectCharOpenIdentityHashMap() { this(ObjectCharOpenIdentityHashMap.DEFAULT_CAPACITY); } /** * Creates a hash map with the given initial capacity, default load factor of * {@value #DEFAULT_LOAD_FACTOR}. * * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). */ public ObjectCharOpenIdentityHashMap(final int initialCapacity) { this(initialCapacity, ObjectCharOpenIdentityHashMap.DEFAULT_LOAD_FACTOR); } /** * Creates a hash map with the given initial capacity, * load factor. * * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). * * @param loadFactor The load factor (greater than zero and smaller than 1). * * */ public ObjectCharOpenIdentityHashMap(final int initialCapacity, final float loadFactor) { assert loadFactor > 0 && loadFactor <= 1 : "Load factor must be between (0, 1]."; this.loadFactor = loadFactor; //take into account of the load factor to garantee no reallocations before reaching initialCapacity. int internalCapacity = (int) (initialCapacity / loadFactor) + ObjectCharOpenIdentityHashMap.MIN_CAPACITY; //align on next power of two internalCapacity = HashContainerUtils.roundCapacity(internalCapacity); this.keys = Internals.newArray(internalCapacity); this.values = new char [internalCapacity]; //fill with "not allocated" value //Take advantage of the rounding so that the resize occur a bit later than expected. //allocate so that there is at least one slot that remains allocated = false //this is compulsory to guarantee proper stop in searching loops this.resizeAt = Math.max(3, (int) (internalCapacity * loadFactor)) - 2; } /** * Create a hash map from all key-value pairs of another container. */ public ObjectCharOpenIdentityHashMap(final ObjectCharAssociativeContainer container) { this(container.size()); putAll(container); } /** * {@inheritDoc} */ @Override public char put(final KType key, final char value) { if (((key) == (null))) { if (this.allocatedDefaultKey) { char previousValue = this.defaultKeyValue; this.defaultKeyValue = value; return previousValue; } this.defaultKeyValue = value; this.allocatedDefaultKey = true; return this.defaultValue; } final int mask = this.keys.length - 1; int slot = Internals.rehash(System.identityHashCode(key)) & mask; final KType[] keys = this.keys; final char[] values = this.values; while ((! ((keys[ slot]) == (null)))) { if (key == keys[slot]) { final char oldValue = values[slot]; values[slot] = value; return oldValue; } slot = (slot + 1) & mask; } // Check if we need to grow. If so, reallocate new data, fill in the last element // and rehash. if (this.assigned == this.resizeAt) { expandAndPut(key, value, slot); } else { this.assigned++; keys[slot] = key; values[slot] = value; } return this.defaultValue; } /** * {@inheritDoc} */ @Override public int putAll(final ObjectCharAssociativeContainer container) { return putAll((Iterable>) container); } /** * {@inheritDoc} */ @Override public int putAll(final Iterable> iterable) { final int count = this.size(); for (final ObjectCharCursor c : iterable) { put(c.key, c.value); } return this.size() - count; } /** * {@inheritDoc} */ @Override public boolean putIfAbsent(final KType key, final char value) { if (!containsKey(key)) { put(key, value); return true; } return false; } /** * Trove-inspired API method. An equivalent * of the following code: *
     *  if (containsKey(key))
     *  {
     *      char v = (char) (lget() + additionValue);
     *      lset(v);
     *      return v;
     *  }
     *  else
     *  {
     *     put(key, putValue);
     *     return putValue;
     *  }
     * 
* * @param key The key of the value to adjust. * @param putValue The value to put if key does not exist. * @param additionValue The value to add to the existing value if key exists. * @return Returns the current value associated with key (after changes). */ @Override public char putOrAdd(KType key, char putValue, char additionValue) { if (((key) == (null))) { if (this.allocatedDefaultKey) { this.defaultKeyValue += additionValue; return this.defaultKeyValue; } this.defaultKeyValue = putValue; this.allocatedDefaultKey = true; return putValue; } final int mask = this.keys.length - 1; int slot = Internals.rehash(System.identityHashCode(key)) & mask; final KType[] keys = this.keys; final char[] values = this.values; while ((! ((keys[ slot]) == (null)))) { if (key == keys[slot]) { values[slot] += additionValue; return values[slot]; } slot = (slot + 1) & mask; } if (assigned == resizeAt) { expandAndPut(key, putValue, slot); } else { assigned++; keys[slot] = key; values[slot] = putValue; } return putValue; } /** * An equivalent of calling *
     *  if (containsKey(key))
     *  {
     *      char v = (char) (lget() + additionValue);
     *      lset(v);
     *      return v;
     *  }
     *  else
     *  {
     *     put(key, additionValue);
     *     return additionValue;
     *  }
     * 
* * @param key The key of the value to adjust. * @param additionValue The value to put or add to the existing value if key exists. * @return Returns the current value associated with key (after changes). */ @Override public char addTo(KType key, char additionValue) { return putOrAdd(key, additionValue, additionValue); } /** * Expand the internal storage buffers (capacity) and rehash. */ private void expandAndPut(final KType pendingKey, final char pendingValue, final int freeSlot) { assert this.assigned == this.resizeAt; //default sentinel value is never in the keys[] array, so never trigger reallocs assert !((pendingKey) == (null)); // Try to allocate new buffers first. If we OOM, it'll be now without // leaving the data structure in an inconsistent state. final KType[] oldKeys = this.keys; final char[] oldValues = this.values; allocateBuffers(HashContainerUtils.nextCapacity(this.keys.length)); // We have succeeded at allocating new data so insert the pending key/value at // the free slot in the old arrays before rehashing. this.lastSlot = -1; this.assigned++; oldKeys[freeSlot] = pendingKey; oldValues[freeSlot] = pendingValue; //for inserts final int mask = this.keys.length - 1; KType key = null; char value = ('\u0000'); int slot = -1; final KType[] keys = this.keys; final char[] values = this.values; //iterate all the old arrays to add in the newly allocated buffers //It is important to iterate backwards to minimize the conflict chain length ! for (int i = oldKeys.length; --i >= 0;) { if ((! ((oldKeys[ i]) == (null)))) { key = oldKeys[i]; value = oldValues[i]; slot = Internals.rehash(System.identityHashCode(key)) & mask; while ((! ((keys[ slot]) == (null)))) { slot = (slot + 1) & mask; } //end while keys[slot] = key; values[slot] = value; } } } /** * Allocate internal buffers for a given capacity. * * @param capacity New capacity (must be a power of two). */ private void allocateBuffers(final int capacity) { final KType[] keys = Internals.newArray(capacity); final char[] values = new char [capacity]; this.keys = keys; this.values = values; //allocate so that there is at least one slot that remains allocated = false //this is compulsory to guarantee proper stop in searching loops this.resizeAt = Math.max(3, (int) (capacity * this.loadFactor)) - 2; } /** * {@inheritDoc} */ @Override public char remove(final KType key) { if (((key) == (null))) { if (this.allocatedDefaultKey) { char previousValue = this.defaultKeyValue; this.allocatedDefaultKey = false; return previousValue; } return this.defaultValue; } final int mask = this.keys.length - 1; int slot = Internals.rehash(System.identityHashCode(key)) & mask; final KType[] keys = this.keys; while ((! ((keys[ slot]) == (null)))) { if (key == keys[slot]) { final char value = this.values[slot]; this.assigned--; shiftConflictingKeys(slot); return value; } slot = (slot + 1) & mask; } //end while true return this.defaultValue; } /** * Shift all the slot-conflicting keys allocated to (and including) slot. */ protected void shiftConflictingKeys(int slotCurr) { // Copied nearly verbatim from fastutil's impl. final int mask = this.keys.length - 1; int slotPrev, slotOther; final KType[] keys = this.keys; final char[] values = this.values; while (true) { slotCurr = ((slotPrev = slotCurr) + 1) & mask; while ((! ((keys[ slotCurr]) == (null)))) { slotOther = (Internals.rehash(System.identityHashCode(keys[slotCurr])) & mask); if (slotPrev <= slotCurr) { // we're on the right of the original slot. if (slotPrev >= slotOther || slotOther > slotCurr) { break; } } else { // we've wrapped around. if (slotPrev >= slotOther && slotOther > slotCurr) { break; } } slotCurr = (slotCurr + 1) & mask; } if (!(! ((keys[ slotCurr]) == (null)))) { break; } // Shift key/value/allocated triplet. keys[slotPrev] = keys[slotCurr]; values[slotPrev] = values[slotCurr]; } //means not allocated /* */ keys[slotPrev] = null; /* */ /* */ } /** * {@inheritDoc} */ @Override public int removeAll(final ObjectContainer container) { final int before = this.size(); for (final ObjectCursor cursor : container) { remove(cursor.value); } return before - this.size(); } /** * {@inheritDoc} *

Important! * If the predicate actually injects the removed keys in another hash container, you may experience performance losses. */ @Override public int removeAll(final ObjectPredicate predicate) { final int before = this.size(); if (this.allocatedDefaultKey) { if (predicate.apply(null)) { this.allocatedDefaultKey = false; } } final KType[] keys = this.keys; for (int i = 0; i < keys.length;) { if ((! ((keys[ i]) == (null)))) { if (predicate.apply(keys[i])) { this.assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - this.size(); } /** * {@inheritDoc} * *

Use the following snippet of code to check for key existence * first and then retrieve the value if it exists.

*
     * if (map.containsKey(key))
     *   value = map.lget();
     * 
*/ @Override public char get(final KType key) { if (((key) == (null))) { if (this.allocatedDefaultKey) { return this.defaultKeyValue; } return this.defaultValue; } final int mask = this.keys.length - 1; int slot = Internals.rehash(System.identityHashCode(key)) & mask; final KType[] keys = this.keys; while ((! ((keys[ slot]) == (null)))) { if (key == keys[slot]) { return this.values[slot]; } slot = (slot + 1) & mask; } //end while true return this.defaultValue; } /** * Returns the last key stored in this has map for the corresponding * most recent call to {@link #containsKey}. * Precondition : {@link #containsKey} must have been called previously ! *

Use the following snippet of code to check for key existence * first and then retrieve the key value if it exists.

*
     * if (map.containsKey(key))
     *   value = map.lkey();
     * 
* *

This is equivalent to calling:

*
     * if (map.containsKey(key))
     *   key = map.keys[map.lslot()];
     * 
*/ public KType lkey() { if (this.lastSlot == -2) { return null; } assert this.lastSlot >= 0 : "Call containsKey() first."; assert ! ((this.keys[lastSlot]) == (null)) : "Last call to exists did not have any associated value."; return this.keys[this.lastSlot]; } /** * Returns the last value saved in a call to {@link #containsKey}. * Precondition : {@link #containsKey} must have been called previously ! * @see #containsKey */ public char lget() { if (this.lastSlot == -2) { return this.defaultKeyValue; } assert this.lastSlot >= 0 : "Call containsKey() first."; assert ! ((this.keys[lastSlot]) == (null)) : "Last call to exists did not have any associated value."; return this.values[this.lastSlot]; } /** * Sets the value corresponding to the key saved in the last * call to {@link #containsKey}, if and only if the key exists * in the map already. * Precondition : {@link #containsKey} must have been called previously ! * @see #containsKey * @return Returns the previous value stored under the given key. */ public char lset(final char value) { if (this.lastSlot == -2) { char previous = this.defaultKeyValue; this.defaultKeyValue = value; return previous; } assert this.lastSlot >= 0 : "Call containsKey() first."; assert ! ((this.keys[lastSlot]) == (null)) : "Last call to exists did not have any associated value."; final char previous = this.values[this.lastSlot]; this.values[this.lastSlot] = value; return previous; } /** * @return Returns the slot of the last key looked up in a call to {@link #containsKey} if * it returned true. * * @see #containsKey */ public int lslot() { assert this.lastSlot >= 0 || this.lastSlot == -2 : "Call containsKey() first."; return this.lastSlot; } /** * {@inheritDoc} * *

Saves the associated value for fast access using {@link #lget} * or {@link #lset}.

*
     * if (map.containsKey(key))
     *   value = map.lget();
     * 
* or, to modify the value at the given key without looking up * its slot twice: *
     * if (map.containsKey(key))
     *   map.lset(map.lget() + 1);
     * 
* or, to retrieve the key-equivalent object from the map: *
     * if (map.containsKey(key))
     *   map.lkey();
     * 
*/ @Override public boolean containsKey(final KType key) { if (((key) == (null))) { if (this.allocatedDefaultKey) { this.lastSlot = -2; } else { this.lastSlot = -1; } return this.allocatedDefaultKey; } final int mask = this.keys.length - 1; int slot = Internals.rehash(System.identityHashCode(key)) & mask; final KType[] keys = this.keys; while ((! ((keys[ slot]) == (null)))) { if (key == keys[slot]) { this.lastSlot = slot; return true; } slot = (slot + 1) & mask; } //end while true //unsuccessful search this.lastSlot = -1; return false; } /** * {@inheritDoc} * *

Does not release internal buffers.

*/ @Override public void clear() { this.assigned = 0; this.lastSlot = -1; // States are always cleared. //Faster than Arrays.fill(keys, null); // Help the GC. ObjectArrays.blankArray(this.keys, 0, this.keys.length); this.allocatedDefaultKey = false; } /** * {@inheritDoc} */ @Override public int size() { return this.assigned + (this.allocatedDefaultKey?1:0) ; } /** * {@inheritDoc} */ @Override public int capacity() { return this.resizeAt - 1; } /** * {@inheritDoc} * *

Note that an empty container may still contain many deleted keys (that occupy buffer * space). Adding even a single element to such a container may cause rehashing.

*/ @Override public boolean isEmpty() { return size() == 0; } /** * {@inheritDoc} */ @Override public int hashCode() { int h = 0; if (this.allocatedDefaultKey) { h += Internals.rehash(null) + Internals.rehash(this.defaultKeyValue); } final KType[] keys = this.keys; final char[] values = this.values; for (int i = keys.length; --i >= 0;) { if ((! ((keys[ i]) == (null)))) { //This hash is an intrinsic property of the container contents h += Internals.rehash(System.identityHashCode(keys[i])) + Internals.rehash(values[i]); } } return h; } /** * this instance and obj can only be equal if :
     * (both are ObjectCharOpenCustomHashMap)
     * and
     * (both have equal hash strategies defined by {@link #ObjectHashingStrategy}.equals(obj.hashStrategy))
* then, both maps are compared using their {@link #ObjectHashingStrategy}. */ @Override public boolean equals(final Object obj) { if (obj != null) { if (obj == this) { return true; } if (!(obj instanceof ObjectCharOpenIdentityHashMap)) { return false; } @SuppressWarnings("unchecked") final ObjectCharOpenIdentityHashMap other = (ObjectCharOpenIdentityHashMap) obj; if (other.size() == this.size()) { final EntryIterator it = this.iterator(); while (it.hasNext()) { final ObjectCharCursor c = it.next(); if (other.containsKey(c.key)) { final char v = other.get(c.key); if (((c.value) == (v))) { continue; } } //recycle it.release(); return false; } return true; } } return false; } /** * An iterator implementation for {@link #iterator}. */ public final class EntryIterator extends AbstractIterator> { public final ObjectCharCursor cursor; public EntryIterator() { this.cursor = new ObjectCharCursor(); this.cursor.index = -2; } /** * Iterate backwards w.r.t the buffer, to * minimize collision chains when filling another hash container (ex. with putAll()) */ @Override protected ObjectCharCursor fetch() { if (this.cursor.index == ObjectCharOpenIdentityHashMap.this.keys.length + 1) { if (ObjectCharOpenIdentityHashMap.this.allocatedDefaultKey) { this.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length; this.cursor.key = null; this.cursor.value = ObjectCharOpenIdentityHashMap.this.defaultKeyValue; return this.cursor; } else { //no value associated with the default key, continue iteration... this.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length; } } int i = this.cursor.index - 1; while (i >= 0 && !(! ((ObjectCharOpenIdentityHashMap.this.keys[ i]) == (null)))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.key = ObjectCharOpenIdentityHashMap.this.keys[i]; this.cursor.value = ObjectCharOpenIdentityHashMap.this.values[i]; return this.cursor; } } /** * internal pool of EntryIterator */ protected final IteratorPool, EntryIterator> entryIteratorPool = new IteratorPool, EntryIterator>( new ObjectFactory() { @Override public EntryIterator create() { return new EntryIterator(); } @Override public void initialize(final EntryIterator obj) { obj.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length +1 ; } @Override public void reset(final EntryIterator obj) { // nothing } }); /** * {@inheritDoc} * @return */ @Override public EntryIterator iterator() { //return new EntryIterator(); return this.entryIteratorPool.borrow(); } /** * {@inheritDoc} */ @Override public > T forEach(final T procedure) { if (this.allocatedDefaultKey) { procedure.apply(null, this.defaultKeyValue); } final KType[] keys = this.keys; final char[] values = this.values; //Iterate in reverse for side-stepping the longest conflict chain //in another hash, in case apply() is actually used to fill another hash container. for (int i = keys.length - 1; i >= 0; i--) { if ((! ((keys[ i]) == (null)))) { procedure.apply(keys[i], values[i]); } } return procedure; } /** * {@inheritDoc} */ @Override public > T forEach(final T predicate) { if (this.allocatedDefaultKey) { if(! predicate.apply(null, this.defaultKeyValue)) { return predicate; } } final KType[] keys = this.keys; final char[] values = this.values; //Iterate in reverse for side-stepping the longest conflict chain //in another hash, in case apply() is actually used to fill another hash container. for (int i = keys.length - 1; i >= 0; i--) { if ((! ((keys[ i]) == (null)))) { if (!predicate.apply(keys[i], values[i])) { break; } } } //end for return predicate; } /** * @return a new KeysContainer view of the keys of this associated container. * This view then reflects all changes from the map. */ @Override public KeysContainer keys() { return new KeysContainer(); } /** * A view of the keys inside this hash map. */ public final class KeysContainer extends AbstractObjectCollection implements ObjectLookupContainer { private final ObjectCharOpenIdentityHashMap owner = ObjectCharOpenIdentityHashMap.this; @Override public boolean contains(final KType e) { return containsKey(e); } @Override public > T forEach(final T procedure) { if (this.owner.allocatedDefaultKey) { procedure.apply(null); } final KType[] keys = this.owner.keys; //Iterate in reverse for side-stepping the longest conflict chain //in another hash, in case apply() is actually used to fill another hash container. for (int i = keys.length - 1; i >= 0; i--) { if ((! ((keys[ i]) == (null)))) { procedure.apply(keys[i]); } } return procedure; } @Override public > T forEach(final T predicate) { if (this.owner.allocatedDefaultKey) { if(! predicate.apply(null)) { return predicate; } } final KType[] keys = this.owner.keys; //Iterate in reverse for side-stepping the longest conflict chain //in another hash, in case apply() is actually used to fill another hash container. for (int i = keys.length - 1; i >= 0; i--) { if ((! ((keys[ i]) == (null)))) { if (!predicate.apply(keys[i])) { break; } } } return predicate; } /** * {@inheritDoc} */ @Override public KeysIterator iterator() { //return new KeysIterator(); return this.keyIteratorPool.borrow(); } /** * {@inheritDoc} */ @Override public int size() { return this.owner.size(); } /** * {@inheritDoc} */ @Override public int capacity() { return this.owner.capacity(); } @Override public void clear() { this.owner.clear(); } @Override public int removeAll(final ObjectPredicate predicate) { return this.owner.removeAll(predicate); } @Override public int removeAllOccurrences(final KType e) { final boolean hasKey = this.owner.containsKey(e); int result = 0; if (hasKey) { this.owner.remove(e); result = 1; } return result; } /** * internal pool of KeysIterator */ protected final IteratorPool, KeysIterator> keyIteratorPool = new IteratorPool, KeysIterator>( new ObjectFactory() { @Override public KeysIterator create() { return new KeysIterator(); } @Override public void initialize(final KeysIterator obj) { obj.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length +1 ; } @Override public void reset(final KeysIterator obj) { // nothing } }); @Override public KType[] toArray(final KType[] target) { int count = 0; if (this.owner.allocatedDefaultKey) { target[count++] = null; } final KType[] keys = this.owner.keys; for (int i = 0; i < keys.length; i++) { if ((! ((keys[ i]) == (null)))) { target[count++] = keys[i]; } } assert count == this.owner.size(); return target; } }; /** * An iterator over the set of assigned keys. */ public final class KeysIterator extends AbstractIterator> { public final ObjectCursor cursor; public KeysIterator() { this.cursor = new ObjectCursor(); this.cursor.index = -2; } /** * Iterate backwards w.r.t the buffer, to * minimize collision chains when filling another hash container (ex. with putAll()) */ @Override protected ObjectCursor fetch() { if (this.cursor.index == ObjectCharOpenIdentityHashMap.this.keys.length + 1) { if (ObjectCharOpenIdentityHashMap.this.allocatedDefaultKey) { this.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length; this.cursor.value = null; return this.cursor; } else { //no value associated with the default key, continue iteration... this.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length; } } int i = this.cursor.index - 1; while (i >= 0 && !(! ((ObjectCharOpenIdentityHashMap.this.keys[ i]) == (null)))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = ObjectCharOpenIdentityHashMap.this.keys[i]; return this.cursor; } } /** * @return a new ValuesContainer, view of the values of this map. * This view then reflects all changes from the map. */ @Override public ValuesContainer values() { return new ValuesContainer(); } /** * A view over the set of values of this map. */ public final class ValuesContainer extends AbstractCharCollection { private final ObjectCharOpenIdentityHashMap owner = ObjectCharOpenIdentityHashMap.this; /** * {@inheritDoc} */ @Override public int size() { return this.owner.size(); } /** * {@inheritDoc} */ @Override public int capacity() { return this.owner.capacity(); } @Override public boolean contains(final char value) { if (this.owner.allocatedDefaultKey && ((value) == (this.owner.defaultKeyValue))) { return true; } // This is a linear scan over the values, but it's in the contract, so be it. final KType[] keys = this.owner.keys; final char[] values = this.owner.values; for (int slot = 0; slot < keys.length; slot++) { if ((! ((keys[ slot]) == (null))) && ((value) == (values[slot]))) { return true; } } return false; } @Override public T forEach(final T procedure) { if (this.owner.allocatedDefaultKey) { procedure.apply(this.owner.defaultKeyValue); } final KType[] keys = this.owner.keys; final char[] values = this.owner.values; for (int slot = 0; slot < keys.length; slot++) { if ((! ((keys[ slot]) == (null)))) { procedure.apply(values[slot]); } } return procedure; } @Override public T forEach(final T predicate) { if (this.owner.allocatedDefaultKey) { if (!predicate.apply(this.owner.defaultKeyValue)) { return predicate; } } final KType[] keys = this.owner.keys; final char[] values = this.owner.values; for (int slot = 0; slot < keys.length; slot++) { if ((! ((keys[ slot]) == (null)))) { if (!predicate.apply(values[slot])) { break; } } } return predicate; } @Override public ValuesIterator iterator() { // return new ValuesIterator(); return this.valuesIteratorPool.borrow(); } /** * {@inheritDoc} * Indeed removes all the (key,value) pairs matching * (key ? , e) with the same e, from the map. */ @Override public int removeAllOccurrences(final char e) { final int before = this.owner.size(); if (this.owner.allocatedDefaultKey) { if(((e) == (this.owner.defaultKeyValue))) { this.owner.allocatedDefaultKey = false; } } final KType[] keys = this.owner.keys; final char[] values = this.owner.values; for (int slot = 0; slot < keys.length;) { if ((! ((keys[ slot]) == (null)))) { if (((e) == (values[slot]))) { this.owner.assigned--; shiftConflictingKeys(slot); // Repeat the check for the same i. continue; } } slot++; } return before - this.owner.size(); } /** * {@inheritDoc} * Indeed removes all the (key,value) pairs matching * the predicate for the values, from the map. */ @Override public int removeAll(final CharPredicate predicate) { final int before = this.owner.size(); if (this.owner.allocatedDefaultKey) { if(predicate.apply(this.owner.defaultKeyValue)) { this.owner.allocatedDefaultKey = false; } } final KType[] keys = this.owner.keys; final char[] values = this.owner.values; for (int slot = 0; slot < keys.length;) { if ((! ((keys[ slot]) == (null)))) { if (predicate.apply(values[slot])) { this.owner.assigned--; shiftConflictingKeys(slot); // Repeat the check for the same i. continue; } } slot++; } return before - this.owner.size(); } /** * {@inheritDoc} * Alias for clear() the whole map. */ @Override public void clear() { this.owner.clear(); } /** * internal pool of ValuesIterator */ protected final IteratorPool valuesIteratorPool = new IteratorPool( new ObjectFactory() { @Override public ValuesIterator create() { return new ValuesIterator(); } @Override public void initialize(final ValuesIterator obj) { obj.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length +1 ; } @Override public void reset(final ValuesIterator obj) { // nothing } }); @Override public char[] toArray(final char[] target) { int count = 0; if (this.owner.allocatedDefaultKey) { target[count++] = this.owner.defaultKeyValue; } final KType[] keys = this.owner.keys; final char[] values = this.owner.values; for (int i = 0; i < values.length; i++) { if ((! ((keys[ i]) == (null)))) { target[count++] = values[i]; } } assert count == this.owner.size(); return target; } } /** * An iterator over the set of values. */ public final class ValuesIterator extends AbstractIterator { public final CharCursor cursor; public ValuesIterator() { this.cursor = new CharCursor(); this.cursor.index = -2; } /** * Iterate backwards w.r.t the buffer, to * minimize collision chains when filling another hash container (ex. with putAll()) */ @Override protected CharCursor fetch() { if (this.cursor.index == ObjectCharOpenIdentityHashMap.this.values.length + 1) { if (ObjectCharOpenIdentityHashMap.this.allocatedDefaultKey) { this.cursor.index = ObjectCharOpenIdentityHashMap.this.values.length; this.cursor.value = ObjectCharOpenIdentityHashMap.this.defaultKeyValue; return this.cursor; } else { //no value associated with the default key, continue iteration... this.cursor.index = ObjectCharOpenIdentityHashMap.this.keys.length; } } int i = this.cursor.index - 1; while (i >= 0 && !(! ((ObjectCharOpenIdentityHashMap.this.keys[ i]) == (null)))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = ObjectCharOpenIdentityHashMap.this.values[i]; return this.cursor; } } /** * Clone this object. * * It also realizes a trim-to- this.size() in the process. * */ @Override public ObjectCharOpenIdentityHashMap clone() { /* */ @SuppressWarnings("unchecked") final/* */ ObjectCharOpenIdentityHashMap cloned = new ObjectCharOpenIdentityHashMap(this.size(), this.loadFactor); cloned.putAll(this); cloned.defaultValue = this.defaultValue; return cloned; } /** * Convert the contents of this map to a human-friendly string. */ @Override public String toString() { final StringBuilder buffer = new StringBuilder(); buffer.append("["); boolean first = true; for (final ObjectCharCursor cursor : this) { if (!first) { buffer.append(", "); } buffer.append(cursor.key); buffer.append("=>"); buffer.append(cursor.value); first = false; } buffer.append("]"); return buffer.toString(); } /** * Creates a hash map from two index-aligned arrays of key-value pairs. */ public static ObjectCharOpenIdentityHashMap from(final KType[] keys, final char[] values) { if (keys.length != values.length) { throw new IllegalArgumentException("Arrays of keys and values must have an identical length."); } final ObjectCharOpenIdentityHashMap map = new ObjectCharOpenIdentityHashMap(keys.length); for (int i = 0; i < keys.length; i++) { map.put(keys[i], values[i]); } return map; } /** * Create a hash map from another associative container. */ public static ObjectCharOpenIdentityHashMap from(final ObjectCharAssociativeContainer container) { return new ObjectCharOpenIdentityHashMap(container); } /** * Create a new hash map without providing the full generic signature (constructor * shortcut). */ public static ObjectCharOpenIdentityHashMap newInstance() { return new ObjectCharOpenIdentityHashMap(); } /** * Create a new hash map with initial capacity and load factor control. (constructor * shortcut). */ public static ObjectCharOpenIdentityHashMap newInstance(final int initialCapacity, final float loadFactor) { return new ObjectCharOpenIdentityHashMap(initialCapacity, loadFactor); } /** * Returns the "default value" value used * in containers methods returning "default value" * @return */ public char getDefaultValue() { return this.defaultValue; } /** * Set the "default value" value to be used * in containers methods returning "default value" * @return */ public void setDefaultValue(final char defaultValue) { this.defaultValue = defaultValue; } //Test for existence with default value sentinels }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy