OpenIntToFieldHashMap.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) 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 ASF 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.  *      http://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.apache.commons.math4.legacy.linear;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.Serializable;
  21. import java.lang.reflect.Array;
  22. import java.util.ConcurrentModificationException;
  23. import java.util.NoSuchElementException;

  24. import org.apache.commons.math4.legacy.core.Field;
  25. import org.apache.commons.math4.legacy.core.FieldElement;
  26. import org.apache.commons.math4.core.jdkmath.JdkMath;

  27. /**
  28.  * Open addressed map from int to FieldElement.
  29.  * <p>This class provides a dedicated map from integers to FieldElements with a
  30.  * much smaller memory overhead than standard <code>java.util.Map</code>.</p>
  31.  * <p>This class is not synchronized. The specialized iterators returned by
  32.  * {@link #iterator()} are fail-fast: they throw a
  33.  * <code>ConcurrentModificationException</code> when they detect the map has been
  34.  * modified during iteration.</p>
  35.  * @param <T> the type of the field elements
  36.  * @since 2.0
  37.  */
  38. class OpenIntToFieldHashMap<T extends FieldElement<T>> implements Serializable { // Not in public API.

  39.     /** Status indicator for free table entries. */
  40.     protected static final byte FREE    = 0;

  41.     /** Status indicator for full table entries. */
  42.     protected static final byte FULL    = 1;

  43.     /** Status indicator for removed table entries. */
  44.     protected static final byte REMOVED = 2;

  45.     /** Serializable version identifier. */
  46.     private static final long serialVersionUID = -9179080286849120720L;

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

  49.     /** Default starting size.
  50.      * <p>This must be a power of two for bit mask to work properly. </p>
  51.      */
  52.     private static final int DEFAULT_EXPECTED_SIZE = 16;

  53.     /** Multiplier for size growth when map fills up.
  54.      * <p>This must be a power of two for bit mask to work properly. </p>
  55.      */
  56.     private static final int RESIZE_MULTIPLIER = 2;

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

  59.     /** Field to which the elements belong. */
  60.     private final Field<T> field;

  61.     /** Keys table. */
  62.     private int[] keys;

  63.     /** Values table. */
  64.     private T[] values;

  65.     /** States table. */
  66.     private byte[] states;

  67.     /** Return value for missing entries. */
  68.     private final T missingEntries;

  69.     /** Current size of the map. */
  70.     private int size;

  71.     /** Bit mask for hash values. */
  72.     private int mask;

  73.     /** Modifications count. */
  74.     private transient int count;

  75.     /**
  76.      * Build an empty map with default size and using zero for missing entries.
  77.      * @param field field to which the elements belong
  78.      */
  79.     OpenIntToFieldHashMap(final Field<T> field) {
  80.         this(field, DEFAULT_EXPECTED_SIZE, field.getZero());
  81.     }

  82.     /**
  83.      * Build an empty map with default size.
  84.      * @param field field to which the elements belong
  85.      * @param missingEntries value to return when a missing entry is fetched
  86.      */
  87.     OpenIntToFieldHashMap(final Field<T> field, final T missingEntries) {
  88.         this(field,DEFAULT_EXPECTED_SIZE, missingEntries);
  89.     }

  90.     /**
  91.      * Build an empty map with specified size and using zero for missing entries.
  92.      * @param field field to which the elements belong
  93.      * @param expectedSize expected number of elements in the map
  94.      */
  95.     OpenIntToFieldHashMap(final Field<T> field,final int expectedSize) {
  96.         this(field,expectedSize, field.getZero());
  97.     }

  98.     /**
  99.      * Build an empty map with specified size.
  100.      * @param field field to which the elements belong
  101.      * @param expectedSize expected number of elements in the map
  102.      * @param missingEntries value to return when a missing entry is fetched
  103.      */
  104.     OpenIntToFieldHashMap(final Field<T> field,final int expectedSize,
  105.                           final T missingEntries) {
  106.         this.field = field;
  107.         final int capacity = computeCapacity(expectedSize);
  108.         keys   = new int[capacity];
  109.         values = buildArray(capacity);
  110.         states = new byte[capacity];
  111.         this.missingEntries = missingEntries;
  112.         mask   = capacity - 1;
  113.     }

  114.     /**
  115.      * Copy constructor.
  116.      * @param source map to copy
  117.      */
  118.     OpenIntToFieldHashMap(final OpenIntToFieldHashMap<T> source) {
  119.         field = source.field;
  120.         final int length = source.keys.length;
  121.         keys = new int[length];
  122.         System.arraycopy(source.keys, 0, keys, 0, length);
  123.         values = buildArray(length);
  124.         System.arraycopy(source.values, 0, values, 0, length);
  125.         states = new byte[length];
  126.         System.arraycopy(source.states, 0, states, 0, length);
  127.         missingEntries = source.missingEntries;
  128.         size  = source.size;
  129.         mask  = source.mask;
  130.         count = source.count;
  131.     }

  132.     /**
  133.      * Compute the capacity needed for a given size.
  134.      * @param expectedSize expected size of the map
  135.      * @return capacity to use for the specified size
  136.      */
  137.     private static int computeCapacity(final int expectedSize) {
  138.         if (expectedSize == 0) {
  139.             return 1;
  140.         }
  141.         final int capacity   = (int) JdkMath.ceil(expectedSize / LOAD_FACTOR);
  142.         final int powerOfTwo = Integer.highestOneBit(capacity);
  143.         if (powerOfTwo == capacity) {
  144.             return capacity;
  145.         }
  146.         return nextPowerOfTwo(capacity);
  147.     }

  148.     /**
  149.      * Find the smallest power of two greater than the input value.
  150.      * @param i input value
  151.      * @return smallest power of two greater than the input value
  152.      */
  153.     private static int nextPowerOfTwo(final int i) {
  154.         return Integer.highestOneBit(i) << 1;
  155.     }

  156.     /**
  157.      * Get the stored value associated with the given key.
  158.      * @param key key associated with the data
  159.      * @return data associated with the key
  160.      */
  161.     public T get(final int key) {

  162.         final int hash  = hashOf(key);
  163.         int index = hash & mask;
  164.         if (containsKey(key, index)) {
  165.             return values[index];
  166.         }

  167.         if (states[index] == FREE) {
  168.             return missingEntries;
  169.         }

  170.         int j = index;
  171.         for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
  172.             j = probe(perturb, j);
  173.             index = j & mask;
  174.             if (containsKey(key, index)) {
  175.                 return values[index];
  176.             }
  177.         }

  178.         return missingEntries;
  179.     }

  180.     /**
  181.      * Check if a value is associated with a key.
  182.      * @param key key to check
  183.      * @return true if a value is associated with key
  184.      */
  185.     public boolean containsKey(final int key) {

  186.         final int hash  = hashOf(key);
  187.         int index = hash & mask;
  188.         if (containsKey(key, index)) {
  189.             return true;
  190.         }

  191.         if (states[index] == FREE) {
  192.             return false;
  193.         }

  194.         int j = index;
  195.         for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
  196.             j = probe(perturb, j);
  197.             index = j & mask;
  198.             if (containsKey(key, index)) {
  199.                 return true;
  200.             }
  201.         }

  202.         return false;
  203.     }

  204.     /**
  205.      * Get an iterator over map elements.
  206.      * <p>The specialized iterators returned are fail-fast: they throw a
  207.      * <code>ConcurrentModificationException</code> when they detect the map
  208.      * has been modified during iteration.</p>
  209.      * @return iterator over the map elements
  210.      */
  211.     public Iterator iterator() {
  212.         return new Iterator();
  213.     }

  214.     /**
  215.      * Perturb the hash for starting probing.
  216.      * @param hash initial hash
  217.      * @return perturbed hash
  218.      */
  219.     private static int perturb(final int hash) {
  220.         return hash & 0x7fffffff;
  221.     }

  222.     /**
  223.      * Find the index at which a key should be inserted.
  224.      * @param key key to lookup
  225.      * @return index at which key should be inserted
  226.      */
  227.     private int findInsertionIndex(final int key) {
  228.         return findInsertionIndex(keys, states, key, mask);
  229.     }

  230.     /**
  231.      * Find the index at which a key should be inserted.
  232.      * @param keys keys table
  233.      * @param states states table
  234.      * @param key key to lookup
  235.      * @param mask bit mask for hash values
  236.      * @return index at which key should be inserted
  237.      */
  238.     private static int findInsertionIndex(final int[] keys, final byte[] states,
  239.                                           final int key, final int mask) {
  240.         final int hash = hashOf(key);
  241.         int index = hash & mask;
  242.         if (states[index] == FREE) {
  243.             return index;
  244.         } else if (states[index] == FULL && keys[index] == key) {
  245.             return changeIndexSign(index);
  246.         }

  247.         int perturb = perturb(hash);
  248.         int j = index;
  249.         if (states[index] == FULL) {
  250.             while (true) {
  251.                 j = probe(perturb, j);
  252.                 index = j & mask;
  253.                 perturb >>= PERTURB_SHIFT;

  254.                 if (states[index] != FULL || keys[index] == key) {
  255.                     break;
  256.                 }
  257.             }
  258.         }

  259.         if (states[index] == FREE) {
  260.             return index;
  261.         } else if (states[index] == FULL) {
  262.             // due to the loop exit condition,
  263.             // if (states[index] == FULL) then keys[index] == key
  264.             return changeIndexSign(index);
  265.         }

  266.         final int firstRemoved = index;
  267.         while (true) {
  268.             j = probe(perturb, j);
  269.             index = j & mask;

  270.             if (states[index] == FREE) {
  271.                 return firstRemoved;
  272.             } else if (states[index] == FULL && keys[index] == key) {
  273.                 return changeIndexSign(index);
  274.             }

  275.             perturb >>= PERTURB_SHIFT;
  276.         }
  277.     }

  278.     /**
  279.      * Compute next probe for collision resolution.
  280.      * @param perturb perturbed hash
  281.      * @param j previous probe
  282.      * @return next probe
  283.      */
  284.     private static int probe(final int perturb, final int j) {
  285.         return (j << 2) + j + perturb + 1;
  286.     }

  287.     /**
  288.      * Change the index sign.
  289.      * @param index initial index
  290.      * @return changed index
  291.      */
  292.     private static int changeIndexSign(final int index) {
  293.         return -index - 1;
  294.     }

  295.     /**
  296.      * Get the number of elements stored in the map.
  297.      * @return number of elements stored in the map
  298.      */
  299.     public int size() {
  300.         return size;
  301.     }


  302.     /**
  303.      * Remove the value associated with a key.
  304.      * @param key key to which the value is associated
  305.      * @return removed value
  306.      */
  307.     public T remove(final int key) {

  308.         final int hash  = hashOf(key);
  309.         int index = hash & mask;
  310.         if (containsKey(key, index)) {
  311.             return doRemove(index);
  312.         }

  313.         if (states[index] == FREE) {
  314.             return missingEntries;
  315.         }

  316.         int j = index;
  317.         for (int perturb = perturb(hash); states[index] != FREE; perturb >>= PERTURB_SHIFT) {
  318.             j = probe(perturb, j);
  319.             index = j & mask;
  320.             if (containsKey(key, index)) {
  321.                 return doRemove(index);
  322.             }
  323.         }

  324.         return missingEntries;
  325.     }

  326.     /**
  327.      * Check if the tables contain an element associated with specified key
  328.      * at specified index.
  329.      * @param key key to check
  330.      * @param index index to check
  331.      * @return true if an element is associated with key at index
  332.      */
  333.     private boolean containsKey(final int key, final int index) {
  334.         return (key != 0 || states[index] == FULL) && keys[index] == key;
  335.     }

  336.     /**
  337.      * Remove an element at specified index.
  338.      * @param index index of the element to remove
  339.      * @return removed value
  340.      */
  341.     private T doRemove(int index) {
  342.         keys[index]   = 0;
  343.         states[index] = REMOVED;
  344.         final T previous = values[index];
  345.         values[index] = missingEntries;
  346.         --size;
  347.         ++count;
  348.         return previous;
  349.     }

  350.     /**
  351.      * Put a value associated with a key in the map.
  352.      * @param key key to which value is associated
  353.      * @param value value to put in the map
  354.      * @return previous value associated with the key
  355.      */
  356.     public T put(final int key, final T value) {
  357.         int index = findInsertionIndex(key);
  358.         T previous = missingEntries;
  359.         boolean newMapping = true;
  360.         if (index < 0) {
  361.             index = changeIndexSign(index);
  362.             previous = values[index];
  363.             newMapping = false;
  364.         }
  365.         keys[index]   = key;
  366.         states[index] = FULL;
  367.         values[index] = value;
  368.         if (newMapping) {
  369.             ++size;
  370.             if (shouldGrowTable()) {
  371.                 growTable();
  372.             }
  373.             ++count;
  374.         }
  375.         return previous;
  376.     }

  377.     /**
  378.      * Grow the tables.
  379.      */
  380.     private void growTable() {

  381.         final int oldLength      = states.length;
  382.         final int[] oldKeys      = keys;
  383.         final T[] oldValues = values;
  384.         final byte[] oldStates   = states;

  385.         final int newLength = RESIZE_MULTIPLIER * oldLength;
  386.         final int[] newKeys = new int[newLength];
  387.         final T[] newValues = buildArray(newLength);
  388.         final byte[] newStates = new byte[newLength];
  389.         final int newMask = newLength - 1;
  390.         for (int i = 0; i < oldLength; ++i) {
  391.             if (oldStates[i] == FULL) {
  392.                 final int key = oldKeys[i];
  393.                 final int index = findInsertionIndex(newKeys, newStates, key, newMask);
  394.                 newKeys[index]   = key;
  395.                 newValues[index] = oldValues[i];
  396.                 newStates[index] = FULL;
  397.             }
  398.         }

  399.         mask   = newMask;
  400.         keys   = newKeys;
  401.         values = newValues;
  402.         states = newStates;
  403.     }

  404.     /**
  405.      * Check if tables should grow due to increased size.
  406.      * @return true if  tables should grow
  407.      */
  408.     private boolean shouldGrowTable() {
  409.         return size > (mask + 1) * LOAD_FACTOR;
  410.     }

  411.     /**
  412.      * Compute the hash value of a key.
  413.      * @param key key to hash
  414.      * @return hash value of the key
  415.      */
  416.     private static int hashOf(final int key) {
  417.         final int h = key ^ ((key >>> 20) ^ (key >>> 12));
  418.         return h ^ (h >>> 7) ^ (h >>> 4);
  419.     }


  420.     /** Iterator class for the map. */
  421.     public final class Iterator {

  422.         /** Reference modification count. */
  423.         private final int referenceCount;

  424.         /** Index of current element. */
  425.         private int current;

  426.         /** Index of next element. */
  427.         private int next;

  428.         /**
  429.          * Simple constructor.
  430.          */
  431.         private Iterator() {

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

  434.             // initialize current index
  435.             next = -1;
  436.             try {
  437.                 advance();
  438.             } catch (NoSuchElementException nsee) { // NOPMD
  439.                 // ignored
  440.             }
  441.         }

  442.         /**
  443.          * Check if there is a next element in the map.
  444.          * @return true if there is a next element
  445.          */
  446.         public boolean hasNext() {
  447.             return next >= 0;
  448.         }

  449.         /**
  450.          * Get the key of current entry.
  451.          * @return key of current entry
  452.          * @exception ConcurrentModificationException if the map is modified during iteration
  453.          * @exception NoSuchElementException if there is no element left in the map
  454.          */
  455.         public int key()
  456.             throws ConcurrentModificationException, NoSuchElementException {
  457.             if (referenceCount != count) {
  458.                 throw new ConcurrentModificationException();
  459.             }
  460.             if (current < 0) {
  461.                 throw new NoSuchElementException();
  462.             }
  463.             return keys[current];
  464.         }

  465.         /**
  466.          * Get the value of current entry.
  467.          * @return value of current entry
  468.          * @exception ConcurrentModificationException if the map is modified during iteration
  469.          * @exception NoSuchElementException if there is no element left in the map
  470.          */
  471.         public T value()
  472.             throws ConcurrentModificationException, NoSuchElementException {
  473.             if (referenceCount != count) {
  474.                 throw new ConcurrentModificationException();
  475.             }
  476.             if (current < 0) {
  477.                 throw new NoSuchElementException();
  478.             }
  479.             return values[current];
  480.         }

  481.         /**
  482.          * Advance iterator one step further.
  483.          * @exception ConcurrentModificationException if the map is modified during iteration
  484.          * @exception NoSuchElementException if there is no element left in the map
  485.          */
  486.         public void advance()
  487.             throws ConcurrentModificationException, NoSuchElementException {

  488.             if (referenceCount != count) {
  489.                 throw new ConcurrentModificationException();
  490.             }

  491.             // advance on step
  492.             current = next;

  493.             // prepare next step
  494.             try {
  495.                 do {
  496.                     ++next;
  497.                 } while (states[next] != FULL);
  498.             } catch (ArrayIndexOutOfBoundsException e) {
  499.                 next = -2;
  500.                 if (current < 0) {
  501.                     throw new NoSuchElementException();
  502.                 }
  503.             }
  504.         }
  505.     }

  506.     /**
  507.      * Read a serialized object.
  508.      * @param stream input stream
  509.      * @throws IOException if object cannot be read
  510.      * @throws ClassNotFoundException if the class corresponding
  511.      * to the serialized object cannot be found
  512.      */
  513.     private void readObject(final ObjectInputStream stream)
  514.         throws IOException, ClassNotFoundException {
  515.         stream.defaultReadObject();
  516.         count = 0;
  517.     }

  518.     /** Build an array of elements.
  519.      * @param length size of the array to build
  520.      * @return a new array
  521.      */
  522.     @SuppressWarnings("unchecked") // field is of type T
  523.     private T[] buildArray(final int length) {
  524.         return (T[]) Array.newInstance(field.getRuntimeClass(), length);
  525.     }
  526. }