ArrayMap.java Source code

Java tutorial

Introduction

Here is the source code for ArrayMap.java

Source

/*
 * Copyright (c) 2002-2008 "Neo Technology,"
 *     Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 * 
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

public class ArrayMap<K, V> {
    private ArrayEntry<K, V>[] arrayEntries;

    private volatile int arrayCount = 0;
    private int toMapThreshold = 5;
    private Map<K, V> propertyMap = null;
    private final boolean useThreadSafeMap;
    private boolean switchBackToArray = false;

    public ArrayMap() {
        useThreadSafeMap = false;
        arrayEntries = new ArrayEntry[toMapThreshold];
    }

    public ArrayMap(int mapThreshold, boolean threadSafe, boolean shrinkToArray) {
        this.toMapThreshold = mapThreshold;
        this.useThreadSafeMap = threadSafe;
        this.switchBackToArray = shrinkToArray;
        arrayEntries = new ArrayEntry[toMapThreshold];
    }

    public void put(K key, V value) {
        if (useThreadSafeMap) {
            synchronizedPut(key, value);
            return;
        }
        for (int i = 0; i < arrayCount; i++) {
            if (arrayEntries[i].getKey().equals(key)) {
                arrayEntries[i].setNewValue(value);
                return;
            }
        }
        if (arrayCount != -1) {
            if (arrayCount < arrayEntries.length) {
                arrayEntries[arrayCount++] = new ArrayEntry<K, V>(key, value);
            } else {
                propertyMap = new HashMap<K, V>();
                for (int i = 0; i < arrayCount; i++) {
                    propertyMap.put(arrayEntries[i].getKey(), arrayEntries[i].getValue());
                }
                arrayCount = -1;
                propertyMap.put(key, value);
            }
        } else {
            propertyMap.put(key, value);
        }
    }

    private synchronized void synchronizedPut(K key, V value) {
        for (int i = 0; i < arrayCount; i++) {
            if (arrayEntries[i].getKey().equals(key)) {
                arrayEntries[i].setNewValue(value);
                return;
            }
        }
        if (arrayCount != -1) {
            if (arrayCount < arrayEntries.length) {
                arrayEntries[arrayCount++] = new ArrayEntry<K, V>(key, value);
            } else {
                propertyMap = new ConcurrentHashMap<K, V>();
                for (int i = 0; i < arrayCount; i++) {
                    propertyMap.put(arrayEntries[i].getKey(), arrayEntries[i].getValue());
                }
                arrayEntries = null;
                arrayCount = -1;
                propertyMap.put(key, value);
            }
        } else {
            propertyMap.put(key, value);
        }
    }

    public V get(K key) {
        if (key == null) {
            return null;
        }
        if (useThreadSafeMap) {
            return synchronizedGet(key);
        }
        int count = arrayCount;
        for (int i = 0; i < count; i++) {
            if (key.equals(arrayEntries[i].getKey())) {
                return arrayEntries[i].getValue();
            }
        }
        if (arrayCount == -1) {
            return propertyMap.get(key);
        }
        return null;
    }

    private synchronized V synchronizedGet(K key) {
        for (int i = 0; i < arrayCount; i++) {
            if (key.equals(arrayEntries[i].getKey())) {
                return arrayEntries[i].getValue();
            }
        }
        if (arrayCount == -1) {
            return propertyMap.get(key);
        }
        return null;
    }

    private synchronized V synchronizedRemove(K key) {
        for (int i = 0; i < arrayCount; i++) {
            if (arrayEntries[i].getKey().equals(key)) {
                V removedProperty = arrayEntries[i].getValue();
                arrayCount--;
                System.arraycopy(arrayEntries, i + 1, arrayEntries, i, arrayCount - i);
                return removedProperty;
            }
        }
        if (arrayCount == -1) {
            V value = propertyMap.remove(key);
            if (switchBackToArray && propertyMap.size() < toMapThreshold) {
                arrayEntries = new ArrayEntry[toMapThreshold];
                int tmpCount = 0;
                for (Entry<K, V> entry : propertyMap.entrySet()) {
                    arrayEntries[tmpCount++] = new ArrayEntry<K, V>(entry.getKey(), entry.getValue());
                }
                arrayCount = tmpCount;
            }
            return value;
        }
        return null;
    }

    public V remove(K key) {
        if (useThreadSafeMap) {
            return synchronizedRemove(key);
        }
        for (int i = 0; i < arrayCount; i++) {
            if (arrayEntries[i].getKey().equals(key)) {
                V removedProperty = arrayEntries[i].getValue();
                arrayCount--;
                System.arraycopy(arrayEntries, i + 1, arrayEntries, i, arrayCount - i);
                return removedProperty;
            }
        }
        if (arrayCount == -1) {
            V value = propertyMap.remove(key);
            if (switchBackToArray && propertyMap.size() < toMapThreshold) {
                arrayEntries = new ArrayEntry[toMapThreshold];
                int tmpCount = 0;
                for (Entry<K, V> entry : propertyMap.entrySet()) {
                    arrayEntries[tmpCount++] = new ArrayEntry<K, V>(entry.getKey(), entry.getValue());
                }
                arrayCount = tmpCount;
            }
            return value;
        }
        return null;
    }

    static class ArrayEntry<K, V> implements Entry<K, V> {
        private K key;
        private V value;

        ArrayEntry(K key, V value) {
            this.key = key;
            this.value = value;
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        void setNewValue(V value) {
            this.value = value;
        }

        public V setValue(V value) {
            V oldValue = value;
            this.value = value;
            return oldValue;
        }
    }

    public Iterable<K> keySet() {
        if (arrayCount == -1) {
            return propertyMap.keySet();
        }
        List<K> keys = new LinkedList<K>();
        for (int i = 0; i < arrayCount; i++) {
            keys.add(arrayEntries[i].getKey());
        }
        return keys;
    }

    public Iterable<V> values() {
        if (arrayCount == -1) {
            return propertyMap.values();
        }
        List<V> values = new LinkedList<V>();
        for (int i = 0; i < arrayCount; i++) {
            values.add(arrayEntries[i].getValue());
        }
        return values;
    }

    public Set<Entry<K, V>> entrySet() {
        if (arrayCount == -1) {
            return propertyMap.entrySet();
        }
        Set<Entry<K, V>> entries = new HashSet<Entry<K, V>>();
        for (int i = 0; i < arrayCount; i++) {
            entries.add(arrayEntries[i]);
        }
        return entries;
    }

    public int size() {
        if (arrayCount != -1) {
            return arrayCount;
        }
        return propertyMap.size();
    }

    public void clear() {
        if (arrayCount != -1) {
            arrayCount = 0;
        } else {
            propertyMap.clear();
        }
    }
}