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

com.carrotsearch.hppc.DoubleCharOpenHashMap Maven / Gradle / Ivy

Go to download

High Performance Primitive Collections: data structures (maps, sets, lists, stacks, queues) generated for combinations of object and primitive types to conserve JVM memory and speed up execution.

There is a newer version: 0.10.0
Show newest version
package com.carrotsearch.hppc;

import java.util.*;

import com.carrotsearch.hppc.cursors.*;
import com.carrotsearch.hppc.hash.MurmurHash3;
import com.carrotsearch.hppc.predicates.*;
import com.carrotsearch.hppc.procedures.*;

import static com.carrotsearch.hppc.Internals.*;

/**
 * A hash map of double to char, implemented using open
 * addressing with linear probing for collision resolution.
 * 
 * 

* The internal buffers of this implementation ({@link #keys}, {@link #values}, * {@link #allocated}) 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. *

* *

See {@link ObjectObjectOpenHashMap} class for API similarities and differences against Java * Collections. * * *

Important node. The implementation uses power-of-two tables and linear * probing, which may cause poor performance (many collisions) if hash values are * not properly distributed. This implementation uses rehashing * using {@link MurmurHash3}.

* * @author This code is inspired by the collaboration and implementation in the fastutil project. */ @javax.annotation.Generated(date = "2011-07-12T16:58:51+0200", value = "HPPC generated from: DoubleCharOpenHashMap.java") public class DoubleCharOpenHashMap implements DoubleCharMap, Cloneable { /** * Default capacity. */ public final static int DEFAULT_CAPACITY = 16; /** * Minimum capacity for the map. */ public final static int MIN_CAPACITY = 4; /** * Default load factor. */ public final static float DEFAULT_LOAD_FACTOR = 0.75f; /** * Hash-indexed array holding all keys. * * @see #values */ public double [] keys; /** * Hash-indexed array holding all values associated to the keys * stored in {@link #keys}. * * @see #keys */ public char [] values; /** * Information if an entry (slot) in the {@link #values} table is allocated * or empty. * * @see #assigned */ public boolean [] allocated; /** * Cached number of assigned slots in {@link #allocated}. */ public int assigned; /** * The load factor for this map (fraction of allocated slots * before the buffers must be rehashed or reallocated). */ public final float loadFactor; /** * Cached capacity threshold at which we must resize the buffers. */ private int resizeThreshold; /** * The most recent slot accessed in {@link #containsKey} (required for * {@link #lget}). * * @see #containsKey * @see #lget */ private 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 DoubleCharOpenHashMap() { this(DEFAULT_CAPACITY); } /** * Creates a hash map with the given initial capacity, default load factor of * {@value #DEFAULT_LOAD_FACTOR}. * *

See class notes about hash distribution importance.

* * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). */ public DoubleCharOpenHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Creates a hash map with the given initial capacity, * load factor. * *

See class notes about hash distribution importance.

* * @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 DoubleCharOpenHashMap(int initialCapacity, float loadFactor) { initialCapacity = Math.max(initialCapacity, MIN_CAPACITY); assert initialCapacity > 0 : "Initial capacity must be between (0, " + Integer.MAX_VALUE + "]."; assert loadFactor > 0 && loadFactor <= 1 : "Load factor must be between (0, 1]."; this.loadFactor = loadFactor; allocateBuffers(roundCapacity(initialCapacity)); } /** * Create a hash map from all key-value pairs of another container. */ public DoubleCharOpenHashMap(DoubleCharAssociativeContainer container) { this((int)(container.size() * (1 + DEFAULT_LOAD_FACTOR))); putAll(container); } /** * {@inheritDoc} */ @Override public char put(double key, char value) { if (assigned >= resizeThreshold) expandAndRehash(); final int mask = allocated.length - 1; int slot = rehash(key) & mask; while (allocated[slot]) { if (((key) == (keys[slot]))) { final char oldValue = values[slot]; values[slot] = value; return oldValue; } slot = (slot + 1) & mask; } assigned++; allocated[slot] = true; keys[slot] = key; values[slot] = value; return ((char) 0); } /** * {@inheritDoc} */ @Override public final int putAll( DoubleCharAssociativeContainer container) { final int count = this.assigned; for (DoubleCharCursor c : container) { put(c.key, c.value); } return this.assigned - count; } /** * Puts all key/value pairs from a given iterable into this map. */ @Override public final int putAll( Iterable iterable) { final int count = this.assigned; for (DoubleCharCursor c : iterable) { put(c.key, c.value); } return this.assigned - count; } /** * Trove-inspired API method. An equivalent * of the following code: *
     * if (!map.containsKey(key)) map.put(value);
     * 
* * @param key The key of the value to check. * @param value The value to put if key does not exist. * @return true if key did not exist and value * was placed in the map. */ public final boolean putIfAbsent(double key, char value) { if (!containsKey(key)) { put(key, value); return true; } return false; } /** * Trove-inspired API method. An equivalent * of the following code: *
     * if (map.containsKey(key)) 
     *    map.lset(map.lget() + additionValue);
     * else
     *    map.put(key, 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). */ public final char putOrAdd(double key, char putValue, char additionValue) { if (assigned >= resizeThreshold) expandAndRehash(); final int mask = allocated.length - 1; int slot = rehash(key) & mask; while (allocated[slot]) { if (((key) == (keys[slot]))) { return values[slot] += additionValue; } slot = (slot + 1) & mask; } assigned++; allocated[slot] = true; keys[slot] = key; char v = values[slot] = putValue; return v; } /** * Expand the internal storage buffers (capacity) or rehash current * keys and values if there are a lot of deleted slots. */ private void expandAndRehash() { final double [] oldKeys = this.keys; final char [] oldValues = this.values; final boolean [] oldStates = this.allocated; assert assigned >= resizeThreshold; allocateBuffers(nextCapacity(keys.length)); /* * Rehash all assigned slots from the old hash table. Deleted * slots are discarded. */ final int mask = allocated.length - 1; for (int i = 0; i < oldStates.length; i++) { if (oldStates[i]) { final double key = oldKeys[i]; final char value = oldValues[i]; /* */ /* */ int slot = rehash(key) & mask; while (allocated[slot]) { if (((key) == (keys[slot]))) { break; } slot = (slot + 1) & mask; } allocated[slot] = true; keys[slot] = key; values[slot] = value; } } /* * The number of assigned items does not change, the number of deleted * items is zero since we have resized. */ lastSlot = -1; } /** * Allocate internal buffers for a given capacity. * * @param capacity New capacity (must be a power of two). */ private void allocateBuffers(int capacity) { this.keys = new double [capacity]; this.values = new char [capacity]; this.allocated = new boolean [capacity]; this.resizeThreshold = (int) (capacity * loadFactor); } /** * {@inheritDoc} */ @Override public char remove(double key) { final int mask = allocated.length - 1; int slot = rehash(key) & mask; while (allocated[slot]) { if (((key) == (keys[slot]))) { assigned--; char v = values[slot]; shiftConflictingKeys(slot); return v; } slot = (slot + 1) & mask; } return ((char) 0); } /** * Shift all the slot-conflicting keys allocated to (and including) slot. */ protected final void shiftConflictingKeys(int slotCurr) { // Copied nearly verbatim from fastutil's impl. final int mask = allocated.length - 1; int slotPrev, slotOther; while (true) { slotCurr = ((slotPrev = slotCurr) + 1) & mask; while (allocated[slotCurr]) { slotOther = rehash(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 (!allocated[slotCurr]) break; // Shift key/value pair. keys[slotPrev] = keys[slotCurr]; values[slotPrev] = values[slotCurr]; } allocated[slotPrev] = false; /* */ /* */ } /** * {@inheritDoc} */ @Override public final int removeAll(DoubleContainer container) { final int before = this.assigned; for (DoubleCursor cursor : container) { remove(cursor.value); } return before - this.assigned; } /** * {@inheritDoc} */ @Override public final int removeAll(DoublePredicate predicate) { final int before = this.assigned; final double [] keys = this.keys; final boolean [] states = this.allocated; for (int i = 0; i < states.length;) { if (states[i]) { if (predicate.apply(keys[i])) { assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - this.assigned; } /** * {@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(double key) { final int mask = allocated.length - 1; int slot = rehash(key) & mask; while (allocated[slot]) { if (((key) == (keys[slot]))) { return values[slot]; } slot = (slot + 1) & mask; } return ((char) 0); } /** * Returns the last value saved in a call to {@link #containsKey}. * * @see #containsKey */ public char lget() { assert lastSlot >= 0 : "Call containsKey() first."; assert allocated[lastSlot] : "Last call to exists did not have any associated value."; return values[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. * * @see #containsKey * @return Returns the previous value stored under the given key. */ public char lset(char key) { assert lastSlot >= 0 : "Call containsKey() first."; assert allocated[lastSlot] : "Last call to exists did not have any associated value."; final char previous = values[lastSlot]; values[lastSlot] = key; return previous; } /** * {@inheritDoc} * *

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

*
     * if (map.containsKey(key))
     *   value = map.lget();
     * 
* or, for example to modify the value at the given key without looking up * its slot twice: *
     * if (map.containsKey(key))
     *   map.lset(map.lget() + 1);
     * 
*/ @Override public boolean containsKey(double key) { final int mask = allocated.length - 1; int slot = rehash(key) & mask; while (allocated[slot]) { if (((key) == (keys[slot]))) { lastSlot = slot; return true; } slot = (slot + 1) & mask; } lastSlot = -1; return false; } /** * Round the capacity to the next allowed value. */ protected int roundCapacity(int requestedCapacity) { // Maximum positive integer that is a power of two. if (requestedCapacity > (0x80000000 >>> 1)) return (0x80000000 >>> 1); return Math.max(MIN_CAPACITY, BitUtil.nextHighestPowerOfTwo(requestedCapacity)); } /** * Return the next possible capacity, counting from the current buffers' * size. */ protected int nextCapacity(int current) { assert current > 0 && Long.bitCount(current) == 1 : "Capacity must be a power of two."; assert ((current << 1) > 0) : "Maximum capacity exceeded (" + (0x80000000 >>> 1) + ")."; if (current < MIN_CAPACITY / 2) current = MIN_CAPACITY / 2; return current << 1; } /** * {@inheritDoc} * *

Does not release internal buffers.

*/ @Override public void clear() { assigned = 0; // States are always cleared. Arrays.fill(allocated, false); /* */ /* */ } /** * {@inheritDoc} */ @Override public int size() { return assigned; } /** * {@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.

*/ public boolean isEmpty() { return size() == 0; } /** * {@inheritDoc} */ @Override public int hashCode() { int h = 0; for (DoubleCharCursor c : this) { h += rehash(c.key) + rehash(c.value); } return h; } /** * {@inheritDoc} */ @Override public boolean equals(Object obj) { if (obj != null) { if (obj == this) return true; if (obj instanceof DoubleCharMap) { /* */ DoubleCharMap other = (DoubleCharMap) obj; if (other.size() == this.size()) { for (DoubleCharCursor c : this) { if (other.containsKey(c.key)) { char v = other.get(c.key); if (((c.value) == (v))) { continue; } } return false; } return true; } } } return false; } /** * An iterator implementation for {@link #iterator}. */ private final class EntryIterator extends AbstractIterator { private final DoubleCharCursor cursor; public EntryIterator() { cursor = new DoubleCharCursor(); cursor.index = -1; } @Override protected DoubleCharCursor fetch() { int i = cursor.index + 1; final int max = keys.length; while (i < max && !allocated[i]) { i++; } if (i == max) return done(); cursor.index = i; cursor.key = keys[i]; cursor.value = values[i]; return cursor; } } /** * {@inheritDoc} */ @Override public Iterator iterator() { return new EntryIterator(); } /** * {@inheritDoc} */ @Override public T forEach(T procedure) { final double [] keys = this.keys; final char [] values = this.values; final boolean [] states = this.allocated; for (int i = 0; i < states.length; i++) { if (states[i]) procedure.apply(keys[i], values[i]); } return procedure; } /** * Returns a specialized view of the keys of this associated container. * The view additionally implements {@link ObjectLookupContainer}. */ public KeysContainer keys() { return new KeysContainer(); } /** * A view of the keys inside this hash map. */ public final class KeysContainer extends AbstractDoubleCollection implements DoubleLookupContainer { private final DoubleCharOpenHashMap owner = DoubleCharOpenHashMap.this; @Override public boolean contains(double e) { return containsKey(e); } @Override public T forEach(T procedure) { final double [] localKeys = owner.keys; final boolean [] localStates = owner.allocated; for (int i = 0; i < localStates.length; i++) { if (localStates[i]) procedure.apply(localKeys[i]); } return procedure; } @Override public T forEach(T predicate) { final double [] localKeys = owner.keys; final boolean [] localStates = owner.allocated; for (int i = 0; i < localStates.length; i++) { if (localStates[i]) { if (!predicate.apply(localKeys[i])) break; } } return predicate; } @Override public boolean isEmpty() { return owner.isEmpty(); } @Override public Iterator iterator() { return new KeysIterator(); } @Override public int size() { return owner.size(); } @Override public void clear() { owner.clear(); } @Override public int removeAll(DoublePredicate predicate) { return owner.removeAll(predicate); } @Override public int removeAllOccurrences(final double e) { final boolean hasKey = owner.containsKey(e); int result = 0; if (hasKey) { owner.remove(e); result = 1; } return result; } }; /** * An iterator over the set of assigned keys. */ private final class KeysIterator extends AbstractIterator { private final DoubleCursor cursor; public KeysIterator() { cursor = new DoubleCursor(); cursor.index = -1; } @Override protected DoubleCursor fetch() { int i = cursor.index + 1; final int max = keys.length; while (i < max && !allocated[i]) { i++; } if (i == max) return done(); cursor.index = i; cursor.value = keys[i]; return cursor; } } /** * @return Returns a container with all values stored in this map. */ @Override public CharContainer values() { return new ValuesContainer(); } /** * A view over the set of values of this map. */ private final class ValuesContainer extends AbstractCharCollection { @Override public int size() { return DoubleCharOpenHashMap.this.size(); } @Override public boolean isEmpty() { return DoubleCharOpenHashMap.this.isEmpty(); } @Override public boolean contains(char value) { // This is a linear scan over the values, but it's in the contract, so be it. final boolean [] allocated = DoubleCharOpenHashMap.this.allocated; final char [] values = DoubleCharOpenHashMap.this.values; for (int slot = 0; slot < allocated.length; slot++) { if (allocated[slot] && ((value) == (values[slot]))) { return true; } } return false; } @Override public T forEach(T procedure) { final boolean [] allocated = DoubleCharOpenHashMap.this.allocated; final char [] values = DoubleCharOpenHashMap.this.values; for (int i = 0; i < allocated.length; i++) { if (allocated[i]) procedure.apply(values[i]); } return procedure; } @Override public T forEach(T predicate) { final boolean [] allocated = DoubleCharOpenHashMap.this.allocated; final char [] values = DoubleCharOpenHashMap.this.values; for (int i = 0; i < allocated.length; i++) { if (allocated[i]) { if (!predicate.apply(values[i])) break; } } return predicate; } @Override public Iterator iterator() { return new ValuesIterator(); } @Override public int removeAllOccurrences(char e) { throw new UnsupportedOperationException(); } @Override public int removeAll(CharPredicate predicate) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } } /** * An iterator over the set of assigned values. */ private final class ValuesIterator extends AbstractIterator { private final CharCursor cursor; public ValuesIterator() { cursor = new CharCursor(); cursor.index = -1; } @Override protected CharCursor fetch() { int i = cursor.index + 1; final int max = keys.length; while (i < max && !allocated[i]) { i++; } if (i == max) return done(); cursor.index = i; cursor.value = values[i]; return cursor; } } /** * {@inheritDoc} */ @Override public DoubleCharOpenHashMap clone() { try { /* */ DoubleCharOpenHashMap cloned = (DoubleCharOpenHashMap) super.clone(); cloned.keys = keys.clone(); cloned.values = values.clone(); cloned.allocated = allocated.clone(); return cloned; } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } /** * 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 (DoubleCharCursor 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 DoubleCharOpenHashMap from(double [] keys, char [] values) { if (keys.length != values.length) throw new IllegalArgumentException("Arrays of keys and values must have an identical length."); DoubleCharOpenHashMap map = new DoubleCharOpenHashMap(); 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 DoubleCharOpenHashMap from(DoubleCharAssociativeContainer container) { return new DoubleCharOpenHashMap(container); } /** * Create a new hash map without providing the full generic signature (constructor * shortcut). */ public static DoubleCharOpenHashMap newInstance() { return new DoubleCharOpenHashMap(); } /** * Create a new hash map without providing the full generic signature (constructor * shortcut). */ public static DoubleCharOpenHashMap newInstance(int initialCapacity, float loadFactor) { return new DoubleCharOpenHashMap(initialCapacity, loadFactor); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy