Do objects in ArrayLists have any references? - java

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.

Related

Copying an object and changing it (in Java)

I'm having an odd problem that I haven't encountered before with copying objects in Java.
So, I've written a class in my code called "State". This contains a few ints, a 2d array, a string and such...
So, for an instance of State called S, I want to make a copy of it called X (I do this simply by writing State X = S; ). Then I want to make changes to X, do some evaluations based on those changes and then just throw away X and keep using S. However, the problem I'm getting is that S seems to be getting the changes that I make to X.
This seems odd to me, since I feel quite certain that I've done things like this before but never had this problem.
Any thoughts?
(Thanks in advance)
I want to make a copy of it called X (I do this simply by writing State X = S; ).
That does not make a copy of the object.
Variables (of non-primitive types) in Java are references - they are not the objects themselves. By doing
State X = S;
you are not copying an object, you are just copying the reference - the result is that you now have two variables that are referring to the same object. If you modify the object through one reference, you'll see the changes also through the other reference.
One way to copy objects is by using the clone() method. For this to work, the class of the object that you are trying to copy must implement interface Cloneable. Another (and probably better) way is to create a copy constructor, and use it to copy the object:
public class State {
public State(State other) {
// initialize this object by copying content from other
}
}
// Make a copy
State X = new State(S);
Your code is not creating a copy of the object. What you are doing there is creating a new reference and pointing it to the same object.
Search for "how to clone an object in Java". Read up on the Cloneable interface.

Clear an Object already inserted to a hashmap

calls is an arrayList, customerCalls is a hashMap. I was debugging using eclipse and I fount that calls.clear clear the arrayList object already inserted in the customerCalls Hashmap. I am confused because I thought that once the object was submitted to another data structure, it has an independent entity and no operations can be taken on it unless I access this data structure containing it.
I need to clear the arrayList calls to make it free for a new set of calls that would be dedicated to another contract and later inserted as value for the hashmap key (contract Number). Not clearing it accumlates all calls as it appends current iteration addition to the past iteration one.
> if (callContractID.equals(currentContractID)==false){
> customerCalls.put(currentContractID, calls);
> currentContractID = callContractID;
> calls.clear();
> calls.add(call);
> count++;
> }
else {
calls.add(call);
}
Passing a reference to object A to another object in no way alters A (unless the other object explicitly invokes a method on A to change its state, of course). It doesn't spawn an independent copy of A, or make A immutable, or anything like that.
It sounds like what you want to do is to just create a new ArrayList once you're done with the first one and have submitted it to the map.
if (!callContractId.equals(currentContractID) {
calls = new ArrayList<Call>(); // or whatever the type is
// rest of your code...
}
This will replace the value of calls with a reference to a new, empty ArrayList that you can add new elements to. The old ArrayList will still be available to the HashMap, because the map has its own copy of the reference to that first list.
I am confused because I thought that once the object was submitted to another data structure, it has an independent entity and no operations can be taken on it unless I access this data structure containing it.
No. The map contains a reference to an object... just as if you simply assigned another variable to it. You can have lots of references to the same object, and any changes made via one reference will be seen via any of the others. As a very simple example:
StringBuilder b1 = new StringBuilder();
StringBuilder b2 = b1;
b2.append("foo");
System.out.println(b1); // Prints foo
The exact same thing happens with collections:
StringBuilder b1 = new StringBuilder();
List<StringBuilder> list = new List<StringBuilder>();
list.add(b1);
StringBuilder b2 = list.get(0);
// Now b1 and b2 are both reference to the same object...
b2.append("foo");
System.out.println(b1); // Prints foo
Not at all. The object has not been "submitted", it has been passed to another method (it happens that such method has made its own copy of the reference to the object.
All references to the object point to the same object and may call the same methods. The object (usually) has no means to know where it is being called from.

clearing or set null to objects in java

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.

Java get an element from an array, edit it, then store it in another array, without altering the first array

I have encountered a problem in one of my Java projects, which causes bugs.
The problem sounds as following:
I have two arrays. Let's name them firstArray and secondArray. Object in this case is a seperate class created by me. It works, the array can be filled with objects of that type.
Object[] firstArray= new Object[];
Object[] secondArray = new Object[];
Now, when I get an element out of the first array, edit it and then copy it in the second array, the object from the first array gets altered too.
tempObj = firstArray[3];
tempObj.modifySomething();
secondArray[3] = tempObj;
Whenever I do this, the (in this case) 3rd element(actually 4th) of the first array gets the modifications. I don't want this. I want the first Array to remain intact, unmodified, and the objects I have extracted from the first array and then modified should be stored in the second so that the second array is actually the first array after some code has been run.
P.S. Even if I get the element from the first array with Array.get(Array, index) and then modify it, the element still gets modified in the first array.
Hopefully you understood what I wanted to say, and if so, please lend me a hand :)
Thank you!
You're going to have to create a new object.
The problem is the modifySomething call. When you do that, it alters the object on which it's called. So if you've only got one object (even by two names), you can't call modifySomething or they will both change.
When you say secondArray[3] = firstArray[3], you aren't creating a new object: you're just assigning a reference. Going through an intermediate temporary reference doesn't change that.
You'll need code that looks like this:
Object tempObj = firstArray[3].clone();
tempObj.modifySomething();
secondArray[3] = tempObj;
The clone() method must return a new object divorced from the original but having identical properties.
When you retrieve an element from your array, you have a reference to it. So if you modify it, the modification are shered through all the object's references.
In order to leave it intact, you should use some method like Object.clone() or create a new Object and use its constructor to initialize its fields.
The object extracted from the first array needs to be cloned to create a new instance that is seperate. Otherwise the modification will affect the object in the first array as it is the same object.
When you retrieve an element from your array, you get a reference to it. So if you modify it, the modification are shared through all the object's references.
In order to leave it intact, you should use some method like Object.clone() or create a new method which take in input your retrieved object and return a new one alike.
In Java, when you do this secondArray[3] = tempObj;, you actually put the reference to the array, not the real object
So firstArray[3] and secondArray[3] point to the same real object
What you need to do is to create a new object that is identical to your original object, and put the reference of the new object to your secondArray
It might worth to point out that default clone() function only does a shallow copy, so if you have mutable objects in your object's fields, it might cause some problems. Take a look at this article about how to do a deep copy

How to make a separated copy of an ArrayList? [duplicate]

This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
Java: how to clone ArrayList but also clone its items?
I have a sample program like the following:
ArrayList<Invoice> orginalInvoice = new ArrayList<Invoice>();
//add some items into it here
ArrayList<Invoice> copiedInvoice = new ArrayList<Invoice>();
copiedInvoice.addAll(orginalInvoice);
I thought I can modify items inside the copiedInvoice and it will not affect these items inside originalInoice. But I was wrong.
How can I make a separated copy / clone of an ArrayList?
Thanks
Yes that's correct - You need to implement clone() (or another suitable mechanism for copying your object, as clone() is considered "broken" by many programmers). Your clone() method should perform a deep copy of all mutable fields within your object. That way, modifications to the cloned object will not affect the original.
In your example code you're creating a second ArrayList and populating it with references to the same objects, which is why changes to the object are visible from both Lists. With the clone approach your code would look like:
List<Foo> originalList = ...;
// Create new List with same capacity as original (for efficiency).
List<Foo> copy = new ArrayList<Foo>(originalList.size());
for (Foo foo: originalList) {
copy.add((Foo)foo.clone());
}
EDIT: To clarify, the above code is performing a deep copy of the original List whereby the new List contains references to copies of the original objects. This contrasts to calling ArrayList.clone(), which performs a shallow copy of the List. In this context a shallow copy creates a new List instance but containing references to the original objects.
If you are storing mutable objects into the ArrayList, you will need to copy each object when you copy the ArrayList. Otherwise, the new ArrayList will still hold the original references.
However if you're storing immutable objects, it's fine to use:
ArrayList copiedInvoice = new ArrayList(originalInvoice);
I thought I can modify items inside the copiedInvoice and it will not affect these itmes inside originalInoice.
This happens because what gets copied is the reference variable and not the object it self.
Hence you end up with two "references" pointing to the same object.
If you need to copy the whole object you may need to clone it.
But you might have problems if you don't clone the object internal attributes if they happen to be other objects.
For instance the following class definition won't give you any problem.
public class Something {
private int x;
private int y;
private String stringObject;
}
If you create a copy of that, you would copy the current values of its attributes and that's it.
But if your class do have another object inside you might consider to clone it too.
class OtherSomething {
Something something;
private int x;
}
If you do the following:
Something shared = new Something();
OtherSomething one = new OtherSomething();
OtherSomething two = new OtherSomething();
one.something = shared;
two.something = shared;
In this case, both one and two have the same reference variable to the same shared "something" and changing the value in one would affect the other.
That's why it is much simpler/better/easier to use immutable objects.
If you need to change the value of an immutable object you just create a new one with the correct value.
Take a look at ByteArrayOutputStream and ByteArrayInputStream. If all of your classes implement Serializable, then you can make a copy using the above mentioned classes.

Categories