java中利用WeakHashMap实现缓存
简介
WeakHashMap是Java集合框架里的一员,从名字可以看出它是某种 Map。它的特殊之处在于 WeakHashMap 里的entry可能会被GC自动删除,即使程序员没有调用remove()或者clear()方法。
更直观的说,当使用 WeakHashMap 时,即使没有显示的添加或删除任何元素,也可能发生如下情况:
- 调用两次
size()方法返回不同的值;- 两次调用
isEmpty()方法,第一次返回false,第二次返回true;- 两次调用
containsKey()方法,第一次返回true,第二次返回false,尽管两次使用的是同一个key;- 两次调用
get()方法,第一次返回一个value,第二次返回null,尽管两次使用的是同一个对象。
遇到这么奇葩的现象,你是不是觉得使用者一定会疯掉?其实不然,WeekHashMap 的这个特点特别适用于需要缓存的场景。在缓存场景下,由于内存是有限的,不能缓存所有对象;对象缓存命中可以提高系统效率,但缓存MISS也不会造成错误,因为可以通过计算重新得到。
要明白 WeekHashMap 的工作原理,还需要引入一个概念:弱引用(WeakReference)。我们都知道Java中内存是通过GC自动管理的,GC会在程序运行过程中自动判断哪些对象是可以被回收的,并在合适的时机进行内存释放。GC判断某个对象是否可被回收的依据是,是否有有效的引用指向该对象。如果没有有效引用指向该对象(基本意味着不存在访问该对象的方式),那么该对象就是可回收的。这里的“有效引用”并不包括弱引用。也就是说,虽然弱引用可以用来访问对象,但进行垃圾回收时弱引用并不会被考虑在内,仅有弱引用指向的对象仍然会被GC回收。
WeakHashMap 内部是通过弱引用来管理entry的,弱引用的特性对应到 WeakHashMap 上意味着什么呢?将一对key, value放入到 WeakHashMap 里并不能避免该key值被GC回收,除非在 WeakHashMap 之外还有对该key的强引用。
关于强引用,弱引用等概念以后再具体讲解,这里只需要知道Java中引用也是分种类的,并且不同种类的引用对GC的影响不同就够了。
是否有WeekHashSet?
答案是没有:( 。不过Java Collections工具类给出了解决方案,Collections.newSetFromMap(Map<E,Boolean> map)方法可以将任何 Map包装成一个Set。通过如下方式可以快速得到一个 Weak HashSet:
// 将WeakHashMap包装成一个Set
Set<Object> weakHashSet = Collections.newSetFromMap(
new WeakHashMap<Object, Boolean>());
不出你所料,newSetFromMap()方法只是对传入的 Map做了简单包装:
// Collections.newSetFromMap()用于将任何Map包装成一个Set
public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
return new SetFromMap<>(map);
} private static class SetFromMap<E> extends AbstractSet<E>
implements Set<E>, Serializable
{
private final Map<E, Boolean> m; // The backing map
private transient Set<E> s; // Its keySet
SetFromMap(Map<E, Boolean> map) {
if (!map.isEmpty())
throw new IllegalArgumentException("Map is non-empty");
m = map;
s = map.keySet();
}
public void clear() { m.clear(); }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
public boolean remove(Object o) { return m.remove(o) != null; }
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
public Iterator<E> iterator() { return s.iterator(); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
public String toString() { return s.toString(); }
public int hashCode() { return s.hashCode(); }
public boolean equals(Object o) { return o == this || s.equals(o); }
public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
public boolean removeAll(Collection<?> c) {return s.removeAll(c);}
public boolean retainAll(Collection<?> c) {return s.retainAll(c);}
// addAll is the only inherited implementation
......
}
// Collections.newSetFromMap()用于将任何Map包装成一个Setpublic static <E> Set<E> newSetFromMap(Map<E, Boolean> map) { return new SetFromMap<>(map);}private static class SetFromMap<E> extends AbstractSet<E> implements Set<E>, Serializable{ private final Map<E, Boolean> m; // The backing map private transient Set<E> s; // Its keySet SetFromMap(Map<E, Boolean> map) { if (!map.isEmpty()) throw new IllegalArgumentException("Map is non-empty"); m = map; s = map.keySet(); } public void clear() { m.clear(); } public int size() { return m.size(); } public boolean isEmpty() { return m.isEmpty(); } public boolean contains(Object o) { return m.containsKey(o); } public boolean remove(Object o) { return m.remove(o) != null; } public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; } public Iterator<E> iterator() { return s.iterator(); } public Object[] toArray() { return s.toArray(); } public <T> T[] toArray(T[] a) { return s.toArray(a); } public String toString() { return s.toString(); } public int hashCode() { return s.hashCode(); } public boolean equals(Object o) { return o == this || s.equals(o); } public boolean containsAll(Collection<?> c) {return s.containsAll(c);} public boolean removeAll(Collection<?> c) {return s.removeAll(c);} public boolean retainAll(Collection<?> c) {return s.retainAll(c);} // addAll is the only inherited implementation ......}java中利用WeakHashMap实现缓存的更多相关文章
- 在JAVA中利用public static final的组合方式对常量进行标识
在JAVA中利用public static final的组合方式对常量进行标识(固定格式). 对于在构造方法中利用final进行赋值的时候,此时在构造之前系统设置的默认值相对于构造方法失效. 常量(这 ...
- Java中利用随机数的猜拳游戏
Java中利用随机数的猜拳游戏,实现非常简单,重难点在于随机数的产生. 首先GameJude类是用于判断输赢的一个类: package testGame; public class GameJudge ...
- java 中利用反射机制获取和设置实体类的属性值
摘要: 在java编程中,我们经常不知道传入自己方法中的实体类中到底有哪些方法,或者,我们需要根据用户传入的不同的属性来给对象设置不同的属性值,那么,java自带的反射机制可以很方便的达到这种目的,同 ...
- JAVA中利用反射机制进行对象和Map相互转换的方法
JAVA的反射机制主要作用是用来访问对象的属性.方法等等.所以,JAVA中对象和Map相互转换可以利用JAVA的反射机制来实现.例子如下: 一.对象转Map的方法 public static Map& ...
- Java中利用MessageFormat对象实现类似C# string.Format方法格式化
我们在写C#代码的时候常常会使用到string.Format("待格式化字符串{0},{1},....",参数1,参数2,...),来格式化字符串,特别是拼接字符的时候,这种方式使 ...
- java中利用RandomAccessFile读取超大文件
超大文件我们使用普通的文件读取方式都很慢很卡,在java中为我提供了RandomAccessFile函数,可以快速的读取超大文件并且不会感觉到卡哦,下面看我的一个演示实例. 服务器的日志文件往往达到4 ...
- java中利用JFrame创建窗体 【转】
1. 一个简单的swing public class Test(){ public static void main(String[] args){ JFrame frame = new JFrame ...
- Java中利用BigInteger类进行大数开方
在Java中有时会用到大数据,基本数据类型的存储范围已经不能满足要求了,如要对10的1000次方的这样一个数据规模的数进行开方运算,很明显不能直接用Math.sqrt()来进行计算,因为已经溢出了. ...
- java中利用dom4j解析XML文件
官网下载Dom4j地址:https://dom4j.github.io/ 注意:使用Dom4j开发,需下载dom4j相应的jar文件 题目:后台利用dom4j解析student.xml文件,并返回Li ...
随机推荐
- 这篇 感觉很实用--DJANGO ORM
Django之model F/Q以及多对多操作 http://www.cnblogs.com/ccorz/p/5882400.html model之F/Q操作 F操作,使用查询条件的值 打个比方吧,有 ...
- HDU 17新生赛 正品的概率【数论-概率论】
正品的概率 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- [Math Review] Statistics Basics: Main Concepts in Hypothesis Testing
Case Study The case study Physicians' Reactions sought to determine whether physicians spend less ti ...
- oracle free space
--Size of All Table Space --1. Used Space SELECT TABLESPACE_NAME,TO_CHAR(SUM(NVL(BYTES,0))/1024/1024 ...
- UVA 11990 ”Dynamic“ Inversion(线段树+树状数组)
[题目链接] UVA11990 [题目大意] 给出一个数列,每次删去一个数,求一个数删去之前整个数列的逆序对数. [题解] 一开始可以用树状数组统计出现的逆序对数量 对于每个删去的数,我们可以用线段树 ...
- 基础博弈论之——简单的博弈问题【hdu1525】【Euclid‘s Game】
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=60481118 由于今天考了一道博弈的问题,我竟什 ...
- MySQL命令show full processlist
processlist命令的输出结果显示了有哪些线程在运行,可以检查当前数据库的运行状态,两种方式使用这个命令. 1 进入MySQL/bin目录下输入mysqladmin processlist; 2 ...
- Chrome下强制http重定向到https的问题
问题: Chrome会强制将http重定向到https,就算是在浏览器手动输入http://xxx也不可以. 解决方案: 1. 在chrome的地址栏输入chrome://net-internals/ ...
- xcode里面使用Memory Leaks和Instruments检测内存泄漏
教程截图: 作为一名无证程序员,无论你多么精通Objective-C的内存管理,随着时间的推移,你也不可避免的犯内存相关的错误.但通常因为代码量太大,以至于你不可能一行一行的去排除(等你解决完,你设计 ...
- 分布式协调服务Zookeeper
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一致性服务的软件,提供的功 ...