如何在Java中有效地使用String作为WeakHashMap的键,或者使用替代解决方案

9

我正在管理一个Java项目,用于存储用户数据。用户可以在线或离线。当用户在线时,他们的数据会加载到数据对象中以便于访问,并在注销时卸载。

然而,为了防止多个命令同时操作数据而导致数据损失,对于离线用户,我使用WeakHashMap作为已加载用户数据对象的缓存。如果需要访问离线数据对象进行修改,系统将首先检查缓存,然后再从文件中加载数据。

我唯一能想到的存储方法是使用字符串键,表示用户的用户名。但由于Java的工作方式,由于VM的字符串缓存系统存在,这种方式并不总是有效。

最初我考虑使用字符串包装器,但由于哈希映射表的工作方式(通过哈希码),创建新的字符串包装器将无法获得所需的值,如果我存储字符串包装器,那么将始终存储强引用到键(阻止从WeakHashMap中删除键)。

也许我只是没有理解WeakHashMap应该如何使用:S如果WeakHashMap不适合我现在的需求,我愿意接受其他解决方案。


你能具体描述一下使用字符串值作为键时遇到的问题吗? - Warren Dew
说实话,结果从来都是不可预测的。我这里有两个地图,一个标准哈希映射用于存储在线用户的数据对象,另一个弱哈希映射用于缓存用户。有时候即使没有引用(在用户注销时,哈希映射的强引用被移除),它也会拒绝删除它,有时候它会在用户在线时从主哈希映射中删除它,而主哈希映射中仍然存在强引用。结果非常不可预测:\ - CorrieKay
1个回答

13
它表现不可预测的原因在WeakHashMap的Javadoc中解释,该段落的最后一句话如下:
“此类主要用于键对象使用==运算符测试对象标识的equals方法。一旦这样的键被丢弃,就无法重新创建它,因此不可能在以后的某个时间查找WeakHashMap中的该键,并惊讶地发现其条目已被删除。对于那些equals方法不基于对象标识的可重建键对象,例如String实例,此类将完全正常工作。但是,已经丢弃其键的WeakHashMap条目的自动删除可能会导致混淆。”
你真正想要的是一个映射,其中条目在值被垃圾回收时被删除,而不是在键被垃圾回收时被删除-也就是说,值是弱的,而不是键是弱的。您可以在以下问题中找到有关该问题的帮助: Java Weak Hash Map-需要根据值的弱点而不是键来删除条目

1
非常感谢,先生。Guava解决方案绝对完美地解决了我的问题!最好的部分是,我正在使用的总体API已经将Guava作为工具集的一部分提供了! - CorrieKay
我同意这份文档中“perfectly well with”的部分,但仅当Java映射允许您替换用于键的对象时。由于您只能更新其值,在许多情况下它并不起作用,即使考虑到被丢弃的条目。例如,WeakHashMap<T, WeakReference<T>>的去重对象缓存无法轻松实现,因为当需要更新值时,键可能与存储在值中的对象不匹配,因为WeakReference已被丢弃,但是映射中的条目尚未。 - Christopher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接