filling HashMap within loop - java

a big doubt! I read that HashMap takes references of objects and doesn't copy values to store data. So if I have something like
HashMap<Integer, Double> map = HashMap<Integer, Double>();
for(int i = 0; i < 100; i++ ) {
Integer key = Integer(i);
Double value = Double(i*2.0);
map.put(key, value);
}
What is the result of
map.get(10);
? Keys and values are created within the for loop so I suppose that they are deleted at the end of for statement and put(10) give something like null. But I think it's a very annoying behaviour because I can't fill a HashMap with a straighforward for loop... I'm wrong?

I'm assuming you meant to ask what the result of map.get(10) is, since map.put(10) is not valid. map.get(10) would return the Double whose value is 20.0.
Since the map variable is declared outside the for loop, the values you put in the map remain there after the end of the for loop. The keys and values are not deleted at the end of the for loop because the map variable holds references to them.

First of all, in Java, all objects are accessed by reference - there is nothing special about the HashMap here.
Second, all objects are kept in memory until the Garbage collector (GC) decides to destroy them. The GC may destroy objects as soon as they are no longer referenced anywhere - it may not destroy them if they are still referenced.
In your example :
After the loop, the object referenced by map holds references to the same objects that were referenced by keyand value : those objects cannot be destroyed.
If, at a later point, you no longer hold any reference to the HashMap object, it may be destroyed by the GC, and the GC may also decide to destroy the key and the value, if they were only referenced by the Hashmap. Note that there is no guarantee given as to when garbage collection will occur: it won't necessarily happen immediately.
You can't do map.put(10), because HashMap doesn't have a method with a compatible signature. The only put method that exists for HashMap expects two parameters.
edit: Since the op corrected map.put(10) to map.get(10): it will return a reference to the same object that was briefly referenced by the variable value during the "i=10" iteration of the loop.

The key are hold by refrences as (almost)all objects in Java; java won't delete an object which is strongly referenced by another object.
object created in the loop will only be collected if no other object references them after the loop
since you put them into an hashmap, java will keep them around
note also that put(10, ...) actually is put(new Integer(10), ...) thanks to autoboxing

Related

Weak Reference maintainability

I was reading up on weak references in java and sounds simple enough, if an object only has weak references on it, then it can be collected by the garbage collector. Except what happens if your reference becomes dead before you use the value?
Example:
Suppose I have a weak hashmap with the keys {1,2,3,4,5}, all with values of 1. Now suppose you have a random number generator for numbers in [1:10]. Now every time the number is gotten, it checks if it a key in the map and then gives a temporary strong reference to the key. So with this setup, you'll have some keys having strong references and thus stay in memory, but you also have the probability that some keys will become dead before being chosen.
If my intuition for weak hashmaps is correct, does that mean that the map will at some point be altered from its original state?
Trying to use Integer objects as keys for a WeakHashMap is likely to result in some strange behavior. To start with, the javadoc for WeakHashMap has the following note:
This class is intended primarily for use with key objects whose equals methods test for object identity using the == operator. Once such a key is discarded it can never be recreated, so it is impossible to do a lookup of that key in a WeakHashMap at some later time and be surprised that its entry has been removed. This class will work perfectly well with key objects whose equals methods are not based upon object identity, such as String instances. With such recreatable key objects, however, the automatic removal of WeakHashMap entries whose keys have been discarded may prove to be confusing.
Consider the following code:
WeakHashMap<Integer, String> map = new WeakHashMap<>();
Integer k = Integer.valueOf(9001);
map.put(k, "OVER 9000!?");
while (true)
{
System.out.println(map.get(k));
Thread.sleep(100);
k = Integer.valueOf(9001);
System.gc();
}
The loop will start by printing "OVER 9000!?", but after the first loop, the original key has been discarded (even if there's now a reference to a key that is equals to it). As a result, if that key object gets garbage collected, the entry will be removed from the map and the loop will begin printing "null" instead. Since we call System.gc(); after discarding the key, it's likely that this happens after a single loop.
That's not the end of the issues with using Integer as a WeakHashMap key, though. If you change the value 9001 above to 1, you'll find that the behavior changes! (Probably? This may be implementation-dependent.) Now, the entry never gets removed from the map. This is because of the integer cache--Integer.valueOf(1) always returns the same Integer instance, but Integer.valueOf(9001) creates a new Integer instance each time.
This second issue is specific to Integer, but the first actually applies to any scheme where you try to use keys where equals is not based on ==. And if equals is based on ==, then your question doesn't really apply--if you don't have a strong reference to the key anymore, it doesn't matter whether the value gets removed from the map because you no longer have a way to get to it--you can't recreate a key that uses identity-based equality.
This answer already addresses issues stemming from the use of types with value-based equality in a construct whose behavior depends on the identity of objects, like the reachability.
In short, when you are able to construct new objects with the same equality as the weakly reachable keys, it’s possible to detect the sudden removal of the keys.
However, you can also turn weakly reachable objects back to the strongly reachable state, e.g. by calling the get() method of a WeakReference or when iterating over the mappings of a WeakHashMap.
WeakHashMap<Object, Boolean> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, true);
WeakReference<Object> ref = new WeakReference<>(key);
key = null;
// now, the key object is only weakly reachable
key = ref.get();
// now, the key object might be strongly reachable again
// in that case, this statement will print true
System.out.println(map.get(key));
The construction of an object with a distinct identity and no overridden equals method via new Object() ensures that no other reference to the same object nor an equal object can exist. At one point of this code, the object is only weakly reachable, but then, it is made strongly reachable, with a very high likelihood.
It is possible that a garbage collection happens between these points and since all weak references to an object are cleared atomically, you can detect this situation by getting a null reference from get(). The likelihood for a garbage collection to happen right at this point is very low, though. That’s why the linked answer uses calls to System.gc() in-between, to raise the likelihood of the weak references to get cleared.
This is rather a contrived example, but helps addressing you question, “…does that mean that the map will at some point be altered from its original state?”.
If you use equal keys with a different identity or keys which are weakly reachable for some time, the map may get altered at some time, but there is no guaranty that this ever happens. This depends on when the garbage collector will run and actually discover the weak reachability of some objects. But typically, JVMs try to prevent garbage collection until there’s really a demand for it. So an application may run quiet a while without garbage collection at all. Further, if you do poll a mapping regularly, it may even happen, that the gc runs right at that point of time when the key is strongly reachable during the lookup.
The purpose of WeakReference is to help with memory management. As you wrote, "if the object is not used normally" (there is no strong reference, in fact direct variable holding it), "then you don't need it any more" (it can be garbage collected). In case of weak hash map it aplies to the key, so you typically use it for caching of temporary associated data.
From that being said, it doesnt make sense to only put something into weak hash map without continue using the key as strong reference, because the collector can collect it immediately before you access it.
It may not (even System.gc() doesn't force the GC to run), but you cannot rely on it.

How to free/GC HashMap<String,XYZ> on one shot (without iterating)?

I have some HashMap < String, XYZ > created having the key type as String and value type of some XYZ Class.Now for some condition check I want to free/ garbage collect the entire HashMap along with the individual objects of XYZ type that it holds. Is it possible to do it without iterating the hash map and setting each object to null ? I guess by setting only HashMap to null will not work. How do I ensure later that the collection of objects is garbage collected ?
If I do something like:
XYZ obj = new XYZ();
hm.put("<unique-id>",obj);
XYZ obj2 = new XYZ();
hm.put("<unique-id>",obj2);
.
.
.<more operations>
.
.
hm = null;
my code no longer reference either obj or obj2 after hm=null statement.
So can I be sure enough that the jvm will garbage collect obj and obj2 also shortly ?
Moreover in cases where hashmap is a huge collection, I dont want to use Iterator over HashMap to get and iterate over every object and set it to null.
Does just hm=null does the above stuffs of GCing the entire collection of objects + map for me ?
If an object isn't referenced anymore, you can be sure that the object is garbage collected properly. That means, if you set the hash map to null and you know that all the items within the map arn't referenced by other objects, then they are garbage collected. In case you want to call the gcc manually keep in mind that the decision whether the gcc process is executed or not is ultimately made by the JVM!

Java collections and memory use

I have a question on Java memory use. It’s for my edification and anyone else who searches and finds this later! For the purpose of the question, please assume, this is a single method and nothing goes out of scope... during my question ;-)
I have created 5 new objects with a single property called ‘name’ of type String.
I create an ArrayList and add the 5 objects to the ArrayList. I then create a HashMap and iterate through the previously created ArrayList, adding the objects to the HashMap.
Q1. When I add the objects from the ArrayList, to the HashMap, I assume I am just creating another collection of ‘pointers’, since I’m not using the ‘new’ keyword. Therefore no new memory is consumed, except for the HashMap itself (the objects are not duplicated).
Q2. If I change the value of ‘name’, in an object in the HashMap, would the same change be seen, if I were to iterate over the ArrayList, after making the change.
I appreciate a ‘sanity check’ on my understanding.
Q1: The HashMap is created and the references to the objects are created. So memory is consumed, but references aren't terribly big, but can make a difference if the number of references is huge.
Q2: Edit: Yes, the name field would change. Better still, write a small program to check it out.
A1 : Yes, other than the references and HashMap, nothing new will be created. (Assuming you are not creating a new set of keys for for the HashMap)
A2 : Yes, the change will reflect on the ArrayList.
To answer your questions.
1.) When you add objects to a HashMap the objects are not duplicated. Internally though the map will create new objects to maintain its inner structure. The inner structure of a map consists of HashMap.Entry objects that contain a linked list with all values that map to the same hash code. Thus whenever you add objects to a map one or more internal objects are created.
2.) I assume you stored the objects in the HashMap using their name as key. In this case chaning the name of an object will update the object (no matter whether it's being accessed through the list or the map, it's always the same object) but not the mapping in the map. In the map the object will still be store under its old name!
Map map = new HashMap();
Foo f = new Foo();
f.setName("A");
map.put(f.getName(),f);
f.getName(); // => "A"
map.get("A"); // => f
f.setName("B");
f.getName(); // => "B"
map.get("B"); // => null
map.get("A"); // => f

How HashMap handles the updated <key,value>,if value itself is a Hashmap?

when we put a <key,value> in a HashMap,if the key is already present in the HashMap then the value gets replaced. But if for a key the value is itself a HashMap then would it get replaced with the HashMap?
Yes, it would be replaced. Remember that a Map only stores references to other objects.
You put a reference to a HashMap in a map, and the map keeps a reference to this HashMap.
If you put a reference to another HashMap using the same key, the reference to the first put HashMap is replaced by the reference to the new HashMap. The type of the object doesn't matter. It always works the same way.
If I understand what you're asking, you want to know if what you just said will cause a memory leak (please update your question if this isn't what you're asking).
If you do:
Map<?, ?> m = new HashMap<Object, Object>();
m.put(m, m);
Then m will ultimately only contain a reference to itself. Because of how Java's GC works via an object reference graph, and because they use an algorithm which tracks visited nodes during a GC sweep, if nothing maintains a reference to m, then m will be garbage collected, despite containing a reference to itself. Circular references are perfectly handled in the Java GC.
If m is placed into a field (i.e., not a local variable declared inside a method) then it's a different story.
If m is placed in a static field, then there will always be a reference to it from a GC root, which means it won't be reclaimed. Note: nothing strongly referenced to from a static field will ever be garbage collected.
If m is placed in a member field (non-static), then the map won't be garbage collected until the object that contains it is garbage collected.
If there are multiple fields that refer to m, then m won't be garbage collected until all those references are either a) part of an object that can be garbage collected or b) are set to null or some other value to no longer refer to m.
TL;DR the garbage collector handles circular object references just fine.
Sidenote: Please update your question with information, don't just add it as comments to your question or others' answers.
The wording in your question is a bit opaque, but a HashMap<HashMap, Object> is perfectly valid (if somewhat strange). In that case, if:
HashMap map = new HashMap<HashMap<String, String>, String>();
HashMap a = new HashMap<String, String>();
HashMap b = new HashMap<String, String>(); //a.equals(b) == true
map.put(a, "foo"); //map.get(a) would now return "foo"
map.put(b, "bar"); //original entry is replaced, map.get(a) would now return "bar"

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

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?
Such a map is supported, for example, in Guava:
Map<..., ...> m = new MapMaker().weakValues().makeMap();
Why do you want the entry to be garbage collected ? I see two reasons
avoid memory leaks (avoid to keep a weakReference pointing to nothing in your Map)
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...
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.
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).

Categories

Resources