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

smile.util.IntDoubleHashMap Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2010-2020 Haifeng Li. All rights reserved.
 *
 * Smile is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * Smile is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Smile.  If not, see .
 ******************************************************************************/

package smile.util;

import java.util.Arrays;

/**
 * HashMap<int, double> for primitive types.
 * Integer.MIN_VALUE (0x80000000) is not allowed as key.
 */
public class IntDoubleHashMap {
    private static final int FREE_KEY = Integer.MIN_VALUE;

    private static final double NO_VALUE = Double.NaN;

    /**
     * Keys and values.
     */
    private int[] keys;
    private double[] values;

    /**
     * The load factor, must be between (0 and 1).
     */
    private final float loadFactor;
    /**
     * We will resize a map once it reaches this size.
     */
    private int threshold;
    /**
     * The number of map entries.
     */
    private int size;

    /**
     * Mask to calculate the original position.
     */
    private int mask;

    /**
     * Constructs an empty HashMap with the default initial
     * capacity (16) and the default load factor (0.75).
     */
    public IntDoubleHashMap() {
        this(16, 0.75f);
    }

    /**
     * Constructor.
     *
     * @param initialCapacity the initial capacity.
     * @param loadFactor the load factor.
     */
    public IntDoubleHashMap(int initialCapacity, float loadFactor) {
        if (loadFactor <= 0 || loadFactor >= 1) {
            throw new IllegalArgumentException("Invalid fill factor: " + loadFactor);
        }

        if (initialCapacity <= 0) {
            throw new IllegalArgumentException("Invalid initial capacity: " + initialCapacity);
        }

        this.loadFactor = loadFactor;
        int capacity = arraySize(initialCapacity, loadFactor);
        mask = capacity - 1;

        keys = new int[capacity];
        Arrays.fill(keys, FREE_KEY);
        values = new double[capacity];
        threshold = (int) (capacity * loadFactor);
    }

    /**
     * Returns the value to which the specified key is mapped,
     * or Double.NaN if this map contains no mapping for the key.
     */
    public double get(int key) {
        if (key == FREE_KEY) {
            throw new IllegalArgumentException("key cannot be 0x80000000");
        }

        int ptr = hash(key);

        do {
            int k = keys[ptr];
            if (k == FREE_KEY) return NO_VALUE;
            if (k == key) return values[ptr];
            ptr = (ptr + 1) & mask; // next index
        } while (true);
    }

    /** Associates the specified value with the specified key in this map. */
    public double put(int key, double value) {
        if (key == FREE_KEY) {
            throw new IllegalArgumentException("key cannot be 0x80000000");
        }

        int ptr = hash(key);
        do {
            int k = keys[ptr];
            if (k == FREE_KEY) {
                keys[ptr] = key;
                values[ptr] = value;
                if (++size >= threshold) {
                    rehash(keys.length * 2);
                }

                return NO_VALUE;
            }

            if (k == key) {
                double ret = values[ptr];
                values[ptr] = value;
                return ret;
            }

            ptr = (ptr + 1) & mask;
        } while (true);
    }

    /** Removes the mapping for the specified key from this map if present. */
    public double remove(int key) {
        if (key == FREE_KEY) {
            throw new IllegalArgumentException("key cannot be 0x80000000");
        }

        int ptr = hash(key);
        do {
            int k = keys[ptr];
            if (k == FREE_KEY) {
                return NO_VALUE;
            }

            if (k == key) {
                double ret = values[ptr];
                keys[ptr] = FREE_KEY;
                return ret;
            }

            ptr = (ptr + 1) & mask;
        } while (true);
    }

    /** Returns the number of key-value mappings in this map. */
    public int size() {
        return size;
    }

    /** Resize the hash table. */
    private void rehash(int newCapacity) {
        threshold = (int) (newCapacity * loadFactor);
        mask = newCapacity - 1;

        int oldCapacity = keys.length;
        int[] oldKeys = keys;
        double[] oldValues = values;

        keys = new int[newCapacity];
        Arrays.fill(keys, FREE_KEY);
        values = new double[newCapacity];

        for (int i = 0; i < oldCapacity; i++) {
            int oldKey = oldKeys[i];
            if (oldKey != FREE_KEY) {
                put(oldKey, oldValues[i]);
            }
        }
    }

    /**
     * Return the least power of two greater than or equal to the specified value.
     *
     * Note that this function will return 1 when the argument is 0.
     *
     * @param x a long integer smaller than or equal to 262.
     * @return the least power of two greater than or equal to the specified value.
     */
    private long nextPowerOfTwo(long x) {
        if (x == 0) return 1;
        x--;
        x |= x >> 1;
        x |= x >> 2;
        x |= x >> 4;
        x |= x >> 8;
        x |= x >> 16;
        return (x | x >> 32) + 1;
    }

    /**
     * Returns the least power of two smaller than or equal to
     * 230 and larger than or equal to
     * ceil(expected / f).
     *
     * @param expected the expected number of elements in a hash table.
     * @param f        the load factor.
     * @return the minimum possible size for a backing array.
     * @throws IllegalArgumentException if the necessary size is larger than 230.
     */
    private int arraySize(int expected, float f) {
        long s = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f)));

        if (s > (1 << 30)) {
            throw new IllegalArgumentException(String.format("Too large %d expected elements with load factor %.2f", expected, f));
        }

        return (int) s;
    }

    /** Magic number for hash function. */
    private static final int INT_PHI = 0x9E3779B9;

    /** The hash function for int. */
    private int hash( int x) {
        int h = x * INT_PHI;
        return (h ^ (h >> 16)) & mask;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy