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

org.btrplace.util.IntObjectMap Maven / Gradle / Ivy

/*
 * Copyright  2021 The BtrPlace Authors. All rights reserved.
 * Use of this source code is governed by a LGPL-style
 * license that can be found in the LICENSE.txt file.
 */

package org.btrplace.util;

import java.util.Arrays;
import java.util.Objects;

/**
 * A map to associate an Object to an element.
 * The map does not compact the key space so it is very efficient in terms of memory when there is no or a very few
 * holes. For the best performances, it is also wise to set the capacity as early as possible to bypass the incremental
 * expansion.
 */
public class IntObjectMap {

    /**
     * Default size for the map.
     */
    public static final int DEFAULT_SIZE = 10;

    /**
     * Value stating that the key is missing.
     */
    private final V noValue;

    /**
     * Value backend.
     */
    private V[] values;

    /**
     * Biggest key in the map.
     */
    private int lastKey;

    /**
     * Number of entries in the map.
     */
    private int count;

    /**
     * Empty may with {@code null} to indicate no value.
     */
    public IntObjectMap() {
        this(null, DEFAULT_SIZE);
    }

    /**
     * New map.
     *
     * @param noValue the value to use to report a missing key.
     */
    public IntObjectMap(V noValue) {
        this(noValue, DEFAULT_SIZE);
    }

    /**
     * New map.
     *
     * @param noValue the value to use to report a missing key.
     * @param size    the backend size.
     */
    public IntObjectMap(V noValue, int size) {
        this.noValue = noValue;
        this.values = (V[]) new Object[size];
        if (noValue != null) {
            Arrays.fill(values, noValue);
        }
        count = 0;
    }

    /**
     * Copy constructor.
     *
     * @param backend the backend.
     */
    private IntObjectMap(final IntObjectMap backend) {
        this.noValue = backend.noValue;
        this.values = Arrays.copyOf(backend.values, backend.values.length);
        this.lastKey = backend.lastKey;
        this.count = backend.count;
    }

    /**
     * Expand the map to a new size.
     *
     * @param newSize the new size for the backend.
     */
    public void expand(final int newSize) {
        if (newSize > values.length) {
            V[] bigger = Arrays.copyOf(values, newSize);
            Arrays.fill(bigger, values.length, newSize, noValue);
            values = bigger;
        }
    }

    /**
     * Get the value for the key.
     * If the key is not in, {@link #noEntryValue()} is returned.
     *
     * @param key the key.
     * @return the value associated to the key. {@link #noEntryValue()} otherwise.
     */
    public V get(final int key) {
        if (key < 0 || key >= values.length) {
            return noValue;
        }
        return values[key];
    }

    /**
     * Unsafe version of {@link #get(int)}.
     *
     * @param key the key.
     * @return the value.
     */
    public V quickGet(final int key) {
        return values[key];
    }

    /**
     * Check if a key is in the map.
     *
     * @param key the key.
     * @return {@code true} if the key is in the map. This means that the value differs from {@link #noEntryValue()}.
     */
    public boolean has(final int key) {
        return get(key) != noValue;
    }

    /**
     * Get the value indicating the key is not in the map.
     *
     * @return the value.
     */
    public V noEntryValue() {
        return noValue;
    }

    /**
     * Put an entry in the map. The map is expanded if needed.
     *
     * @param key   the entry key.
     * @param value the value.
     * @return the previous value.
     */
    public V put(final int key, V value) {
        if (key < 0) {
            return noValue;
        }
        if (key >= values.length) {
            int curCap = values.length;
            // 50% grow at minimum, up to key.
            expand(Math.max(key + 1, curCap + curCap / 2));
        }
        V old = values[key];
        if (old == noValue) {
            // put a new key.
            count++;
        }
        values[key] = value;
        lastKey = Math.max(key, lastKey);
        return old;
    }

    /**
     * Returns a copy of the map.
     * The values associated to the keys are not copied.
     *
     * @return a copy.
     */
    public IntObjectMap copy() {
        return new IntObjectMap<>(this);
    }

    /**
     * Clear the given key. The value will be set to {@link #noEntryValue()}.
     *
     * @param key the key.
     * @return the removed value.
     */
    public V clear(final int key) {
        if (key >= 0 && key < values.length) {
            V old = values[key];
            values[key] = noValue;
            if (old != noValue) {
                count--;
            }
            return old;
        }
        return noValue;
    }

    /**
     * Clear the map.
     */
    public void clear() {
        Arrays.fill(values, noValue);
        count = 0;
    }

    /**
     * Iterate over the map entries.
     * This should not be used for any performance critical computation as the performance will depend on the key
     * density.
     *
     * @param e the iterator to use.
     */
    public void forEach(final Entry e) {
        if (count == 0) {
            return;
        }
        for (int i = 0; i <= lastKey; i++) {
            final V v = values[i];
            if (v == noValue) {
                continue;
            }
            if (!e.entry(i, v)) {
                break;
            }
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        IntObjectMap intMap = (IntObjectMap) o;
        return noValue == intMap.noValue && Arrays.equals(values, intMap.values);
    }

    @Override
    public int hashCode() {
        int result = Objects.hash(noValue);
        result = 31 * result + Arrays.hashCode(values);
        return result;
    }

    /**
     * Interface used to iterate over the entries.
     */
    @FunctionalInterface
    public interface Entry {

        /**
         * Gives an entry.
         *
         * @param k the key
         * @param v the value
         * @return {@code true} to continue the iteration.
         */
        boolean entry(final int k, final V v);
    }

    /**
     * Get the number of entries in the map.
     *
     * @return a positive number.
     */
    public int size() {
        return count;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy