浅谈WeakHashMap

浅谈WeakHashMapJavaWeakHashMap到底Weak在哪里,它真的很弱吗?WeakHashMap的适用场景是什么,使用时需要注意些什么?弱引用和强引用对JavaGC有什么不同影响?本文将给出清晰而简洁

大家好,又见面了,我是你们的朋友全栈君。

Java WeakHashMap 到底Weak在哪里,它真的很弱吗?WeakHashMap 的适用场景是什么,使用时需要注意些什么?弱引用和强引用对Java GC有什么不同影响?本文将给出清晰而简洁的介绍。

总体介绍

在Java集合框架系列文章的最后,笔者打算介绍一个特殊的成员:WeakHashMap,从名字可以看出它是某种 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的影响不同就够了。

具体实现

WeakHashMap的存储结构类似于HashMap,读者可自行参考前文,这里不再赘述。

关于强弱引用的管理方式,博主将会另开专题单独讲解。

Weak HashSet?

如果你看过前几篇关于 MapSet 的讲解,一定会问:既然有 WeekHashMap,是否有 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
    ......
}

结语

至此深入Java集合框架(Java Collections Framework Internals)系列已经全部讲解完毕,希望这几篇简短的博文能够帮助各位读者对Java容器框架建立基本的理解。通过这里可以返回本系列文章目录

如果对各位有哪怕些微的帮助,博主将感到非常高兴!如果博文中有任何的纰漏和谬误,欢迎各位博友指正。

本文GitHub地址

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/155341.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 黑盒测试用例设计方法详解

    黑盒测试用例设计方法详解黑盒测试用例设计方法包括等价类划分法、边界值分析法、错误推测法、因果图法、判定表驱动法、正交试验设计法、功能图法、场景图法等。(一)等价类划分法定义:等价类划分法是把所有可能输入的数据,即程序的输入域划分策划国内若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例。方法是一种重要的、常用的黑盒测试用例设计方法。等价类是指某个输入域的子集合。在该子集合中,各个输入数据…

    2022年6月6日
    28
  • CMD命令实现数字雨

    CMD命令实现数字雨使用cmd命令可以实现类似黑客帝国中的数字雨,这里给出脚本和演示效果:digitalrain.bat@echoofftitledigitalraincolor0bsetlocalENABLEDELAYEDEXPANSIONfor/l%%iin(0)do(set”line=”for/l%%jin(1,1,80)do(set/aDown%%…

    2022年6月9日
    48
  • listagg小记录[通俗易懂]

    listagg小记录[通俗易懂]listagg的作用是将分组范围内的所有行特定列的记录加以合并成行。函数签名中的measure_expr为分组中每个列的表达式,而delimiter为合并分割符。如果delimiter不设置的话,就表示无分割符。  中间withingroup后面的order_by_clause表示的是进行合并中要遵守的排序顺序。而后面的over子句表明listagg是具有分析函数analyze

    2025年9月6日
    5
  • 计划任务 SchedulerFactoryBean 配置

    计划任务 SchedulerFactoryBean 配置Quartz是开源任务调度框架中的翘首,它提供了强大任务调度机制,同时保持了使用的简单性。Quartz允许开发人员灵活地定义触发器的调度时间表,并可以对触发器和任务进行关联映射。此外,Quartz提供了调度运行环境的持久化机制,可以保存并恢复调度现场,即使系统因故障关闭,任务调度现场数据并不会丢失。此外,Quartz还提供了组件式的侦听器、各种插件、线程池等功能。Spring为创建Quart…

    2022年5月10日
    126
  • 面试题jmeter怎么做性能测试_web测试面试题

    面试题jmeter怎么做性能测试_web测试面试题面试中遇到的问题:1.如何使用Jmeter进行并发测试2.如何设置并发量为10003.如果http请求每个都不一样,如何配置4.如何设置sessionID一、安装配置1.在Terminal中输入命令:ruby-e”$(curl-fsSLhttps://raw.githubusercontent.com/Homebrew/install/master/…

    2022年9月30日
    3
  • js动画效果_js动画函数

    js动画效果_js动画函数一、setTimeoutVS.requestAnimationFrame传统js动画实现一般使用setTimeout/setInterval等定时方式执行一个动画更新操作,但这种方式在使用中存在一些问题。动画帧间隔interval问题大部分显示器的刷新频率是16.7ms,如果setTimeout的interval小于这个值,就会出现绘制的帧无法在显示器上展现的问题,好像被吞掉了一样。另

    2022年10月15日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号