首页 > 编程知识 正文

java hashmap用法,hashmap做缓存

时间:2023-05-05 03:08:19 阅读:63016 作者:683

作为java开发者,你一定知道并使用HashMap,但我想大多数人对WeakHashMap不太了解。 从类定义来看,它继承了AbstractMap类并实现了Map接口,就像在常规HashMap中一样。 也就是说,它具有与HashMap相同的功能。 那么,既然jdk已经提供了HashMap,为什么还要提供另一个WeakHashMap呢? 黑格尔曾经说过存在是合理的,接下来我们来看看为什么会有WeakHashMap。

首先,想象一下由于某种需要,需要Cache。 不能将所有数据都放在Cache中,或者放在Cache中一定会面临性价比太低的问题。 此时,您可能很快就想到了各种Cache数据的过期策略。 现在,有些软件包提供了功能丰富的Cache,如谷歌的Guava Cache。 它支持定期数据过期、LRU、LFU等策略,但ssdsb可能会丢弃有用的数据,难以丢弃无用的数据。 (

如果我说现在有一种机制可以自动清理你Cache上不用的key数据,用的东西还剩下,误杀、杀了你都不相信! 没错,WeakHashMap可以实现这样的功能。 这也是和普通的HashMap不同的地方。 ——有自助清洁机制。

如何实现自助清洗的HashMap? 我的做法一定是在知道一定没有使用某个Key之后,再考虑如何清理到HashMap的对应中小学。 一个对象在JVM中不再有用,意味着GC进程中没有GCRoots无法达到的、直接或间接执行它的其他有用对象。 Jvm提供了一种感知对象是否成为垃圾对象的机制。 这就是唤醒参考。 如果您不知道WeakReference,请参阅前面介绍博客中的“WeakReferences弱引用”。

如果某个WeakReference对象指向的对象被确定为垃圾对象,则Jvm会将该WeakReference对象放入ReferenceQueue中。 只要查看此队列的内容,就可以知道某个对象是否仍然有用。 因为WeakHashMap是这么做的,所以这里的Weak是指WeakReference。 接下来看看那个代码,看看它具体是怎么实现的。

源代码分析privatestaticfinalintdefault _ initial _ capacity=16; 私密性staticfinalintmaximum _ capacity=130; privatestaticfinalfloatdefault _ load _ factor=0.75 f; 与常规HashMap一样,WeakHashMap也有几个默认值。 例如,默认容量为16,最大容量为2^30,使用75%以上时会自动扩展。

entry private静态类entryk,vextendsweakreferenceobjectimplementsmap.entryk,V { V value; final int hash; 进入,下一步; 输入(对象密钥,V value,参考查询队列对象队列,输入散列,输入,V next ) {super密钥,队列; this.value=value; this.hash=hash; this.next=next; } /* *其他代码*/}其Entry与普通HashMap的Entry最不一样的是继承WeakReference,将Key作为弱引用(请注意,只有Key没有Value ),然后再引用

putpublicvput(kkey,V value ) objectk=掩码空值) key; inth=hash(k; EntryK,V[] tab=getTable (; intI=indexfor(h,tab.length ); for (输入,V e=tab[i]; e!=null; e=e.next(if ) h==e.hasheq,e.get ) ) { V oldValue=e.value; if (值!=oldvalue(e.value=value; 返回oldvalu

e; } } modCount++; Entry<K,V> e = tab[i]; tab[i] = new Entry<>(k, value, queue, h, e); if (++size >= threshold) resize(tab.length * 2); return null; }

put方法也很简单,用key的hashcode在tab中定位,然后判断是否是已经存在的key,已经存在就替换旧值,否则就新建Entry,遇到多个不同的key有同样的hashCode就采用开链的方式解决hash冲突。注意这里和HashMap不太一样的地方,HashMap会在链表太长的时候对链表做树化,把单链表转换为红黑树,防止极端情况下hashcode冲突导致的性能问题,但在WeakHashMap中没有树化。
  同样,在size大于阈值的时候,WeakHashMap也对做resize的操作,也就是把tab扩大一倍。WeakHashMap中的resize比HashMap中的resize要简单好懂些,但没HashMap中的resize优雅。WeakHashMap中resize有另外一个额外的操作,就是expungeStaleEntries(),就是对tab中的死对象做清理,稍后会详细介绍。

get public V get(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null) { if (e.hash == h && eq(k, e.get())) return e.value; e = e.next; } return null; }

get方法就没什么特别的了,因为Entry里存了hash值和key的值,所以只要用indexFor定位到tab中的位置,然后遍历一下单链表就知道了。

expungeStaleEntries private void expungeStaleEntries() { for (Object x; (x = queue.poll()) != null; ) { synchronized (queue) { @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) x; int i = indexFor(e.hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> p = prev; while (p != null) { Entry<K,V> next = p.next; if (p == e) { if (prev == e) table[i] = next; else prev.next = next; // Must not null out e.next; // stale entries may be in use by a HashIterator e.value = null; // Help GC size--; break; } prev = p; p = next; } } } }

expungeStaleEntries就是WeakHashMap的核心了,它承担着Map中死对象的清理工作。原理就是依赖WeakReference和ReferenceQueue的特性。在每个WeakHashMap都有个ReferenceQueue queue,在Entry初始化的时候也会将queue传给WeakReference,这样当某个可以key失去所有强应用之后,其key对应的WeakReference对象会被放到queue里,有了queue就知道需要清理哪些Entry里。这里也是整个WeakHashMap里唯一加了同步的地方。
  除了上文说的到resize中调用了expungeStaleEntries(),size()中也调用了这个清理方法。另外 getTable()也调了,这就意味着几乎所有其他方法都间接调用了清理。

其他

除了上述几个和HashMap不太一样的地方外,WeakHashMap也提供了其他HashMap所有的方法,比如像remove、clean、putAll、entrySet…… 功能上几乎可以完全替代HashMap,但WeakHashMap也有一些自己的缺陷。

缺陷 1.非线程安全

关键修改方法没有提供任何同步,多线程环境下肯定会导致数据不一致的情况,所以使用时需要多注意。

2.单纯作为Map没有HashMap好

HashMap在Jdk8做了好多优化,比如单链表在过长时会转化为红黑树,降低极端情况下的操作复杂度。但WeakHashMap没有相应的优化,有点像jdk8之前的HashMap版本。

3.不能自定义ReferenceQueue

WeakHashMap构造方法中没法指定自定的ReferenceQueue,如果用户想用ReferenceQueue做一些额外的清理工作的话就行不通了。如果即想用WeakHashMap的功能,也想用ReferenceQueue,貌似得自己实现一套新的WeakHashMap了。

用途

这里列举几个我所知道的WeakHashMap的使用场景。

1.阿里Arthas

在阿里开源的Java诊断工具中使用了WeakHashMap做类-字节码的缓存。

// 类-字节码缓存 private final static Map<Class<?>/*Class*/, byte[]/*bytes of Class*/> classBytesCache = new WeakHashMap<Class<?>, byte[]>(); 2.Cache

WeakHashMap这种自清理的机制,非常适合做缓存了。

3.ThreadLocalMap

ThreadLocal中用ThreadLocalMap存储Thread对象,虽然ThreadLocalMap和WeakHashMap不是一个东西,但ThreadLocalMap也利用到了WeakReference的特性,功能用途很类似,所以我很好奇为什么ThreadLocalMap不直接用WeakHashMap呢!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。