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

org.jboss.marshalling.util.IntKeyMap Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.marshalling.util;

import java.io.IOException;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * An integer-keyed map, optimized for fast copying.  Based on {@code FastCopyHashMap} by Jason T. Greene.
 *
 * @author Jason T. Greene
 * @author David M. Lloyd
 */
public final class IntKeyMap implements Cloneable, Serializable, Iterable> {

    /**
     * Same default as HashMap, must be a power of 2
     */
    private static final int DEFAULT_CAPACITY = 8;

    /**
     * MAX_INT - 1
     */
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 67%, just like IdentityHashMap
     */
    private static final float DEFAULT_LOAD_FACTOR = 0.67f;

    /**
     * The open-addressed table
     */
    private transient Entry[] table;

    /**
     * The current number of key-value pairs
     */
    private transient int size;

    /**
     * The next resize
     */
    private transient int threshold;

    /**
     * The user defined load factor which defines when to resize
     */
    private final float loadFactor;

    private static final long serialVersionUID = -6864280848239317243L;

    public IntKeyMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Can not have a negative size table!");
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (!(loadFactor > 0F && loadFactor <= 1F))
            throw new IllegalArgumentException("Load factor must be greater than 0 and less than or equal to 1");
        this.loadFactor = loadFactor;
        init(initialCapacity, loadFactor);
    }

    @SuppressWarnings("unchecked")
    public IntKeyMap(IntKeyMap map) {
        table = (Entry[]) map.table.clone();
        loadFactor = map.loadFactor;
        size = map.size;
        threshold = map.threshold;
    }

    @SuppressWarnings("unchecked")
    private void init(int initialCapacity, float loadFactor) {
        int c = 1;
        for (; c < initialCapacity; c <<= 1) ;
        threshold = (int) (c * loadFactor);
        // Include the load factor when sizing the table for the first time
        if (initialCapacity > threshold && c < MAXIMUM_CAPACITY) {
            c <<= 1;
            threshold = (int) (c * loadFactor);
        }
        table = (Entry[]) new Entry[c];
    }

    public IntKeyMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }

    public IntKeyMap() {
        this(DEFAULT_CAPACITY);
    }

    private int nextIndex(int index, int length) {
        index = (index >= length - 1) ? 0 : index + 1;
        return index;
    }

    private static boolean eq(Object o1, Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    private static int index(int hashCode, int length) {
        return hashCode & (length - 1);
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public V get(int key) {
        int length = table.length;
        int index = index(key, length);
        for (int start = index; ;) {
            Entry e = table[index];
            if (e == null)
                return null;
            if (e.key == key)
                return e.value;
            index = nextIndex(index, length);
            if (index == start) // Full table
                return null;
        }
    }

    public boolean containsKey(int key) {
        int length = table.length;
        int index = index(key, length);
        for (int start = index; ;) {
            Entry e = table[index];
            if (e == null)
                return false;
            if (e.key == key)
                return true;
            index = nextIndex(index, length);
            if (index == start) // Full table
                return false;
        }
    }

    public boolean containsValue(Object value) {
        for (Entry e : table)
            if (e != null && eq(value, e.value))
                return true;
        return false;
    }

    public V put(int key, V value) {
        Entry[] table = this.table;
        int hash = key;
        int length = table.length;
        int index = index(hash, length);
        for (int start = index; ;) {
            Entry e = table[index];
            if (e == null)
                break;
            if (e.key == key) {
                table[index] = new Entry(key, value);
                return e.value;
            }
            index = nextIndex(index, length);
            if (index == start)
                throw new IllegalStateException("Table is full!");
        }
        table[index] = new Entry(key, value);
        if (++size >= threshold)
            resize(length);
        return null;
    }

    @SuppressWarnings("unchecked")
    private void resize(int from) {
        int newLength = from << 1;
        // Can't get any bigger
        if (newLength > MAXIMUM_CAPACITY || newLength <= from)
            return;
        Entry[] newTable = new Entry[newLength];
        Entry[] old = table;
        for (Entry e : old) {
            if (e == null)
                continue;
            int index = index(e.key, newLength);
            while (newTable[index] != null)
                index = nextIndex(index, newLength);
            newTable[index] = e;
        }
        threshold = (int) (loadFactor * newLength);
        table = newTable;
    }

    public V remove(int key) {
        Entry[] table = this.table;
        int length = table.length;
        int start = index(key, length);
        for (int index = start; ;) {
            Entry e = table[index];
            if (e == null)
                return null;
            if (e.key == key) {
                table[index] = null;
                relocate(index);
                size--;
                return e.value;
            }
            index = nextIndex(index, length);
            if (index == start)
                return null;
        }
    }

    private void relocate(int start) {
        Entry[] table = this.table;
        int length = table.length;
        int current = nextIndex(start, length);
        for (; ;) {
            Entry e = table[current];
            if (e == null)
                return;
            // A Doug Lea variant of Knuth's Section 6.4 Algorithm R.
            // This provides a non-recursive method of relocating
            // entries to their optimal positions once a gap is created.
            int prefer = index(e.key, length);
            if ((current < prefer && (prefer <= start || start <= current))
                    || (prefer <= start && start <= current)) {
                table[start] = e;
                table[current] = null;
                start = current;
            }
            current = nextIndex(current, length);
        }
    }

    public void clear() {
        Entry[] table = this.table;
        for (int i = 0; i < table.length; i++)
            table[i] = null;
        size = 0;
    }

    @SuppressWarnings("unchecked")
    public IntKeyMap clone() {
        try {
            IntKeyMap clone = (IntKeyMap) super.clone();
            clone.table = table.clone();
            return clone;
        }
        catch (CloneNotSupportedException e) {
            // should never happen
            throw new IllegalStateException(e);
        }
    }

    public void printDebugStats() {
        int optimal = 0;
        int total = 0;
        int totalSkew = 0;
        int maxSkew = 0;
        for (int i = 0; i < table.length; i++) {
            Entry e = table[i];
            if (e != null) {
                total++;
                int target = index(e.key, table.length);
                if (i == target)
                    optimal++;
                else {
                    int skew = Math.abs(i - target);
                    if (skew > maxSkew) maxSkew = skew;
                    totalSkew += skew;
                }
            }
        }
        System.out.println(" Size:             " + size);
        System.out.println(" Real Size:        " + total);
        System.out.println(" Optimal:          " + optimal + " (" + (float) optimal * 100 / total + "%)");
        System.out.println(" Average Distance: " + ((float) totalSkew / (total - optimal)));
        System.out.println(" Max Distance:     " + maxSkew);
    }

    @SuppressWarnings("unchecked")
    private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();
        int size = s.readInt();
        init(size, loadFactor);
        for (int i = 0; i < size; i++) {
            int key = s.readInt();
            V value = (V) s.readObject();
            putForCreate(key, value);
        }
        this.size = size;
    }

    @SuppressWarnings("unchecked")
    private void putForCreate(int key, V value) {
        Entry[] table = this.table;
        int length = table.length;
        int index = index(key, length);
        Entry e = table[index];
        while (e != null) {
            index = nextIndex(index, length);
            e = table[index];
        }
        table[index] = new Entry(key, value);
    }

    private void writeObject(java.io.ObjectOutputStream s) throws IOException {
        s.defaultWriteObject();
        s.writeInt(size);
        for (Entry e : table) {
            if (e != null) {
                s.writeInt(e.key);
                s.writeObject(e.value);
            }
        }
    }

    /**
     * Iterate over the entries.  Read-only operation.
     *
     * @return the entry iterator
     */
    public Iterator> iterator() {
        return new Iterator>() {
            int i = 0;

            public boolean hasNext() {
                final Entry[] table = IntKeyMap.this.table;
                final int len = table.length;
                if (i == len) {
                    return false;
                }
                while (table[i] == null) {
                    if (++i == len) {
                        return false;
                    }
                }
                return false;
            }

            public Entry next() {
                if (! hasNext()) {
                    throw new NoSuchElementException();
                }
                return table[i++];
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    /**
     * A map entry.
     *
     * @param  the value type
     */
    public static final class Entry {

        private final int key;
        private final V value;

        private Entry(int key, V value) {
            this.key = key;
            this.value = value;
        }

        public int getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy