I am having trouble understanding some simple code relating with Lists and maps. Take the code below as an example:
public class test {
private Map<Integer, List<String>> myMap ;
public test(){
myMap = new HashMap<Integer, List<String>>();
}
public void addToMap(String ss){
List<String> myTemp = myMap.get(ss);
Random r = new Random();
if(myTemp == null){
myTemp = new ArrayList<String>();
myMap.put(r.nextInt(100), myTemp);
}
myTemp.add(ss);
}
public Map<Integer, List<String>> getMap1(){
return myMap;
}
public static void main(String args[]){
test myTest = new test();
myTest.addToMap("abdc");
myTest.addToMap("eeer");
System.out.println(myTest.getMap1());
}
}
How exactly does the addToMap() add a new element to the mylist Map. More specifically, how does myTemp.add(ss) add a new element to myMap when myTemp is a local variable and gets deleted once its done executing. Moreover, removing myTemp.add(ss) from the addToMap() method prints out an empty HashMap in the main method, why is this? How does the put method insert the element into the map when it is executed before the add method? Thanks.
Edit: I edited the code to make a little more sense.
Unlike C++, in Java all variables are references to the real objects. So, when you do
myTemp = new Object();
you create an object in the heap whose reference is copied to the stack variable myTemp. If the reference is added to the list (or other variable), a copy of the reference is added there.
Once myTemp is destroyed, the object continues alive. The exception is when all the references to the object are gone; then the object can not be reached by the code and Garbage Collection can (but it is not forced to) delete it from memory.
myTemp isn't the thing - the list - it is instead a reference to the thing. But it isn't that specific reference which you are putting into the map; it is the thing to which it refers. The map has its own reference to the thing, so deleting the myTemp reference doesn't affect the map.
myTemp is a reference to the list. The list is allocated in heap memory and myTemp is actually is a pointer stored on the stack. Whenever you exit the function it gets deleted but the memory allocated in heap memory won't get deleted. Memories allocated in heap memory release when there is no reference to them by Garbage Collector.
You are right. When you leave the addToMap() method, the myTemp will be released(Actually it will be poped from the stack) but the list which is allocated in heap memory space will remain. Everything which is created using new keyword, will be placed inside heap memory space.
Read this link Java Reference Objects
From a best practice point of view, calling a HashMap "mylist" is problematic in a number of ways. It isn't orthogonal and "mylist" doesn't tell you a lot about its usage.
In answer to your question, it's a simple case of, if the item exists in the map, it is retrieved and the string added to it, if not the list is created and added to the map. As long as the map is in scope, the references to any lists added to it (even if they were created in the scope of a function) remain in scope themselves.
However, this isn't a helpful example at all as it mixes its naming and provides no real world use-case.
Related
I've made an ArrayList called books, which only contains the class LibraryBook that I've created. And I've created this in another class called Library.
public class Library {
ArrayList<LibraryBook> books;
public Library() {
books = new ArrayList<LibraryBook>();
}
Then I have a method in this Library class which adds LibraryBooks to this ArrayList.
public void add( String title, String author) {
LibraryBook b = new LibraryBook( title, author);
books.add( b);
}
So if I say, in a main method;
Library library = new Library();
library.add( "Title", "Author");
I get a new LibraryBook in my ArrayList. However if I do this again;
Library library = new Library();
library.add( "anotherTitle", "anotherAuthor");
Now I have another LibraryBook in my ArrayList. But in the add method, I've made LibraryBook b to refer to these objects. So what refers to these objects now? Are they both referred by the variable b (which shouldn't happen as far as Java is concerned)? Or they don't have any references at all?
My guessing would be, that when I put an object into an ArrayList, it creates a referrence to this object and stores that instead.
And if I put a reference to an object into an ArrayList, it does the same thing.
If this is true, then another question, how does Java let me create two different LibraryBooks that are referred with the same variable b in my add method?
EDIT: I've found another thread with the same title before I made this post, but I didn't quite get the answer I was looking for so here we go.
EDIT2: With all due respect, how is this question any relevant to the linked post? The linked post is so "general" that any post in this forum can be linked to that one and considered as a duplicate. It doesn't hold an answer to my question at all. Even the word ArrayList is not present in there. I would appreciate a feedback on how the two posts are even related other than the fact that the linked post is a general "lesson" for the basics of Java.
My guessing would be, that when I put an object into an ArrayList, it creates a referrence to this object and stores that instead. And if I put a reference to an object into an ArrayList, it does the same thing.
Almost. When you say something like..
libraryList.add(new LibraryBook("hello", "world"));
You're not actually adding a new object into the list. The new keyword returns the reference of the newly created object, not the object itself. So saying..
LibraryBook b = new LibraryBook("hello", "world");
and adding that to the list is exactly the same. In fact, the compiler will likely remove the unnecessary variable b to save memory allocations during runtime.
If this is true, then another question, how does Java let me create two different LibraryBooks that are referred with the same variable b in my add method?
Good question! To understand this, you need to understand scoping. If I declare a variable inside a function, like so:
public void myMethod() {
String s = "hello";
System.out.println(s);
}
When that method exits, the reference (that you've called s) is destroyed. If no other references exist, the garbage collector comes along and destroys the object itself, since nothing refers to it.
This means that if you call myMethod again, a new reference is created. They might share the same block of code but they will exist as two mutually exclusive variables in the system.
In your case, because you're adding objects into your list, your list maintains a reference to the objects and the garbage collector won't remove them, so every time a new b is made when you call the function that has no link to any other version of b that was made when the method was called previously.
If look at your add method,
public void add( String title, String author) {
LibraryBook b = new LibraryBook( title, author);
'b' is a method local variable (reference) which will get destroyed, once the method execution gets completed, but the object still will be there on heap. so everytime you call this method, a new object will get created, and reference b will be pointing to the newly created object
books.add( b);
This line will put the refernce of objects created to arraylist object, objects are always stored in the heap. So whenever you create and object, memory is allocated in heap, and the reference will be stored in the stack segment, and the frame will be taken out of stack, once the method execution finishes.
Since books is a class level variable, the reference to those objects will exist till Library object is garbage collected.
I have a problem with understanding one thing.
I have:
List<Map> resultList = new ArrayList<Map>();
Then this resultList is filled with some data
resultList.addAll(somemethod(something, something, else));
Later in the method I have this kind of code:
Map timeSpan = someMethod(resultList, date);
timeSpan.put(KEY_ART, VALUE_ART);
timeSpan.put(KEY_TIMESPAN, true);
So I have a question now. If Map timeSpan is a map referenced to an element of List<Map> resultList, is using the put() method on the timeSpan map affecting the element in resultList?
I am asking this question cause a collegue told me that this is working this way - modifying an element in timeSpan is also modyfying this element in resultList. She is far more experienced, and I don't just want to believe her but I want to understand why it is working this way.
You have to remember that resultList is a reference to an object. You can copy this reference around and use it in many way, in many places but there is only one object. This means when you alter the object, there is only one view of this object.
Java objects are always instantiated as a reference to a memory space. If you create a second object from the first object, both will point to the same memory space:
Map a = new HashMap();
Map B=b = a;
Here, we first create an instance A which points to a HashMap which is created somewhere in memory. Next, we create an instance of Map b and have it reference to the same memory space as Map a. Now, when we change map b, these changes will also be made to Map a, since they point to the same memory construct.
In you case, you have a List this in itself is a memory construct. Each item in the list references a seperate Map. These are each also created somewhere in memory. The moment you retreive a Map from the list, you retreive the reference to the memory space where the actual map is located. After that, it works exactly as the example.
addAll() will copy all the elements to the current list from the Collection/List you passed as an argument to this method. In your case, as each element is a reference to Map object, after copying, you have 2 references pointing to same Map object, so changes done using any one reference are visible through the other one.
The short answer is yes, put() for timeSpan is affecting resultList, because when a method returns a Map, which is got from the List, it returns a reference to the heap where map elements are located.
What is better:
This
public Move(moveString) {
ArrayList<Square> moveSquares = splitToSquares(moveString.toLowerCase());
this.from = new Square(moveSquares.get(0));
this.to = new Square(moveSquares.get(1));
}
or this:
public Move(moveString) {
ArrayList<Square> moveSquares = splitToSquares(moveString.toLowerCase());
this.from = moveSquares.get(0);
this.to = moveSquares.get(1);
}
In the first, I use the information from the move objects to create a new one.
In the second, I directly use the object.
It doesn't make much difference for my program now, but I am wondering if Java needs to keep the complete ArrayList because I referenced them. If that is a huge list, it would be better to just copy the two objects I need and let the rest be collected by the GC, wouldn't it?
Or is the GC intelligent enough to do that himself? Then the first method would make unnecessary copies of the objects. Not a big deal in this case, but in another there may be hundreds or thousands such objects.
In both cases, the ArrayList will not be referenced once you exit the Move() method. It does not matter that you reference elements within the list. If the list itself is unreachable, it becomes eligible for garbage collection.
The two referenced elements will remain alive as long they are referenced by another live object. In your second example, those two elements will be reachable at least as long as your Move object is reachable. But if there are other elements in the list, and they are not referenced outside of the list, then those elements will be eligible for garbage collection when the list goes out of scope.
Your list moveSquares becomes unreachable as soon as the Move constructor ends (this is a reasonable assumption, although it ultimately depends on what exactly the splitToSquares method does).
In additon to that, the fact that one item of the list is still reachable has nothing to do with the reachability of the list itself, or of any other list item. They will all become unreachable, thus collectible.
I was recently looking into freeing up memory occupied by Java objects. While doing that I got confused about how objects are copied (shallow/deep) in Java and how to avoid accidently clearing/nullifying objects while they are still in use.
Consider following scenarios:
passing a ArrayList<Object> as an argument to a method.
passing a ArrayList<Object> to a runnable class to be processed by a thread.
putting a ArrayList<Object> into a HashMap.
Now in these case, if I call list = null; or list.clear();, what happens to the objects? In which case the objects are lost and in which cases only the reference is set to null?
I guess it has to do with shallow and deep copying of objects, but in which cases does shallow copying happens and in which case does deep copy happens in Java?
Firstly, you never set an object to null. That concept has no meaning. You can assign a value of null to a variable, but you need to distinguish between the concepts of "variable" and "object" very carefully. Once you do, your question will sort of answer itself :)
Now in terms of "shallow copy" vs "deep copy" - it's probably worth avoiding the term "shallow copy" here, as usually a shallow copy involves creating a new object, but just copying the fields of an existing object directly. A deep copy would take a copy of the objects referred to by those fields as well (for reference type fields). A simple assignment like this:
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<String> list2 = list1;
... doesn't do either a shallow copy or a deep copy in that sense. It just copies the reference. After the code above, list1 and list2 are independent variables - they just happen to have the same values (references) at the moment. We could change the value of one of them, and it wouldn't affect the other:
list1 = null;
System.out.println(list2.size()); // Just prints 0
Now if instead of changing the variables, we make a change to the object that the variables' values refer to, that change will be visible via the other variable too:
list2.add("Foo");
System.out.println(list1.get(0)); // Prints Foo
So back to your original question - you never store actual objects in a map, list, array etc. You only ever store references. An object can only be garbage collected when there are no ways of "live" code reaching that object any more. So in this case:
List<String> list = new ArrayList<String>();
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("Foo", list);
list = null;
... the ArrayList object still can't be garbage collected, because the Map has an entry which refers to it.
To clear the variable
According to my knowledge,
If you are going to reuse the variable, then use
Object.clear();
If you are not going to reuse, then define
Object=null;
Note:
Compare to removeAll(), clear() is faster.
Please correct me, If I am wrong....
It depends on how many variables are referenciating to each of your objects, to explain this it would be better some code:
Object myAwesomeObject = new Object();
List<Object> myList = new ArrayList<Object>();
myList.add(myAwesomeObject);
myList = null; // Your object hasn't been claimed by the GC just yet, your variable "myAwesomeObject" is still refering to it
myAwesomeObject = null; // done, now your object is eligible for garbage collection.
So it doesn't depend whether you pass your ArrayList as an argument to a method or the like, it depends on how many variables are still refering to your objects.
If you passed an ArrayList to a method then list = null will have no effect if there is a live reference to the list somewhere eg in the calling code. If you call list.clear() anywhere in the code the references to the objects from this list will be nulled. Passing a reference to a method is not shallow copying it is passing reference by-value
Java GC automatically claims the objects when they are not referenced anywhere. So in most cases you will have to set the reference as null explicitly
As soon as the scope of the variable ends the object becomes eligible for GC and gets freed up if no other reference points to the object.
Java is pass by value so if you set the list as null in the method then it will not affect the original reference that was passed to you in the method.
public class A{
private List<Integer> list = new ArrayList<Integer>();
public static void main(String[] args) {
A a = new A();
B b = new B();
b.method(a.list);
System.out.println(a.list.size()); //Will print 0 and not throw NullPointerException
}
}
class B{
public void method(List<Integer> list){
list = null;
//just this reference is set to null and not the original one
//so list of A will not be GCed
}
}
If you put the list into a hash map, the hash map now holds a reference to the list.
If you pass the list as an argument to a method, the method will have a reference to it for the duration of the method.
If you pass it to a thread to be manipulated, the thread will have a reference to the object until it terminates.
In all of these cases, if you set list = null, the references will still be maintained, but they will disappear after these references disappear.
If you simply clear the list, the references will still be valid, but will now point to a list that has suddenly been emptied, by means that may be unknown to the programmer and may be considered a bug, especially if you use the thread.
I was recently looking into freeing up memory occupied by java objects.
A piece of advice.
It is usually a bad idea to think about this. And it is usually a worse idea to try to "help". In 99.8% of cases, the Java garbage collector is able to do a better job of collecting the garbage if you actually just let it get on with it ... and don't waste your effort by assigning null to things. Indeed, the chances are that the fields you are nulling are in objects that are about to become unreachable anyway. And in that case, the GC is not even going to look at the fields that you've nulled.
If you take this (pragmatic) view, all your thinking about shallow versus deep copies and when it is safe to null things is moot.
There is a tiny percentage of cases where it is advisable to assign null ... to avoid medium or long term storage leaks. And if you are in one of those rare situations where it is "recycling" objects is actually a good idea, then nulling is also advisable.
I'm writing an array-backed hashtable in Java, where the type of key and value are Object; no other guarantee.
The easiest way for me code-wise is to create an object to hold them:
public class Pair {
public Object key;
public Object value;
}
And then create an array
public Pair[] storage = new Pair[8];
But how does the jvm treat that in memory? Which is to say, will the array actually:
be an array of pointers to Pair() objects sitting elsewhere, or
contain the actual data?
edit
Since the objects are instantiated later as new Pair(), they're randomly placed in the heap. Is there any good way to ensure they're sequential in the heap? Would I need to do some trickery with sun.misc.unsafe to make that work?
Explaining my motivation, if I want to try and ensure that sequential items are in the same page of memory, is there any way to do this in Java?
The array will be an object on the heap containing pointers to the Pair objects which will also be on the heap (but separate from the array itself).
No, the storage array will only contain pointers to the actual Pair objects existing somewhere else on the heap. Yet, remember to instantiate 8 Pair objects and make each element of the array point to these objects. You need to have something like this after the code that you have written:
for(int i=0;i<storage.length;i++)
storage[i] = new Pair() ;
Only then will the Pair objects be created and correctly referred to by the storage array.