AbstractOpenIntHashMap.java

  1. /*
  2.  * Licensed to the Hipparchus project under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The Hipparchus project 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
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  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.
  16.  */
  17. package org.hipparchus.util;

  18. import java.util.Arrays;
  19. import java.util.ConcurrentModificationException;
  20. import java.util.NoSuchElementException;

  21. /** Base class for open addressed map from int.
  22.  * @since 3.1
  23.  */
  24. public abstract class AbstractOpenIntHashMap {

  25.     /** Default starting size.
  26.      * <p>This must be a power of two for bit mask to work properly. </p>
  27.      */
  28.     protected static final int DEFAULT_EXPECTED_SIZE = 16;

  29.     /** Multiplier for size growth when map fills up.
  30.      * <p>This must be a power of two for bit mask to work properly. </p>
  31.      */
  32.     protected static final int RESIZE_MULTIPLIER = 2;

  33.     /** Status indicator for free table entries. */
  34.     private static final byte FREE    = 0;

  35.     /** Status indicator for full table entries. */
  36.     private static final byte FULL    = 1;

  37.     /** Status indicator for removed table entries. */
  38.     private static final byte REMOVED = 2;

  39.     /** Load factor for the map. */
  40.     private static final float LOAD_FACTOR = 0.5f;

  41.     /** Number of bits to perturb the index when probing for collision resolution. */
  42.     private static final int PERTURB_SHIFT = 5;

  43.     /** Keys table. */
  44.     private int[] keys;

  45.     /** States table. */
  46.     private byte[] states;

  47.     /** Current size of the map. */
  48.     private int size;

  49.     /** Bit mask for hash values. */
  50.     private int mask;

  51.     /** Modifications count. */
  52.     private transient int count;

  53.     /** Build an empty map with default size.
  54.      */
  55.     protected AbstractOpenIntHashMap() {
  56.         this(DEFAULT_EXPECTED_SIZE);
  57.     }

  58.     /**
  59.      * Build an empty map with specified size.
  60.      * @param expectedSize expected number of elements in the map
  61.      */
  62.     protected AbstractOpenIntHashMap(final int expectedSize) {
  63.         final int capacity = computeCapacity(expectedSize);
  64.         keys   = new int[capacity];
  65.         states = new byte[capacity];
  66.         mask   = capacity - 1;
  67.         resetCount();
  68.     }

  69.     /**
  70.      * Copy constructor.
  71.      * @param source map to copy
  72.      */
  73.     protected AbstractOpenIntHashMap(final AbstractOpenIntHashMap source) {
  74.         final int length = source.keys.length;
  75.         keys = new int[length];
  76.         System.arraycopy(source.keys, 0, keys, 0, length);
  77.         states = new byte[length];
  78.         System.arraycopy(source.states, 0, states, 0, length);
  79.         size  = source.size;
  80.         mask  = source.mask;
  81.         count = source.count;
  82.     }

  83.     /** Get capacity.
  84.      * @return capacity
  85.      * @since 3.1
  86.      */
  87.     protected int getCapacity() {
  88.         return keys.length;
  89.     }

  90.     /** Get the number of elements stored in the map.
  91.      * @return number of elements stored in the map
  92.      */
  93.     public int getSize() {
  94.         return size;
  95.     }

  96.     /**
  97.      * Compute the capacity needed for a given size.
  98.      * @param expectedSize expected size of the map
  99.      * @return capacity to use for the specified size
  100.      */
  101.     private static int computeCapacity(final int expectedSize) {
  102.         if (expectedSize == 0) {
  103.             return 1;
  104.         }
  105.         final int capacity   = (int) FastMath.ceil(expectedSize / LOAD_FACTOR);
  106.         final int powerOfTwo = Integer.highestOneBit(capacity);
  107.         if (powerOfTwo == capacity) {
  108.             return capacity;
  109.         }
  110.         return nextPowerOfTwo(capacity);
  111.     }

  112.     /** Reset count.
  113.      * @since 3.1
  114.      */
  115.     protected final void resetCount() {
  116.         count = 0;
  117.     }

  118.     /**
  119.      * Find the smallest power of two greater than the input value
  120.      * @param i input value
  121.      * @return smallest power of two greater than the input value
  122.      */
  123.     private static int nextPowerOfTwo(final int i) {
  124.         return Integer.highestOneBit(i) << 1;
  125.     }

  126.     /**
  127.      * Check if a value is associated with a key.
  128.      * @param key key to check
  129.      * @return true if a value is associated with key
  130.      */
  131.     public boolean containsKey(final int key) {

  132.         final int hash  = hashOf(key);
  133.         int index = hash & mask;
  134.         if (containsKey(key, index)) {
  135.             return true;
  136.         }

  137.         if (states[index] == FREE) {
  138.             return false;
  139.         }

  140.         int j = index;
  141.         for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
  142.             j = probe(perturb, j);
  143.             index = j & mask;
  144.             if (containsKey(key, index)) {
  145.                 return true;
  146.             }
  147.         }

  148.         return false;

  149.     }

  150.     /**
  151.      * Perturb the hash for starting probing.
  152.      * @param hash initial hash
  153.      * @return perturbed hash
  154.      */
  155.     private static int perturb(final int hash) {
  156.         return hash & 0x7fffffff;
  157.     }

  158.     /**
  159.      * Find the index at which a key should be inserted
  160.      * @param keys keys table
  161.      * @param states states table
  162.      * @param key key to lookup
  163.      * @param mask bit mask for hash values
  164.      * @return index at which key should be inserted
  165.      */
  166.     private static int findInsertionIndex(final int[] keys, final byte[] states,
  167.                                           final int key, final int mask) {
  168.         final int hash = hashOf(key);
  169.         int index = hash & mask;
  170.         if (states[index] == FREE) {
  171.             return index;
  172.         } else if (states[index] == FULL && keys[index] == key) {
  173.             return changeIndexSign(index);
  174.         }

  175.         int perturb = perturb(hash);
  176.         int j = index;
  177.         if (states[index] == FULL) {
  178.             while (true) {
  179.                 j = probe(perturb, j);
  180.                 index = j & mask;
  181.                 perturb >>= PERTURB_SHIFT;

  182.                 if (states[index] != FULL || keys[index] == key) {
  183.                     break;
  184.                 }
  185.             }
  186.         }

  187.         if (states[index] == FREE) {
  188.             return index;
  189.         } else if (states[index] == FULL) {
  190.             // due to the loop exit condition,
  191.             // if (states[index] == FULL) then keys[index] == key
  192.             return changeIndexSign(index);
  193.         }

  194.         final int firstRemoved = index;
  195.         while (true) {
  196.             j = probe(perturb, j);
  197.             index = j & mask;

  198.             if (states[index] == FREE) {
  199.                 return firstRemoved;
  200.             } else if (states[index] == FULL && keys[index] == key) {
  201.                 return changeIndexSign(index);
  202.             }

  203.             perturb >>= PERTURB_SHIFT;

  204.         }

  205.     }

  206.     /**
  207.      * Compute next probe for collision resolution
  208.      * @param perturb perturbed hash
  209.      * @param j previous probe
  210.      * @return next probe
  211.      */
  212.     private static int probe(final int perturb, final int j) {
  213.         return (j << 2) + j + perturb + 1;
  214.     }

  215.     /**
  216.      * Change the index sign
  217.      * @param index initial index
  218.      * @return changed index
  219.      */
  220.     private static int changeIndexSign(final int index) {
  221.         return -index - 1;
  222.     }

  223.     /**
  224.      * Get the number of elements stored in the map.
  225.      * @return number of elements stored in the map
  226.      */
  227.     public int size() {
  228.         return size;
  229.     }

  230.     /**
  231.      * Check if the tables contain an element associated with specified key
  232.      * at specified index.
  233.      * @param key key to check
  234.      * @param index index to check
  235.      * @return true if an element is associated with key at index
  236.      */
  237.     public boolean containsKey(final int key, final int index) {
  238.         return (key != 0 || states[index] == FULL) && keys[index] == key;
  239.     }

  240.     /** Locate the index of value associated with the given key
  241.      * @param key key associated with the data
  242.      * @return index of value associated with the given key or negative
  243.      * if key not present
  244.      */
  245.     protected int locate(final int key) {

  246.         final int hash  = hashOf(key);
  247.         int index = hash & mask;
  248.         if (containsKey(key, index)) {
  249.             return index;
  250.         }

  251.         if (states[index] == FREE) {
  252.             return -1;
  253.         }

  254.         int j = index;
  255.         for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
  256.             j = probe(perturb, j);
  257.             index = j & mask;
  258.             if (containsKey(key, index)) {
  259.                 return index;
  260.             }
  261.         }

  262.         return -1;

  263.     }

  264.     /** Remove an element at specified index.
  265.      * @param index index of the element to remove
  266.      */
  267.     protected void doRemove(int index) {
  268.         keys[index]   = 0;
  269.         states[index] = REMOVED;
  270.         --size;
  271.         ++count;
  272.     }

  273.     /** Put a value associated with a key in the map.
  274.      * @param key key to which value is associated
  275.      * @return holder to manage insertion
  276.      */
  277.     protected InsertionHolder put(final int key) {
  278.         int     oldIndex   = findInsertionIndex(keys, states, key, mask);
  279.         int     newIndex   = oldIndex;
  280.         boolean existing   = false;
  281.         boolean newMapping = true;
  282.         if (oldIndex < 0) {
  283.             oldIndex   = changeIndexSign(oldIndex);
  284.             existing   = true;
  285.             newMapping = false;
  286.         }
  287.         keys[oldIndex] = key;
  288.         states[oldIndex] = FULL;
  289.         if (newMapping) {
  290.             ++size;
  291.             if (shouldGrowTable()) {
  292.                 newIndex = growTable(oldIndex);
  293.             }
  294.             ++count;
  295.         }
  296.         return new InsertionHolder(existing ? oldIndex : newIndex, existing);

  297.     }

  298.     /** Grow the tables.
  299.      * @param oldIndex index the entry being inserted should have used
  300.      * @return index the entry being inserted should really use
  301.      */
  302.     protected abstract int growTable(int oldIndex);

  303.     /** Grow the tables.
  304.      * @param oldIndex index the entry being inserted should have used
  305.      * @param valueCopier copier for existing values
  306.      * @return index the entry being inserted should really use
  307.      */
  308.     protected int doGrowTable(final int oldIndex, final ValueCopier valueCopier) {

  309.         int newIndex = oldIndex;
  310.         final int    oldLength = states.length;
  311.         final int[]  oldKeys   = keys;
  312.         final byte[] oldStates = states;

  313.         final int    newLength = RESIZE_MULTIPLIER * oldLength;
  314.         final int[]  newKeys   = new int[newLength];
  315.         final byte[] newStates = new byte[newLength];
  316.         final int newMask = newLength - 1;
  317.         for (int srcIndex = 0; srcIndex < oldLength; ++srcIndex) {
  318.             if (oldStates[srcIndex] == FULL) {
  319.                 final int key   = oldKeys[srcIndex];
  320.                 final int dstIndex = findInsertionIndex(newKeys, newStates, key, newMask);
  321.                 newKeys[dstIndex]  = key;
  322.                 valueCopier.copyValue(srcIndex, dstIndex);
  323.                 if (srcIndex == oldIndex) {
  324.                     newIndex = dstIndex;
  325.                 }
  326.                 newStates[dstIndex] = FULL;
  327.             }
  328.         }

  329.         mask   = newMask;
  330.         keys   = newKeys;
  331.         states = newStates;

  332.         return newIndex;

  333.     }

  334.     /**
  335.      * Check if tables should grow due to increased size.
  336.      * @return true if  tables should grow
  337.      */
  338.     private boolean shouldGrowTable() {
  339.         return size > (mask + 1) * LOAD_FACTOR;
  340.     }

  341.     /**
  342.      * Compute the hash value of a key
  343.      * @param key key to hash
  344.      * @return hash value of the key
  345.      */
  346.     private static int hashOf(final int key) {
  347.         final int h = key ^ ((key >>> 20) ^ (key >>> 12));
  348.         return h ^ (h >>> 7) ^ (h >>> 4);
  349.     }

  350.     /** Check if keys are equals.
  351.      * @param other other map
  352.      * @return true if keys are equals
  353.      */
  354.     protected boolean equalKeys(final AbstractOpenIntHashMap other) {
  355.         return Arrays.equals(keys, other.keys);
  356.     }

  357.     /** Check if states are equals.
  358.      * @param other other map
  359.      * @return true if states are equals
  360.      */
  361.     protected boolean equalStates(final AbstractOpenIntHashMap other) {
  362.         return Arrays.equals(states, other.states);
  363.     }

  364.     /** Compute partial hashcode on keys and states.
  365.      * @return partial hashcode on keys and states
  366.      */
  367.     protected int keysStatesHashCode() {
  368.         return  53 * Arrays.hashCode(keys) + 31 * Arrays.hashCode(states);
  369.     }

  370.     /** Iterator class for the map. */
  371.     protected class BaseIterator {

  372.         /** Reference modification count. */
  373.         private final int referenceCount;

  374.         /** Index of current element. */
  375.         private int current;

  376.         /** Index of next element. */
  377.         private int next;

  378.         /**
  379.          * Simple constructor.
  380.          */
  381.         protected BaseIterator() {

  382.             // preserve the modification count of the map to detect concurrent modifications later
  383.             referenceCount = count;

  384.             // initialize current index
  385.             next = -1;
  386.             try {
  387.                 advance();
  388.             } catch (NoSuchElementException nsee) { // NOPMD
  389.                 // ignored
  390.             }

  391.         }

  392.         /**
  393.          * Check if there is a next element in the map.
  394.          * @return true if there is a next element
  395.          */
  396.         public boolean hasNext() {
  397.             return next >= 0;
  398.         }

  399.         /** Get index of current entry.
  400.          * @return key of current entry
  401.          * @since 3.1
  402.          */
  403.         protected int getCurrent() {
  404.             return current;
  405.         }

  406.         /**
  407.          * Get the key of current entry.
  408.          * @return key of current entry
  409.          * @exception ConcurrentModificationException if the map is modified during iteration
  410.          * @exception NoSuchElementException if there is no element left in the map
  411.          */
  412.         public int key() throws ConcurrentModificationException, NoSuchElementException {
  413.             return keys[getCurrent()];
  414.         }

  415.         /**
  416.          * Advance iterator one step further.
  417.          * @exception ConcurrentModificationException if the map is modified during iteration
  418.          * @exception NoSuchElementException if there is no element left in the map
  419.          */
  420.         public void advance()
  421.             throws ConcurrentModificationException, NoSuchElementException {

  422.             if (referenceCount != count) {
  423.                 throw new ConcurrentModificationException();
  424.             }

  425.             // advance on step
  426.             current = next;

  427.             // prepare next step
  428.             try {
  429.                 while (states[++next] != FULL) { // NOPMD
  430.                     // nothing to do
  431.                 }
  432.             } catch (ArrayIndexOutOfBoundsException e) {
  433.                 next = -2;
  434.                 if (current < 0) {
  435.                     throw new NoSuchElementException(); // NOPMD
  436.                 }
  437.             }

  438.         }

  439.     }

  440.     /** Holder for handling values insertion.
  441.      * @since 3.1
  442.      */
  443.     protected static class InsertionHolder {

  444.         /** Index at which new value should be put. */
  445.         private final int index;

  446.         /** Indicator for value already present before insertion. */
  447.         private final boolean existing;

  448.         /** Simple constructor.
  449.          * @param index index at which new value should be put
  450.          * @param existing indicator for value already present before insertion
  451.          */
  452.         InsertionHolder(final int index, final boolean existing) {
  453.             this.index    = index;
  454.             this.existing = existing;
  455.         }

  456.         /** Get index at which new value should be put.
  457.          * @return index at which new value should be put
  458.          */
  459.         public int getIndex() {
  460.             return index;
  461.         }

  462.         /** Get indicator for value already present before insertion.
  463.          * @return indicator for value already present before insertion
  464.          */
  465.         public boolean isExisting() {
  466.             return existing;
  467.         }
  468.     }

  469.     /** Interface for copying values.
  470.      * @since 3.1
  471.      */
  472.     @FunctionalInterface
  473.     protected interface ValueCopier {
  474.         /** Copy a value.
  475.          * @param src source index
  476.          * @param dest destination index
  477.          */
  478.         void copyValue(int src, int dest);
  479.     }

  480. }