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

dorkbox.collections.ConcurrentWeakIdentityHashMap Maven / Gradle / Ivy

/*
 * Copyright 2016 zhanhb.
 *
 * 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 dorkbox.collections;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @param 
 * @param 
 *
 * @author zhanhb
 */
class ConcurrentWeakIdentityHashMap extends AbstractMap implements ConcurrentMap {
    public static final String version = Collections.version;

    private final ConcurrentMap, V> map;
    private final ReferenceQueue queue = new ReferenceQueue<>();
    private transient Set> es;

    ConcurrentWeakIdentityHashMap(int initialCapacity) {
        this.map = new ConcurrentHashMap<>(initialCapacity);
    }

    @SuppressWarnings("CollectionWithoutInitialCapacity")
    ConcurrentWeakIdentityHashMap() {
        this.map = new ConcurrentHashMap<>();
    }

    @Override
    public
    V get(Object key) {
        purgeKeys();
        return map.get(new Key<>(key, null));
    }

    @Override
    public
    V put(K key, V value) {
        purgeKeys();
        return map.put(new Key<>(key, queue), value);
    }

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

    @SuppressWarnings({"NestedAssignment", "element-type-mismatch"})
    private
    void purgeKeys() {
        Reference reference;
        while ((reference = queue.poll()) != null) {
            map.remove(reference);
        }
    }

    @Override
    @SuppressWarnings("NestedAssignment")
    public
    Set> entrySet() {
        Set> entrySet;
        return ((entrySet = this.es) == null) ? es = new EntrySet() : entrySet;
    }

    @Override
    public
    V putIfAbsent(K key, V value) {
        purgeKeys();
        return map.putIfAbsent(new Key<>(key, queue), value);
    }

    @Override
    public
    V remove(Object key) {
        return map.remove(new Key<>(key, null));
    }

    @Override
    public
    boolean remove(Object key, Object value) {
        purgeKeys();
        return map.remove(new Key<>(key, null), value);
    }

    @Override
    public
    boolean replace(K key, V oldValue, V newValue) {
        purgeKeys();
        return map.replace(new Key<>(key, null), oldValue, newValue);
    }

    @Override
    public
    V replace(K key, V value) {
        purgeKeys();
        return map.replace(new Key<>(key, null), value);
    }

    @Override
    public
    boolean containsKey(Object key) {
        purgeKeys();
        return map.containsKey(new Key<>(key, null));
    }

    @Override
    @SuppressWarnings("empty-statement")
    public
    void clear() {
        while (queue.poll() != null) {
        }
        map.clear();
    }

    @Override
    public
    boolean containsValue(Object value) {
        purgeKeys();
        return map.containsValue(value);
    }

    private static
    class Key extends WeakReference {

        private final int hash;

        Key(T t, ReferenceQueue queue) {
            super(t, queue);
            hash = System.identityHashCode(Objects.requireNonNull(t));
        }

        @Override
        public
        boolean equals(Object obj) {
            return this == obj || obj instanceof Key && ((Key) obj).get() == get();
        }

        @Override
        public
        int hashCode() {
            return hash;
        }

    }


    private
    class Iter implements Iterator> {

        private final Iterator, V>> it;
        private Map.Entry nextValue;

        Iter(Iterator, V>> it) {
            this.it = it;
        }

        @Override
        public
        boolean hasNext() {
            if (nextValue != null) {
                return true;
            }
            while (it.hasNext()) {
                Map.Entry, V> entry = it.next();
                K key = entry.getKey().get();
                if (key != null) {
                    nextValue = new Entry(key, entry.getValue());
                    return true;
                }
                else {
                    it.remove();
                }
            }
            return false;
        }

        @Override
        public
        Map.Entry next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            Map.Entry entry = nextValue;
            nextValue = null;
            return entry;
        }

        @Override
        public
        void remove() {
            it.remove();
            nextValue = null;
        }

    }


    private
    class EntrySet extends AbstractSet> {

        @Override
        public
        Iterator> iterator() {
            return new Iter(map.entrySet().iterator());
        }

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

        @Override
        public
        void clear() {
            ConcurrentWeakIdentityHashMap.this.clear();
        }

        @Override
        @SuppressWarnings("element-type-mismatch")
        public
        boolean contains(Object o) {
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            Map.Entry e = (Map.Entry) o;
            return ConcurrentWeakIdentityHashMap.this.get(e.getKey()) == e.getValue();
        }

        @Override
        public
        boolean remove(Object o) {
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            Map.Entry e = (Map.Entry) o;
            return ConcurrentWeakIdentityHashMap.this.remove(e.getKey(), e.getValue());
        }
    }


    private
    class Entry extends SimpleEntry {

        private static final long serialVersionUID = 1L;

        Entry(K key, V value) {
            super(key, value);
        }

        @Override
        public
        V setValue(V value) {
            ConcurrentWeakIdentityHashMap.this.put(getKey(), value);
            return super.setValue(value);
        }

        @Override
        public
        boolean equals(Object obj) {
            if (obj instanceof Map.Entry) {
                Map.Entry e = (Map.Entry) obj;
                return getKey() == e.getKey() && getValue() == e.getValue();
            }
            return false;
        }

        @Override
        public
        int hashCode() {
            return System.identityHashCode(getKey()) ^ System.identityHashCode(getValue());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy