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.
Related
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]
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.
I have a problem with removing some objects from a list in java. I'm using ibatis and have two list; both lists are created by different sql queries. I'm sending both lists to the same result map which means same bean class. If second list have same rows with the first one I want to remove them from the first list.
lst = dao.listWaitingCustomers(totalThreadCount , jobNumber);
List lst2 = dao.listWaitingCustomers2();
lst.removeAll(lst2);
But this does not work because I think even same rows return from db, they're not in same reference I mean they're different objects. How can remove those objects from one list if they have same column values?
You have to override the equals and the hashcode method of the object that you are storing in the lists.
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
I have a Map like this :
Map<String,GridCell> cellsMap
I pass this into a method and the return from that method should contain a Map(say answerMap) which contains all the entries of cellsMap map plus an extra entry that contains a String as the key and a String as the value . Something like :
Map<String,Object> answerMap = new ConcurrentHashMap<String,Object>();
//answer should first contain all the map entries of cellsMap and then add an extra entry like the following
answer.put(getId(), getSelectionValue()); // getSelectionValue() returns a String that contains coordinates of the selected cells.
return answerMap;
Have you considered the Map.putAll() method ?
e.g.
answerMap.putAll(cellsMap);
I don't think this is a good object model, by the way. I think you're better off creating a new class that contains your original map (maybe a copy) and an additional field for your String/String pair.
Otherwise you're throwing objects of different types into the same map, and that's going to make life complicated when you later extract that info. Each time you extract via a key you're going to have to check the type of the object returned. Note that ConcurrentHashMaps don't maintain insertion order.
Use clone() method.
HashMap answerMap = (HashMap)cellsMap.clone();
Map interface has putall() method which add all values of another object in Map.