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

com.landawn.abacus.util.ObjectPool Maven / Gradle / Ivy

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 5.2.4
Show newest version
/*
 * Copyright (C) 2015 HaiYang Li
 *
 * 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 com.landawn.abacus.util;

import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.Internal;

/**
 * It's is a multiple-thread safety map with fixed size. it's designed for frequent get and few add/remove operation
 * with a few limited keys.
 *
 * @author Haiyang Li
 * @param  the key type
 * @param  the value type
 * @since 0.8
 */
@Internal
@Beta
@SuppressWarnings("java:S2160")
public final class ObjectPool extends AbstractMap {

    final int capacity;

    private final Entry[] table;

    private final int indexMask;

    private int _size = 0; //NOSONAR

    private transient Set _keySet = null; //NOSONAR

    private transient Collection _values = null; //NOSONAR

    private transient Set> _entrySet; //NOSONAR

    /**
     * 
     *
     * @param capacity 
     */
    @SuppressWarnings("unchecked")
    public ObjectPool(int capacity) {
        this.capacity = capacity;
        this.table = new Entry[capacity];
        this.indexMask = table.length - 1;
    }

    /**
     *
     * @param key
     * @return
     */
    @Override
    public V get(Object key) {
        final int hash = hash(key);
        final int i = hash & indexMask;

        for (Entry entry = table[i]; entry != null; entry = entry.next) {
            if ((hash == entry.hash) && key.equals(entry.key)) {
                return entry.value;
            }
        }

        return null;
    }

    /**
     *
     * @param key
     * @param value
     * @return
     */
    @Override
    public V put(K key, V value) {
        synchronized (table) {
            return internalPut(key, value);
        }
    }

    private V internalPut(K key, V value) {
        if ((key == null) || (value == null)) {
            throw new NullPointerException();
        }

        final int hash = hash(key);
        final int i = hash & indexMask;

        //            if (size() > capacity) {
        //                throw new IndexOutOfBoundsException("Object pool is full with capacity=" + capacity);
        //            }
        //
        for (Entry entry = table[i]; entry != null; entry = entry.next) {
            if ((hash == entry.hash) && key.equals(entry.key)) {
                V previousValue = entry.value;
                entry.value = value;

                return previousValue;
            }
        }

        Entry entry = new Entry<>(hash, key, value, table[i]);
        table[i] = entry;

        _keySet = null;
        _values = null;
        _entrySet = null;

        _size++;

        return null;
    }

    /**
     *
     * @param m
     */
    @Override
    public void putAll(Map m) {
        synchronized (table) {
            for (Map.Entry entry : m.entrySet()) {
                if (entry.getValue() != null) {
                    internalPut(entry.getKey(), entry.getValue());
                }
            }
        }
    }

    /**
     *
     * @param key
     * @return
     */
    @Override
    public V remove(Object key) {
        if (_size == 0) {
            return null;
        }

        final int hash = hash(key);
        final int i = hash & indexMask;

        synchronized (table) {
            Entry prev = table[i];
            Entry e = prev;

            while (e != null) {
                Entry next = e.next;

                if ((hash == e.hash) && key.equals(e.key)) {
                    if (prev == e) {
                        table[i] = next;
                    } else {
                        prev.next = next;
                    }

                    _keySet = null;
                    _values = null;
                    _entrySet = null;

                    _size--;

                    return e.value;
                }

                prev = e;
                e = next;
            }

            return null;
        }
    }

    /**
     *
     * @param key
     * @return
     */
    @Override
    public boolean containsKey(Object key) {
        final int hash = hash(key);
        final int i = hash & indexMask;

        for (Entry entry = table[i]; entry != null; entry = entry.next) {
            if ((hash == entry.hash) && (entry.value != null) && key.equals(entry.key)) {
                return true;
            }
        }

        return false;
    }

    /**
     *
     * @param value
     * @return
     */
    @Override
    public boolean containsValue(Object value) {
        if (value == null) {
            return false;
        }

        for (Entry element : table) {
            for (Entry entry = element; entry != null; entry = entry.next) {
                if ((entry.value != null) && value.equals(entry.value)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 
     *
     * @return 
     */
    @Override
    public Set keySet() {
        Set tmp = _keySet;

        if (tmp == null) {
            synchronized (table) {
                tmp = N.newHashSet(_size);

                for (Entry element : table) {
                    for (Entry entry = element; entry != null; entry = entry.next) {
                        if (entry.value != null) {
                            tmp.add(entry.key);
                        }
                    }
                }

                tmp = Collections.unmodifiableSet(tmp);

                _keySet = tmp;
            }
        }

        return tmp;
    }

    /**
     * 
     *
     * @return 
     */
    @Override
    public Collection values() {
        Collection tmp = _values;

        if (tmp == null) {
            synchronized (table) {
                tmp = N.newHashSet(_size);

                V value = null;

                for (Entry element : table) {
                    for (Entry entry = element; entry != null; entry = entry.next) {
                        value = entry.value;

                        if (value != null) {
                            tmp.add(value);
                        }
                    }
                }

                tmp = Collections.unmodifiableCollection(tmp);

                _values = tmp;
            }
        }

        return tmp;
    }

    /**
     * 
     *
     * @return 
     */
    @Override
    public Set> entrySet() {
        Set> tmp = _entrySet;

        if (tmp == null) {
            synchronized (table) {
                tmp = N.newHashSet(_size);

                for (Entry element : table) {
                    for (Entry entry = element; entry != null; entry = entry.next) {
                        if (entry.value != null) {
                            tmp.add(entry);
                        }
                    }
                }

                tmp = Collections.unmodifiableSet(tmp);

                _entrySet = tmp;
            }
        }

        return tmp;
    }

    /**
     * 
     *
     * @return 
     */
    @Override
    public int size() {
        return _size;
    }

    /**
     * Checks if is empty.
     *
     * @return true, if is empty
     */
    @Override
    public boolean isEmpty() {
        return _size == 0;
    }

    /**
     * Clear.
     */
    @Override
    public void clear() {
        synchronized (table) {
            Arrays.fill(table, null);

            _keySet = null;
            _values = null;
            _entrySet = null;

            _size = 0;
        }
    }

    /**
     *
     * @param key
     * @return
     */
    static int hash(Object key) {
        int h;

        return (key == null) ? 0 : ((h = key.hashCode()) ^ (h >>> 16));
    }

    /**
     * The Class Entry.
     *
     * @param  the key type
     * @param  the value type
     */
    static class Entry implements Map.Entry {

        /** The key. */
        final K key;

        /** The value. */
        V value;

        /** The next. */
        Entry next;

        /** The hash. */
        int hash;

        /**
         * Creates new entry.
         *
         * @param h
         * @param k
         * @param v
         * @param n
         */
        Entry(int h, K k, V v, Entry n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        /**
         * Gets the key.
         *
         * @return
         */
        @Override
        public final K getKey() {
            return key;
        }

        /**
         * Gets the value.
         *
         * @return
         */
        @Override
        public final V getValue() {
            return value;
        }

        /**
         * Sets the value.
         *
         * @param newValue
         * @return
         */
        @Override
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;

            return oldValue;
        }

        /**
         *
         * @param obj
         * @return
         */
        @Override
        public final boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }

            if (obj instanceof Map.Entry) {
                Map.Entry other = (Map.Entry) obj;

                return N.equals(getKey(), other.getKey()) && N.equals(getValue(), other.getValue());
            }

            return false;
        }

        /**
         *
         * @return
         */
        @Override
        public final int hashCode() {
            return N.hashCode(getKey()) ^ N.hashCode(getValue());
        }

        /**
         *
         * @return
         */
        @Override
        public final String toString() {
            return getKey() + "=" + getValue();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy