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

de.tsl2.nano.collection.ReferenceMap Maven / Gradle / Ivy

There is a newer version: 2.5.1
Show newest version
/*
 * File: $HeadURL$
 * Id  : $Id$
 * 
 * created by: Tom
 * created on: 26.12.2013
 * 
 * Copyright: (c) Thomas Schneider 2013, all rights reserved
 */
package de.tsl2.nano.collection;

/**
 * Simple HashMap holding it's values as Soft- or Weak References. To have weak references on keys, use java's WeakHashMap.
 * @author Tom
 * @version $Revision$ 
 */
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

public class ReferenceMap extends AbstractMap
        implements Serializable {
    /** serialVersionUID */
    private static final long serialVersionUID = 1L;

    /** internal HashMap holding the Reference. */
    private final Map> hash =
        new HashMap>();

    private final Map, K> reverseLookup =
        new HashMap, K>();

    /** Reference queue for cleared Reference objects. */
    transient private final ReferenceQueue queue = new ReferenceQueue();

    private boolean weak = true;

    /**
     * hashmap as weak map
     */
    public ReferenceMap() {
        this(true);
    }

    /**
     * constructor
     * @param weak if false, {@link SoftReference}s will be used on it's values.
     */
    public ReferenceMap(boolean weak) {
        super();
        this.weak = weak;
    }
    
    /**
     * creates a reference using {@link #weak} to instantiate {@link WeakReference} or {@link SoftReference}.
     * 
     * @param value value
     * @param queue queue
     * @return new {@link Reference} instance
     */
    protected Reference createReference(V value, ReferenceQueue queue) {
        return weak ? new WeakReference(value, queue) : new SoftReference(value, queue);
    }

    @Override
    public V get(Object key) {
        expungeStaleEntries();
        V result = null;
        // We get the Reference represented by that key
        Reference soft_ref = hash.get(key);
        if (soft_ref != null) {
            // From the Reference we get the value, which can be
            // null if it has been garbage collected
            result = soft_ref.get();
            if (result == null) {
                // If the value has been garbage collected, remove the
                // entry from the HashMap.
                hash.remove(key);
                reverseLookup.remove(soft_ref);
            }
        }
        return result;
    }

    private void expungeStaleEntries() {
        Reference sv;
        while ((sv = queue.poll()) != null) {
            hash.remove(reverseLookup.remove(sv));
        }
    }

    @Override
    public V put(K key, V value) {
        expungeStaleEntries();
        Reference soft_ref = createReference(value, queue);
        reverseLookup.put(soft_ref, key);
        Reference result = hash.put(key, soft_ref);
        if (result == null) {
            return null;
        }
        reverseLookup.remove(result);
        return result.get();
    }

    @Override
    public V remove(Object key) {
        expungeStaleEntries();
        Reference result = hash.remove(key);
        if (result == null) {
            return null;
        }
        return result.get();
    }

    @Override
    public void clear() {
        hash.clear();
        reverseLookup.clear();
    }

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

    /**
     * Returns a copy of the key/values in the map at the point of calling. However, setValue still sets the value in
     * the actual HashMap.
     */
    @Override
    public Set> entrySet() {
        expungeStaleEntries();
        Set> result = new LinkedHashSet>();
        for (final Entry> entry : hash.entrySet()) {
            final V value = entry.getValue().get();
            if (value != null) {
                result.add(new Entry() {
                    @Override
                    public K getKey() {
                        return entry.getKey();
                    }

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

                    @Override
                    public V setValue(V v) {
                        entry.setValue(createReference(v, queue));
                        return value;
                    }
                });
            }
        }
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy