Thu. Mar 28th, 2024

Sometimes you need to maintain a large number of objects in memory. If it is strings you may want to take a look at StringCache, which is based on my Cache class presented here. Now the Cache workers simply call create an instance of Cache:

Cache<Person> peopleCache = new Cache<>();

This will give you a Cache for Person objects. The key to this, the equals() method must be implemented and able to distinguish if two objects are truly equal or not. This is only as accurate as your equals() method. Now to use the cache just as simple as:

Person person = peopleCache.cache(somePerson);

If somePerson already exists in the cache either as itself, or as an object that is equals() then it will return the cached object to use, otherwise, it will add the somePerson object to the cache, and return the object. The complete implementation of the class can be found here:



import name.mymiller.utils.ListUtils;

import java.lang.ref.WeakReference;
import java.util.List;
import java.util.WeakHashMap;

/**
 * Object Cache to remove redundant Objects that contain the same data.
 *
 * @author jmiller
 */
public class Cache<E> {
    /**
     * Initial capacity of the cache.
     */
    private static final int globalInitialCapacity = 65535;
    /**
     * Initial Load factor to grow the map.
     */
    private static final float globalLoadFactor = (float) 0.75;
    
    /**
     * Number of collisions the cache has saved.
     */
    private int collisionCount = 0;
    /**
     * Weak has map to allow Objects to be removed when only the cache has a link to
     * them.
     */
    private WeakHashMap<E, WeakReference<E>> cache = null;

    /**
     * Protected constructor forcing the use of the getInstance methods.
     */
    public Cache() {
        this(Cache.globalInitialCapacity, Cache.globalLoadFactor);
    }

    /**
     * Protected constructor allowing the configuration of the capacity and load
     * factor
     *
     * @param globalInitialCapacity Initial Capacity of the cache
     * @param globalLoadFactor      Load factor for growth.
     */
    public Cache(final int globalInitialCapacity, final float globalLoadFactor) {
        this.cache = new WeakHashMap<>(globalInitialCapacity, globalLoadFactor);
    }
    
    /**
     * Returns the string matching the string from the cache.
     *
     * @param key Object to find cache value.
     * @return Cached instance of the string.
     */
    public synchronized E cache(final E key) {
        E cached = null;
        if (this.cache.containsKey(key)) {
            final WeakReference<E> ref = this.cache.get(key);

            if (ref != null) {
                cached = ref.get();
                this.collisionCount++;
            }
        }

        if (cached == null) {
            this.cache.put(key, new WeakReference<>(key));
            cached = key;
        }

        return cached;
    }

    /**
     * Clears the cache
     */
    public synchronized void clear() {
        this.cache.clear();
        this.collisionCount = 0;
    }

    /**
     * Determines if the cache contains an Object.
     *
     * @param key Object to check if it is in the Cache.
     * @return boolean indicating if the Object was found.
     * @see WeakHashMap#containsKey(Object)
     */
    public synchronized boolean containsKey(final E key) {
        return this.cache.containsKey(key);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.name.mymiller.lang.Object#equals(java.name.mymiller.
     * extensions.lang.Object)
     */
    @Override
    public synchronized boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Cache)) {
            return false;
        }
        final Cache other = (Cache) obj;
        if (this.cache == null) {
            return other.cache == null;
        } else {
            return this.cache.equals(other.cache);
        }
    }

    /**
     * Returns a Set view of the strings this cache.
     *
     * @return Set View
     */
    public synchronized List<E> getCached() {
        return ListUtils.iterate(this.cache.keySet().iterator());
    }

    /**
     * @return Number of Collisions that have occurred.
     */
    public int getCollisionCount() {
        return this.collisionCount;
    }

    /*
     * (non-Javadoc)
     *
     * @see java.name.mymiller.lang.Object#hashCode()
     */
    @Override
    public synchronized int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((this.cache == null) ? 0 : this.cache.hashCode());
        return result;
    }

    /**
     * Inserts an existing Object cache into this instance.
     *
     * @param insert Cache to insert.
     */
    protected synchronized void insertCache(final Cache<E> insert) {
        this.cache.putAll(insert.cache);
    }

    /**
     * Remove an Object from the Cache.
     *
     * @param key Object to remove.
     */
    public synchronized void remove(final E key) {
        this.cache.remove(key);
    }

    /**
     * @return The size of the Cache.
     * @see WeakHashMap#size()
     */
    public synchronized int size() {
        return this.cache.size();
    }
}

By Jeffery Miller

I am known for being able to quickly decipher difficult problems to assist development teams in producing a solution. I have been called upon to be the Team Lead for multiple large-scale projects. I have a keen interest in learning new technologies, always ready for a new challenge.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d