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

vlsi.utils.CompactHashMapClass Maven / Gradle / Ivy

There is a newer version: 0.40.13
Show newest version
/*
 * Copyright (c) 2011
 *
 * This file is part of CompactMap
 *
 * CompactMap 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.
 *
 * CompactMap 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 CompactMap.  If not, see .
 */

package vlsi.utils;

import com.github.andrewoma.dexx.collection.Pair;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.*;

abstract class CompactHashMapClass {
    public static final CompactHashMapClass EMPTY = new CompactHashMapClassEmptyDefaults(
            new com.github.andrewoma.dexx.collection.HashMap());

    final com.github.andrewoma.dexx.collection.Map key2slot; // Immutable

    // This value is used as a marker of deleted object
    // "new String" is required to avoid clashing with regular strings
    public static final String REMOVED_OBJECT = new String("Non existing mapping value");

    public CompactHashMapClass(com.github.andrewoma.dexx.collection.Map key2slot) {
        this.key2slot = key2slot;
    }

    protected Map getDefaultValues() {
        return Collections.emptyMap();
    }

    protected abstract CompactHashMapClassEmptyDefaults getMapWithEmptyDefaults();

    public V get(CompactHashMap map, K key) {
        Object result = getInternal(map, key);
        return result != REMOVED_OBJECT ? (V) result : null;
    }

    private Object getInternal(CompactHashMap map, Object key) {
        final Integer slot = key2slot.get((K) key);
        if (slot == null)
            return getDefaultValues().get(key);

        return getValueFromSlot(map, slot);
    }

    protected static Object getValueFromSlot(CompactHashMap map, int slot) {
        switch (slot) {
            case -1:
                return map.v1;
            case -2:
                return map.v2;
            case -3:
                return map.v3;
        }

        return ((Object[]) map.v1)[slot];
    }

    public V put(CompactHashMap map, K key, Object value) {
        Integer slot = key2slot.get(key);
        Object prevValue = REMOVED_OBJECT;
        if (slot == null) {
            prevValue = getDefaultValues().get(key);

            // Try put value as "default"
            Map newDef = CompactHashMapDefaultValues.getNewDefaultValues(getDefaultValues(), key, value);
            if (newDef != null) {
                map.klass = getMapWithEmptyDefaults().getNewDefaultClass(newDef);
                return (V) prevValue;
            }

            if (value == REMOVED_OBJECT)
                return (V) prevValue;
            // The value is not default -- put using regular way
            slot = createNewSlot(map, key);
        }

        switch (slot) {
            case -1:
                if (prevValue == REMOVED_OBJECT)
                    prevValue = map.v1;
                map.v1 = value;
                break;
            case -2:
                if (prevValue == REMOVED_OBJECT)
                    prevValue = map.v2;
                map.v2 = value;
                break;
            case -3:
                if (prevValue == REMOVED_OBJECT)
                    prevValue = map.v3;
                map.v3 = value;
                break;
            default:
                Object[] array = (Object[]) map.v1;
                if (prevValue == REMOVED_OBJECT)
                    prevValue = array[slot];
                array[slot] = value;
                break;
        }

        return (V) prevValue;
    }

    private Integer createNewSlot(CompactHashMap map, K key) {
        final CompactHashMapClass nextKlass = getMapWithEmptyDefaults().getNextKlass(key, getDefaultValues());
        map.klass = nextKlass;

        int prevSize = key2slot.size();

        if (prevSize == 3) {
            // Array length should be odd to play well with 8 byte alignment of object size
            //  1.5 refs (object header) + 1 int (array length) + n*length refs (contents)
            Object[] array = new Object[4];
            array[0] = map.v1;
            map.v1 = array;
        } else if (prevSize > 3) {
            Object[] array = (Object[]) map.v1;
            if (array.length < prevSize - 1) {
                int newSize = array.length * 3 / 2;
                newSize += newSize & 1; // If odd, round to next even
                Object[] newArray = new Object[newSize];
                System.arraycopy(array, 0, newArray, 0, array.length);
                map.v1 = newArray;
            }
        }

        return nextKlass.key2slot.get(key);
    }

    public int size(CompactHashMap map) {
        return key2slot.size() + getDefaultValues().size() - removedSlotsCount(map);
    }

    private int removedSlotsCount(CompactHashMap map) {
        int emptySlots = 0;
        switch (key2slot.size()) {
            default: // more than 3
                for (Object o : (Object[]) map.v1) {
                    if (o == REMOVED_OBJECT) emptySlots++;
                }
                /* fall through */
            case 3: // v1 is filled after v2
                if (map.v1 == REMOVED_OBJECT) emptySlots++;
                /* fall through */
            case 2: // v2 is filled after v3
                if (map.v2 == REMOVED_OBJECT) emptySlots++;
                /* fall through */
            case 1: // v3 is filled the first
                if (map.v3 == REMOVED_OBJECT) emptySlots++;
            case 0:
        }

        return emptySlots;
    }

    public boolean containsKey(CompactHashMap map, Object key) {
        // We cannot use plain getInternal here since we will be unable to distinguish
        // existing, but null default value
        final Integer slot = key2slot.get((K) key);
        if (slot == null)
            return getDefaultValues().containsKey(key);

        return getValueFromSlot(map, slot) != REMOVED_OBJECT;
    }

    public Set keySet(CompactHashMap map) {
        return new CompactHashMapClass.KeySet(map);
    }

    public Set values(CompactHashMap map) {
        return new CompactHashMapClass.Values(map);
    }

    public Set> entrySet(CompactHashMap map) {
        return new CompactHashMapClass.EntrySet(map);
    }

    public void serialize(final CompactHashMap map, final ObjectOutputStream s) throws IOException {
        // We serialize default and non default values separately
        // That makes serialized representation more compact when several maps share defaults
        int size = key2slot.size() - removedSlotsCount(map);
        s.writeInt(size);

        if (size > 0)
            for (Pair entry : key2slot) {
                Object value = getValueFromSlot(map, entry.component2());
                if (value == REMOVED_OBJECT) continue;
                s.writeObject(entry.component1());
                s.writeObject(value);
            }

        // Serialize default values as separate map
        s.writeObject(getDefaultValues());
    }

    public static  void deserialize(CompactHashMap map, ObjectInputStream s) throws IOException, ClassNotFoundException {
        int size = s.readInt();
        map.klass = CompactHashMapClass.EMPTY;

        for (int i = 0; i < size; i++) {
            K key = (K) s.readObject();
            V value = (V) s.readObject();
            map.put(key, value);
        }

        Map defaults = (Map) s.readObject();
        // TODO: optimize to CompactHashMapClassEmptyDefaults.getNewDefaultClass
        // Current implementation of getNewDefaultClass relies on identity equality, thus it does not fit
        for (Map.Entry entry : defaults.entrySet()) {
            map.put(entry.getKey(), entry.getValue());
        }
    }

    static class KeySet extends AbstractSet {
        private final CompactHashMap map;

        public KeySet(CompactHashMap map) {
            this.map = map;
        }

        @Override
        public int size() {
            return map.size();
        }

        @Override
        public boolean contains(Object o) {
            return map.containsKey(o);
        }

        @Override
        public boolean remove(Object o) {
            return map.remove(o) != null; // TODO: support null as "previous" value
        }

        @Override
        public Iterator iterator() {
            return new KeyIterator(map);
        }

        @Override
        public void clear() {
            map.clear();
        }
    }

    static class Values extends AbstractSet {
        private final CompactHashMap map;

        public Values(CompactHashMap map) {
            this.map = map;
        }

        @Override
        public int size() {
            return map.size();
        }

        @Override
        public boolean contains(Object o) {
            return map.containsValue(o);
        }

        @Override
        public Iterator iterator() {
            return new ValueIterator(map);
        }

        @Override
        public void clear() {
            map.clear();
        }
    }

    static class EntrySet extends AbstractSet> {
        private final CompactHashMap map;

        public EntrySet(CompactHashMap map) {
            this.map = map;
        }

        @Override
        public Iterator> iterator() {
            return new EntryIterator(map);
        }

        @Override
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry) o;
            Object mapValue = map.get(e.getKey());
            return mapValue == e.getValue() || mapValue != null && mapValue.equals(e.getValue());
        }

        @Override
        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry) o;
            return map.remove(e.getKey()) != null; // TODO: support "return true" when value was null
        }

        @Override
        public int size() {
            return map.size();
        }

        @Override
        public void clear() {
            map.clear();
        }
    }

    static abstract class HashIterator implements Iterator {
        boolean defValues = true;
        private final CompactHashMap map;
        Iterator it;
        Map.Entry current, next;

        public HashIterator(CompactHashMap map) {
            this.map = map;
            if (map.isEmpty()) return;
            this.it = map.klass.getDefaultValues().entrySet().iterator();
            advance();
        }

        private void advance() {
            if (!it.hasNext() && defValues) {
                defValues = false;
                it = map.klass.key2slot.asMap().entrySet().iterator();
            }

            if (!it.hasNext()) {
                next = null;
                return;
            }

            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                V value;
                if (defValues)
                    value = (V) entry.getValue();
                else {
                    value = (V) getValueFromSlot(map, (Integer) entry.getValue());
                    if (value == REMOVED_OBJECT) continue;
                }
                next = new SimpleEntry(map, (K) entry.getKey(), value);
                return;
            }
            next = null;
        }

        public boolean hasNext() {
            return next != null;
        }

        public Map.Entry nextEntry() {
            if (next == null)
                throw new NoSuchElementException();
            current = next;
            advance();
            return current;
        }

        public void remove() {
            map.remove(current.getKey());
        }
    }


    static class KeyIterator extends HashIterator {
        public KeyIterator(CompactHashMap kvCompactMap) {
            super(kvCompactMap);
        }

        public K next() {
            return nextEntry().getKey();
        }
    }

    static class ValueIterator extends HashIterator {
        public ValueIterator(CompactHashMap kvCompactMap) {
            super(kvCompactMap);
        }

        public V next() {
            return nextEntry().getValue();
        }
    }

    static class EntryIterator extends HashIterator> {
        public EntryIterator(CompactHashMap kvCompactMap) {
            super(kvCompactMap);
        }

        public Map.Entry next() {
            return nextEntry();
        }
    }

    static class SimpleEntry implements Map.Entry {
        final K key;
        V value;
        private final CompactHashMap map;

        public SimpleEntry(CompactHashMap map, K key, V value) {
            this.map = map;
            this.key = key;
            this.value = value;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            this.value = value;
            return map.put(key, value);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy