Java Weak Hash Map - Need to remove entry based on weakness of value, not key
Asked Answered
B

4

9

So the Java WeakHashMap lets one create a map whose entries are removed if its keys become weak. But how can I create a Map whose entries are removed when the values in the map become weak? The reason I want to use a map is as a Global Hash Table which keeps track of objects according to their ID's.

ID --->  Object Address

Key ---> Value

(Where ID is a text string)

I want key-value pairs to be removed when the object addresses become weak, not the Strings that point to them. Anyone any thoughts on this?

Basion answered 14/4, 2011 at 16:30 Comment(2)
You may find this an interesting read: weblogs.java.net/blog/enicholas/archive/2006/05/…Dorris
Thanks for the link, Lucas - its a great discussion of the subjectBasion
B
10

Such a map is supported, for example, in Guava:

Map<..., ...> m = new MapMaker().weakValues().makeMap();
Beaverette answered 14/4, 2011 at 16:39 Comment(4)
This won't work, it will just wrap values in weakReferences but will not remove entries when values are garbage collected...Herrington
@pgras: No, it removes entries as well. The only difference is that WeakHashMap removes stale entries during following access to the map, whereas Guava's implementation remove them in a background thread (FinalizableReferenceQueue).Beaverette
you are right, from the documentation "An entry whose key or value is reclaimed by the garbage collector immediately disappears from the map"Herrington
Many thanks for this, it seems like the best solution I've seen. Its unfortunate it requires adding such a large JAR file to the build but the API seems great.Basion
H
3

Why do you want the entry to be garbage collected ? I see two reasons

  1. avoid memory leaks (avoid to keep a weakReference pointing to nothing in your Map)
  2. myMap.get(myKey) should return null if the object was garbage collected.

Solution use a regular HashMap:

Map<String, WeakReference<Object>>

then if 2) is the sole problem just use myMap.get(myKey).get()

If you need to also remove the entries have a look at this post that describes a softHashMap and adapt it to use weakReferences...

Herrington answered 14/4, 2011 at 17:12 Comment(0)
P
2

The API has the answer:

Implementation note: The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded. Note that a value object may refer indirectly to its key via the WeakHashMap itself; that is, a value object may strongly refer to some other key object whose associated value object, in turn, strongly refers to the key of the first value object. One way to deal with this is to wrap values themselves within WeakReferences before inserting, as in: m.put(key, new WeakReference(value)), and then unwrapping upon each get.

Paluas answered 14/4, 2011 at 16:41 Comment(8)
Unfortunately it is not the answer to the question. If a WeakReference is inserted as the value in a weakHashMap nothing will happen (the entry will not be removed from the map) if the referenced object gets garbage collected...Herrington
@pgras: when a value is only referenced from the map (through the weak reference) it can be reclaimed. At which point the key (if referenced from the value can also be reclaimed.Paluas
@subsub: it -the key - could be reclaimed if it were weakly reachable (or softly or phantom) but the key must be strongly reachable if you want to use it as a key...Herrington
@pgras: no you don't need a strongly reachable key, hashmaps search by hash. the key is only checked to ensure equality. If the key is gone, well, then there's not going to be equality.Paluas
@subsub: I agree you do not need strongly reachable keys, but if your key is not strongly reachable, the entry will be removed from the map (even if the value is strongly reachable). Now I see there would be a solution where the only strong reference to the key would be from the value that would itself be wrapped in a WeakRef. But here we are speaking about using Strings as keys and with interning it is not easy to have a String with no strong reference to it and be able to recreate an equal String later.Herrington
@pgras: I agree that interned strings are a problem as keys, but values could still be reclaimed. However (last time I checked) only string literals are automatically interned, and String.equals checks for equal content. So I don't understand your last point.Paluas
@subsub: at the beginning I thought your solution could not work, now I see it will work as long as you guarantee you do not keep a strong ref to key from outside the map AND you keep a strong ref to key from within the value. IMHO this makes it a complicated solution where simpler solutions are possible that allow to keep a strong ref to the key and don't force your value to reference the key. The Guava solution for example seems better to me...Herrington
@pgras: The Guava solution also seems better to me than your solution, which also does not remove the entries, and will always retain the keys.Paluas
V
2

You can do what WeakHashMap does, but to values instead of keys: wrap your values in WeakReferences, and associate them with a ReferenceQueue held in the map. Whenever the map is accessed, check the ReferenceQueue to see if anything has been added, and if it has, remove its entry from the map. You'll need to make a subclass of WeakReference that holds the key, so you know which entry to remove. You will also need to add a check to methods which query the map (get and containsKey, the iterator methods, etc) to check that a retrieved WeakReference actually contains a value (remember to either ban null values, or use a special sentinel object to represent them).

Verboten answered 14/4, 2011 at 17:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.