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]
Related
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.
I have the following code:
podmienky.put(key, podmienkyOblast);
podmienkyOblast.clear();
podmienky is HashTable<String, ArrayList<String>> and podmienkyOblast is ArrayList<String>. If I put podmienky.get("index").size() before podmienkyOblast.clear() it returns 6 as it should. However if I put podmienky.get("index").size() after podmienkyOblast.clear() it returns 0. I thought that if I put something to hashtable I actualy add content of it to it. But as I can see it is storing only reference to arrayList. Am I wrong? How should I make this work?
Thanks in forward
You put a reference to your ArrayList into the Hashtable. If you want to add values from your ArrayList to the table, you have to iterate over the elements in the list.
for(Object element : podmienkyOblast) {
// TODO generate a key for your value here
String key = "...";
podmienky.put(key, element);
}
Java deals with Object reference, if you need to copy the object, you need to consider cloning (a.k.a deep copy).
Also, Favour interfaces over implementations, so use List instead of ArrayList.
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
I'm having trouble trying to initiate an arraylist in which the first column I want to be a string, and the second column be a custom object.
For example column [0] be String, and column[1] be an Integer. Convention attempts of creating a multidimensional arraylist as in those used by int[][] or String[][] don't seem to work :( I would welcome any help. At this point I don't think it's something java allows. I can make it work for just one type of object but it's not what I want. Thanks!
Do you need an arraylist? You could create a Map<String, Object> or Map<String, Integer> or whatever you need..
Sure it does, but you weaken/eliminate type-checking:
Map myMap<String>, Integer> myData = new HashMap<String, Integer>();
Now your list of strings can be retrieved by myMap.keySet() and values can be retrieved by myMap.values(). Each of these return a Set, which you can easily convert to a List using the following code:
List<String> strings = new ArrayList<String>(myMap.keySet()); // get your strings
List<Integer> numbers = new ArrayList<Integer>(myMap.values(); // get your numbers
Good luck and if you should run into problems, do leave a comment.
Arrays are geared towards one specific type of thing - be they Object or String or int. Despite the fact that you're adding multiple dimensions to them, they still only hold one type of information.
What you would rather have is a mapping between two objects. This allows you to do the following:
Associate any key to a particular value
Eliminate duplicate key entries
Be much easier to access instead of array indexing
Here's an example. Say your custom object is a Cat, and you want to map the name of the owner to a particular Cat. You create a new instance of a Map.
Map<String, Cat> catOwners = new HashMap<>();
You can then put elements into it...
catOwners.put("Jamie", new Cat("Tycho"));
...and retrieve them with relative ease.
Cat currentCat = catOwners.get("Jamie"); // gets Jamie's cat
if you really want to, you can even iterate over them using the Map.Entry object provided with all Maps:
for(Map.Entry<String, Cat> element : catOwners.entrySet()) {
System.out.println(element.getKey()
+ " owns " + element.getValue().getName());
}
What you can do is use the generic Object type, and cast accordingly.
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