具有软引用特性的哈希表类

      软引用类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会在引发“内存不足”异常前先回收这些对象的内存。软引用可用来实现内存敏感的高速缓存。

      Java自带了一个 WeakHashMap 类用以实现弱引用,自带一个 SoftCache 类用以实现软引用缓存,以下给出自定义的软引用类的源码。

package com.ffcs.ods.common.util;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

/**
 * @版权:福富软件 版权所有 (c)
 * @文件:com.ffcs.ods.common.util.SoftHashMap.java
 * @author: g.yanjj
 * @see: Map, HashMap, WeakHashMap, SoftCache
 * @创建日期:
 * @功能说明:具有软引用特性的哈希表,可用于存放缓存数据
 * @修改记录:
 */
public class SoftHashMap<K,V> implements Map<K,V> {
    /**
     * 用来存放软引用的内部Map
     */
    private final Map<K, SoftReferValue<K,V>> hash = new HashMap<K, SoftReferValue<K,V>>();
    /**
     * 用来存放已被清除的软引用对象
     */
    private ReferenceQueue<V> queue = new ReferenceQueue<V>();
    /**
     * 需要保留最近使用的缓存的数量,为0则代表不用特意保留缓存
     */
    private final int HARD_SIZE;
    /**
     * 用来保留最近使用的缓存的FIFO链表,将最近使用过的缓存存入链首
     */
    private final LinkedList<V> hardCache = new LinkedList<V>();

    /**
     * 具有软引用特性的哈希表
     */
    public SoftHashMap() {
        HARD_SIZE = 0;
    }

    /**
     * 具有软引用特性的哈希表
     * 
     * @param keepSize 指定最多需要保留的最近使用过的缓存数量
     */
    public SoftHashMap(int keepSize) {
        HARD_SIZE = keepSize;
    }
    
    /**
     * 清除所有的键值对,执行后哈希表为空。
     */
    @Override
    public void clear() {
        hardCache.clear();
        clearReferQueue();
        hash.clear();
    }

    /**
     * 判断哈希表是否包含指定的键
     *
     * @param key 要判断的键对象,键对象为null时一定返回false。
     * @return <tt>true</tt> 包含指定键,<tt>false</tt> 不包含指定键。
     */
    @Override
    public boolean containsKey(Object key) {
        if (null != key){
            clearReferQueue();
            return hash.containsKey(key);
        } else {
            return false;
        }
    }

    /**
     * 判断哈希表是否包含有指定的值
     *
     * @param value 要判断的值对象,值对象为null时一定返回false。
     * @return <tt>true</tt> 包含指定值,<tt>false</tt> 不包含指定值。
     */
    @Override
    public boolean containsValue(Object value) {
        if (null != value) {
            clearReferQueue();
            for (SoftReferValue<K,V> srv : hash.values()) {
                if (value.equals(srv.get())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * SoftHashMap类并没有使用内部集,因此此方法会抛出一个异常
     *
     * @return 不会返回任何对象,抛出异常
     * @exception UnsupportedOperationException
     */
    @Override
    public Set<Map.Entry<K,V>> entrySet() {
        //没有内部集合类
        throw new UnsupportedOperationException();
    }

    /**
     * 根据键对象,获取值对象。
     * 
     * <p>由于值对象可能会被回收,此方法只会返回键存在,并且未被回收的值对象。
     * 
     * <p>如果在构造函数中指定了“缓存保留数”,则最近获取的“缓存保留数”个数的值对象将不会被回收。
     * 
     * @param key 要用于取值的键对象
     * @return 返回对应的未被回收的值对象,如果键不存在或值对象已被回收,则返回{@code null}
     */
    @Override
    public V get(Object key) {
        V result = null;
        //根据键获取值的引用
        SoftReferValue<K,V> valueRef = hash.get(key);
        if (null != valueRef) {
            //软引用所指向的对象有可能已被回收,因此需要判断目标对象是否为空
            result = valueRef.get();
            if (null == result) {
                //如果目标对象已被回收,则从哈希表中移除这个无效键值对
                hash.remove(key);
                clearReferQueue();
            } else {
                //如果需要保留最近使用的缓存,则将目标对象推入强引用链表
                if (HARD_SIZE > 0) {
                    hardCache.addFirst(result);
                    if (hardCache.size() > HARD_SIZE) {
                        hardCache.removeLast();
                    }
                }
            }
        }
        return result; 
    }

    /**
     * 判断哈希表是否是空的
     *
     * @return <tt>true</tt>哈希表没有任何键值对,<tt>false</tt>哈希表至少有一个键值对。
     */
    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    /**
     * 返回哈希表的键对象的集合
     */
    @Override
    public Set<K> keySet() {
        return hash.keySet();
    }

    /**
     * 将键值对添加到哈希表
     *
     * @param key 键对象类似一个索引,可用于查找值对象
     * @param value 值对象是保存实际数据的对象,可通过键对象查找
     * @return 如果该键对象之前已在哈希表中存在,则返回原有的值对象。
     *         如果返回{@code null},可能是该键对象第一次添加到哈希表,或原先存放的值对象为null,或原先存放的值对象已被回收。
     */
    @Override
    public V put(K key, V value) {
        clearReferQueue();
        SoftReferValue<K,V> srv = new SoftReferValue<K,V>(value, key, queue);
        srv = hash.put(key, srv);
        if (null == srv){
            return null;
        } else {
            return srv.get();
        }
    }

    /**
     * 将指定Map中的键值对,批量添加到哈希表
     *
     * @param m 要添加到哈希表的源表
     */
    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
            Map.Entry<? extends K, ? extends V> e = i.next();
            hash.put(e.getKey(), new SoftReferValue<K,V>(e.getValue(), e.getKey(), queue));
        }
    }

    /**
     * 根据指定键,从哈希表移除对应的键值对
     *
     * @param key 要移除的键
     * @return 返回被移除的值对象,如果哈希表不存在指定键则返回{@code null}
     */
    @Override
    public V remove(Object key) {
        clearReferQueue();
        SoftReferValue<K,V> srv = hash.remove(key);
        if (null == srv){
            return null;
        } else {
            return srv.get();
        }
    }

    /**
     * 返回哈希表有效的键值对数量,在返回之前,已被回收的值对象将被移除
     *
     * @return 哈希表有效的键值对数量
     */
    @Override
    public int size() {
        clearReferQueue();
        return hash.size();
    }

    /**
     * SoftHashMap类不支持返回值对象集合,此方法会抛出一个异常
     *
     * @return 不会返回任何对象,抛出异常
     * @exception UnsupportedOperationException
     */
    @Override
    public Collection<V> values() {
        //不支持获取软引用集合
        throw new UnsupportedOperationException();
    }

    /**
     * 继承的软引用内部类,加入key属性是为了方便从值对象查找对应的键
     */
    private static class SoftReferValue<K,V> extends SoftReference<V> {
        private final K key;
        private SoftReferValue(V value, K key, ReferenceQueue<V> queue) {
            super(value, queue);
            this.key = key;
        }
    }

    /**
     * 清空引用队列,清除已被回收的软引用对象
     */
    private void clearReferQueue() {
        SoftReferValue<K,V> srv;
        while ((srv = (SoftReferValue<K,V>) queue.poll()) != null) {
            hash.remove(srv.key);
        }
    }
}

使用示例:

public class SysManagerImpl extends BaseManager implements ServletContextAware {
    private ServletContext servletContext;

    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    private List getPubDomain(String domainId) {
        final String contextName = "SYSCACHE.PUB_DOMAIN";
        final String cacheName = domainId;
        List result = null;
        //从上下文中获取缓存数据
        Map<String, List> cacheData = (Map<String, List>) servletContext.getAttribute(contextName);
        if (null == cacheData){
            cacheData = new SoftHashMap<String, List>();
        } else {
            if (cacheData.containsKey(cacheName)) {
                result = cacheData.get(cacheName);
            }
        }
        //如果上下文中没有指定缓存,则重新从数据库获取
        if (null == result) {
            result = getPubDomainForSQL(domainId);
            cacheData.put(cacheName, result);
            servletContext.setAttribute(contextName, cacheData);
        }
        //返回指定数据
        return result;
    }
}

 查询所有缓存示例:

 

public List querySysCache() {
    	List result = new ArrayList();
    	Enumeration<E> e = servletContext.getAttributeNames();
    	while (e.hasMoreElements()) {
    		String domainName = e.nextElement().toString();
    		if (domainName.startsWith("SYSCACHE.")) {
    			Map cacheData = (Map) servletContext.getAttribute(domainName);
    			for (Object cacheName : cacheData.keySet()) {
    				String[] data = new String[2];
    				data[0] = domainName.substring(9);
    				data[1] = cacheName.toString();
    			}
    		}
    	}
    	return result;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值