HashMap data is deleted unexpectedly [duplicate] - java

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 9 years ago.
I'm creating a HashMap as follows:
private HashMap <String, JSONArray> HiveMap;
HiveMap = new HashMap <String, JSONArray>();
and also a private class variable to store some calculated data which will be put into the HashMap after the calculations are done:
private JSONArray hiveList;
After hiveList is calculated, I put the hiveList into the created HashMap (HiveMap):
HiveMap.put(hiveNode,hiveList);
When I print out HiveMap now, the output I get is as expected, with both the fields hiveNode and hiveList existing in the Hashmap. <"hiveNode":"hiveList">
After this is done, I assume that the data I put into the HashMap from hiveList is going to persist, so I clear the hiveList array using hiveList.clear();
But when I print the Hashmap after clearing hiveList, I see that the data from hiveList is gone from the HashMap as well.
Printing the HashMap after clearing hiveList results in: <"hiveNode": >
I don't understand this behavior, I would appreciate it if someone could shed some light on this.

The error is this:
After this is done, I assume that the data I put into the HashMap from hiveList is going to persist, so I clear the hiveList array using hiveList.clear();
What got passed to the hash map is the reference to the list, not a copy of it.
The most simple fix is to create a new instance instead of clearing it. Alternatively you can make a copy, and put that in the map.

The hiveList list and the list that resides as a value in the Map are one and the same, both the variable and the Map are holding references to the same object; if you clear() one list, the other is also emptied - there's a single list, not two.
If you have to clear and reuse hiveList for some reason (and I don't see why), then you'll need to put a different list in the Map, here's how to do a shallow copy:
HiveMap.put(hiveNode, new ArrayList<Hive>(hiveList));
Replace <Hive> with the actual type of the elements in the list.

In java objects are passed by copy of reference and not copy of object. So if you modify the object using reference at one place it get affected at all the places where this object is referred. In you case hiveList.clear();
is causing to remove all the elements of the list which is also referred at HiveMap.put(hiveNode,hiveList);and hence is eventually removed from the map.

That's how java manages the Object. What you have is the reference to that object and you only stored a reference to it. So, if you clear the object after storing it in the hashmap, the object from hashmap would also be cleared.
When you create an object java stores the reference along with the object at the same location in the memory. Hence, there's only one reference to that object. So, the result was expected
//Stores the reference to the object
HiveMap.put(hiveNode,hiveList);
//Modifies the same object in the memory
hiveList.clear();
You may want to read about Pass by reference and Pass by Value

Related

Trouble using HashMaps with ArrayList [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
I am having trouble with implementing HashMaps with ArrayListsin my java class. The thing is it keeps adding objects to the ArrayList is the HashMap even though I am not updating my HashMap.
This is the code that I can not understand how works:
HashMap<String, ArrayList<String>> map = new HashMap<>();
ArrayList<String> array = new ArrayList<String>();
array.add("One");
array.add("Two");
map.put("Key", array);
array.add("Three"); //2. Why does this get added to the HashMap?
System.out.println(map1.get("Key"));
//1. This print out [One, Two, Three].. When it should be [One, Two]!
The ArrayList is passed by reference to the map.put() call. It means no copying, after the call your array variable refers to the same object. If you copy when adding the entry then it will work: map.put("Key", new ArrayList<String>(array));
map.put("Key", array);
That means you are adding a reference of list to the map. Hence the changes to that reference can be seen everywhere.
If you don't want to do that, create a new list and add to it.
This is the expected behaviour. You put the list into the map, not a copy, the list itself. So if you later modify the list, the list inside the map (which is the actual list) will also be modified.
Because you add a reference to a list into your map, and you still hold the original reference, when you amend that list, you're amending the list referenced within the map.
Remember that Java passes references to objects around (not copies), and if you have a mutable object referenced within a container, that object can still be changed.
If you want to avoid this behaviour, you need to make a defensive copy. Note that this applies to mutable objects generally (not just collections) and you need to be clear when you pass references around and hold them, that anyone else holding that reference can change/mutate your object without your control. It's often preferable to create and pass around immutable objects instead
you are adding a reference of your ArrayList as the value to your map.
so if you want only the first two value, you can simply point your ArrayList to null to ensure you don't add stuff to it then re-initiate it
HashMap<String, ArrayList<String>> map = new HashMap<>();
ArrayList<String> array = new ArrayList<String>();
array.add("One");
array.add("Two");
map.put("Key", array);
array=null; //note this isn't really necessary, just a precaution that you won't change the value of arraylist inside the map using this refrence
array=new ArrayList<String>(map.get("key"));
array.add("Three");
System.out.println(map1.get("Key"));
output:
[one, two]

ArrayList constructor accepting Collection

I have a simple question.
Lets say we have a Map, for example a Map<String, Object>
I want a method that returns a list of all values inside the Map.
The approach i use is the following:
I create a List<Object> myList = new ArrayList<>();
Get an iterator from the value set of the Map.
For each element inside the iterator i put a reference in the myList list.
Return the list
...later for each element i use i wrap it inside a synchronized block because the list contains references.
Now i am woring about using an easier apporach. The one i mean is the following:
return new ArrayList(myMap.values());
As you see in this case i simply use the constructor of the List interface which accepts a Collection.
And finally my question is:
If i use the second approach do i still get references or it copies the value objects that are inside the map?
In both cases you will get "shallow" copy of collecion, so both arrays will keep references to the same objects.
return new ArrayList(myMap.values()) will return an ArrayList containing the references of the original values of the Map. No copies of the values instances are created.
Note that if your Map contains duplicate values (i.e. values that are equal to each other), your ArrayList will also contain duplicate values. If you want to eliminate the duplicates, you should create a Set of the values instead of a List.
In either case you'll get a copy of the reference (so called "shallow copy").
There is no deep-copying (creating a completely new object with meaningfully equivalent fields -- also deep-copied) involved.

Why is this HashMap being passed as reference [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 9 years ago.
I have two HashMaps defined as follows:
HashMap EventTable = new HashMap();
HashMap MainTable = new HashMap ();
Now, suppose I insert some data into EventTable:
EventTable.put("name", "Test Event Name");
Then, suppose we insert the EventTable into the MainTable:
MainTable.put("table", EventTable);
and then print out MainTable:
{table={name=Test Event Name}}
Now if I modify Event Table as follows:
EventTable.put("more names", "More test events");
This changes the MainTable as well:
{table={more names=More test events, name=Test Event Name}}
So obviously, EventTable is being passed by reference to MainTable.
I don't understand why EventTable is being passed by reference.
Ideally I would like to reuse the EventTable HashMap to create other entries, while retaining my old entries, so I would like to pass EventTable by value.
EDIT:
Forgot to mention this in the main question, but when I add a String to the MainTable, that does not change when I modify the string afterwards. So how is the behavior is different for the String?
Although I would never use raw types and follow the Java naming conventions, you can do something like this to get your desired behavior:
mainTable.put("table", new HashMap(eventTable));
To address your edit: Strings are immutable in Java.
It is because, even though Java passes all arguments by value, when you pass an object, you are really passing an object reference. if you were to set EventTable to a new Hashmap, the one you passed in to MainTable would not be changed. This article may help clear things up: http://javadude.com/articles/passbyvalue.htm
as in #assylias said, parameters passed by value.
here HashMap can not copy the date because it could be anything. but you can create a new HashMap by its constructor.
HashMap newHashMap= new HashMap(oldHashMap);

Objects in java. What happens with them after they are no more assigned to a variable? [duplicate]

This question already has answers here:
What is the garbage collector in Java?
(16 answers)
Closed 9 years ago.
I've been working with HashMaps when I came to a question which I couldn't solve myself or find on the web.
HashMap<String,HashMap<String,String>> m= new HashMap<>();
HashMap<String,String> t = new HashMap<>();
t.put("test1","1");
m.put("h1",t);
t = new HashMap<>();
t.put("test2,"2");
m.put("h2",t);
System.out.println(m);
That gives me {h1={test1=1}, h2={test2=2}}
Thus the big HashMap contains data of both HashMaps. So the question is did it simply copy the data of smaller HashMaps, or do both "t" HashMaps stay in JVM memory, and HashMap m simply links me to them?
Your big HashMap contains a reference to both HashMaps. What you've done is changed what HashMap t is pointing too.
So as a more visual example lets say
t = 0x00000001
when you put t into m, m now looks like
m = {0x00000001}
at this point both t and m have references to the same HashMap, meaning any change to one will appear in the other. Now when you go t = new HashMap<>() you are actually reassigning the reference that t points to
t = 0x00000002
but m still looks like {0x00000001} so you didn't lose your first reference. And when you put t into m the second time they look like
t = 0x00000002
m = {0x00000001, 0x00000002}
so in the end, m still contains a reference to both HashMaps
Whenever you place t into m, no copy of the data is made. Instead, a reference to the sub-map is placed.
You add t to m twice. However, each time t points to a different object. Thus you end up with two separate, independent sub-maps in m.
Contrast this with the following example:
t.put("subkey","old");
m.put("h1",t);
m.put("h2",t);
t.put("subkey", "new");
System.out.println(m);
This prints out
{h1={subkey=new}, h2={subkey=new}}
Here, t and both keys of m point to the same sub-map. When you change one, they all change.
Map coutains reference of your Object. It internally contains array bucket of Entry class which holds key,value object reference.
HashMap m will contains object reference of your first created hashmap against h1 and second hashmap's reference aginst h2.
Everything in Java is a pointer or reference, so the objects are held in memory and the hash map is storing references, not copying the actual data.
The big hash map contains a reference to the smaller ones so the garbage collector won't free up the memory.
Both HashMap will stay in memory. You could refer them through the hashmap m with key h1 and h2. The data wont be copied from hashmaps but the reference of the object is changed to hashmap key(i.e. h1 & h2).

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

Categories

Resources