I'm turning crazy on this one.
What I want to do is to get an object from an ArrayList, and add it in 3 others after modifying it.
The problem is that the object I get from my ArrayList is modified aswell when I modify the others... Can't figure out why, is this normal?
Code below:
final Product tmpRef = productsRef.get(i);
Product tmp = tmpRef;
tmp.setPos(products1.size());
Log.d("test2","tmpRef:"+tmpRef.getPos()+";tmp:"+tmp.getPos());
Product tmp2 = tmpRef;
tmp2.setPos(products2.size());
Log.d("test2","tmpRef:"+tmpRef.getPos()+";tmp:"+tmp.getPos()+";tmp2:"+tmp2.getPos());
Product tmp3 = tmpRef;
tmp3.setPos(products3.size());
Log.d("test2","tmpRef:"+tmpRef.getPos()+";tmp:"+tmp.getPos()+";tmp2:"+tmp2.getPos()+";tmp3:"+tmp3.getPos());
tmp.setPos(products1.size());
"pos" is just a simple int, with a getter/setter.
LogCat output:
03-21 09:56:14.926: D/test2(6200): tmpRef:9;tmp:9
03-21 09:56:14.926: D/test2(6200): tmpRef:7;tmp:7;tmp2:7
03-21 09:56:14.926: D/test2(6200): tmpRef:0;tmp:0;tmp2:0;tmp3:0
With #FD_, #blackbelt and #Guidobaldo da Montefelt's explainations, I ended creating a simple new Porduct's constructor to be able to copy the object, and not only the reference. Thanks guys.
Essentially, you just store references in your ArrayList, so it does not matter whether you use something like Product tmp = tmpRef;. tmp still points to the same object, thus changes are applied to both.
In this case, final just means that a pointer cannot be changed once it has been set.
Just search for deep copy java for possible solutions.
final Product tmpRef
means that the Object's reference of tmpRef can not be change. Meaning that you can not do
tmpRef = null;
or
tmpRef = anotherProdocut;
after
final Product tmpRef = productsRef.get(i);,
but the content of the object could change if its member are not final.
Adding to what #blackbelt said, you are getting confused between
"final" and "immutable" concepts. If you don't want the "object's
value" to change, then you have to make it immutable (java doesn't
provide any built-in mechanism for this). final merely prevents you
from reassigning the reference to another object.
EDIT:
1. final - if a "reference" is made final, then you cannot reassign it to point to something else. if a primitive is made final, then it's value cannot change.
eg:
final int i=5;
i=10; // error
final List<String> l = new ArrayList<String>();
l.add("one"); // works
l =new ArrayList<String>() // fails
2. Immutable - you cannot change the values of attributes of the object.
if list l were immutable, then
List<String> l = new ArrayList<String>();
l.add("one"); // fails, depending on your definition/implementation of immutablility
l =new ArrayList<String>() // l is not final, so it works fine
Before doing such a thing, you should really try to understand the difference between references and objects, and more deeply, the difference between copying a reference vs copying an object.
Product is an object stored in a memory zone called Heap. "tmpRef" is a reference that contains the address that points to that object.
This code:
Product tmp = tmpRef;
does not copy any "Product" objects but it simply assign the tmpReference in a new reference variable called tmp. So, you´ll have 1 Object pointed by 2 references. Passing a reference (4 bytes in a 32bit system) is very cheap. That´s why you can pass objects in your methods as parameters. It´s what it is called "copy by reference".
The final keyword means that the reference is immutable, not the Product object.
Probably you have confusion because these logic is not applied to primitive objects, where reference copy does not exist, but primitives are always copied by value, and never by reference.
So if you want to do not change the original object, you do need to create new Product objects in the heap.
Hope this helps.
Related
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.
This is a little confusing question for me to express, but I'll do my best.
So:
ArrayList<Object> fieldList = new ArrayList<Object>();
I then dump a lot of different variables to this array:
fieldList.add(objectsURL); //string
fieldList.add(X); //int
fieldList.add(Y); //int
...
If I change the variable, the values in the array change
too-confirming the array stores a reference to the memory, rather
then value itself.
However, if I then retrieve data from the array then set that...
Object object = ((String)this.fieldList.get(0));
Then set object
object = "meeep!"
objectsURL is not set to "meep!" but rather it retains its original
value.
I assume this is because the "object" is not referencing the original
variable anymore, that instead its pointing to a new immutable string
in the memory.
All expected Java behavior I think....but then, how would I go about
setting the actual original variable? is this possible in java?.
So, in other words. Given only access to "fieldList" is it possible to change the value of
"objectsURL"?
So, if:
String objectsURL = "www.google.com"
fieldList.add(objectsURL);
Is there a way to set objectsURL to "www.stackoverflow.com" using only a reference from fieldList?
I dont want to change the fact that fieldList contains "objectsURL", I want to change what string the variable "objectsURL" actualy contains.
If not, is there an alternative method to achieve the same thing?
I hope my question explains the problem well enough.
My use-case is trying to make a serialization/
deserialization system for a bunch of my objects. I was hoping to put
all the fields into a arraylist I could retrieve for both reading and
writing....thus avoiding having to hard-code long lists of
field[0]=blah and blah=field[0] and then going though constant pains
of needing to renumber them each time I add a new field before
another.
(I cant use Javas inbuilt serialization, as I am using GWT and this is client side only.)
Thanks,
I assume this is because the "object" is not referencing the original variable anymore, that instead its pointing to a new immutable string in the memory.
Correct, each time you use the assignment operator = on an object you change the object it refers to, not the object itself.
To change the values in the List, you use the .set method of an ArrayList
this.fieldList.set(0, newValue);
Since your variable is a String, there is no way you can change the String-variable through the list
Your alternatives:
using a char-array
List myList = new ArrayList();
char[] charArray = "My String".toCharArray();
myList.add(charArray);
charArray[0] = 'A';
String theString = new String(myList.get(0)); // "Ay String"
If you use a char-array, make sure that the length of the array is enough to contain the number of characters you want to have in the future, because to change the length of the array you will need to create a new array (array lists can be expanded dynamically, arrays can not)
Embed the String inside your own class (I have ignored getters and setters here)
class MyString {
public String value;
public MyString(String value) {
this.value = value;
}
}
MyString myStr = new MyString("some value");
list.add(myStr);
((MyString) list.get(0)).value = "a new value";
System.out.println(myStr.value); // will print "a new value"
Strings are immutable, so it is impossible to change the contents of a String object. Also, you cannot use the list to change what object the reference variable objectsURL points to. To achieve what you want, you will need to create a custom class that has a String member. You can then store instances of this class in a List and change the String references to via the references in the list. The changes will then be reflected in any other reference variables which refer to the objects in the list.
First, you declare a variable 'object' and assign some Object out of the ArrayList. Later you assign some other object "meeep!" to this variable. There is no reason that your 'object' variable is related to the ArrayList.
When adding an object to a data structure the reference to the object is always added, not the object itself. So below code snippets are fundamentally the same ?
Snippet 1 -
Object objectRef = new Object();
Vector vector = new Vector();
vector.add(objectRef);
Object object = (Object)vector.get(0);
Snippet 2 -
Vector vector = new Vector();
vector.add(new Object());
Object object = (Object)vector.get(0);
Java always passes references by value. So whenever you use a reference as an argument to a function call, the reference value is copied. The object itself is not passed, a copy of its reference is. Its an important distinction to understand. It also is important to understand that with the reference you (or the library) can still operate on the object passed via its reference.
Your snippets are not entirely the same. In (1) you end up with two references to the same object, objectRef and object. In (2) you end up with only 1, object. In both cases the vector has a different reference to the object.
Both snippets are the same, the only difference is that you have a location variable named objectRef which can be modified after it was added to the vector, if it is modified, it will be reflected in the object returned from the vector as well since it IS the same object. Though the "Object" class doesn't exactly have any methods which can modify the object.
That's correct. In the java programming language there is only heap. Every object exists out there in magic heap land. Every non-primitive value is just a pointer whose only operation is to dereference. A List can be thought of as a list of pointers to strings, the actual data content of the strings is out on the heap, not inside the collection structure.
(This is the semantics of the language of course. Modern runtimes are under no obligation to actually do it that way, the JIT uses method stacks, it just has to behave exactly the same as if it worked that way.)
They result in the same state except that at the end of snippet 2, you don't have a symbol called objectRef.
Both snippets give the same results. The difference lies that in the first snippet you have three references to the same object (objectRef, object and a reference in vector), and in the second snippet you only have two references to the same object (object and a reference in vector).
so suppose i have
ArrayList<E> X = new ArrayList<E>();
and I pass X into some parameter:
Something Y = new Something(X);
it will pass X by reference rather than by value and I don't want this....class Something has a field with Arraylist type that is supposed to be distinct to itself and I don't want to go and iterate through the damn arraylist and set it individually just to instantiate the field...
is there a way to easily make Java pass any object parameters by value rather than reference without having have to implement cloneable interface on all my objects which is a pain in the butt
As Java do not allow direct pointer manipulation, you cannot dereference a pointer. You have to live with references. If you want to prevent the passed object from being modified, you have to clone it or make it immutable (like String). Also keep in mind that object references are passed-by-value. So statements like "Java has pass-by-reference" is not exact, if we take pass-by-reference in the C++ sense.
It actually passes X by value. (The Something constructor can't change the variable X in the calling code.) X happens to be a reference to an ArrayList, not an ArrayList. You could try:
Something Y = new Something(new ArrayList<E>(X));
Instead of creating a new object everytime you can pass an unmodifiable list. This list is read-only and the user has to create another list if he wants to make any modification.
List unmodifiableList = Collections.unmodifiableList(list);
List newList = new ArrayList(unmodifiableList);
Collections.sort(newList);
The constructor of ArrayList takes an existing list, reads its elements (without modifying them!), and adds them to the new List.
hey people,
I have created a class which extends application to share certain variables. Within each activity I then use an object of that class (globalstate) as so:
gs = (GlobalState) getApplication();
I then declare local variable which reference the shared ones such as:
boolean localStr = gs.str;
Now I am under the impression this would be passed a a reference and therefore any change to localStr would be reflected in str within globalstate. However this does not seem to be the case.
Some variables created in this manor such as an array of object seem to be passed as reference and therefore any changes are reflected within globalstate however for almost every other variables (booleans, strings & ints) it seems the changes aren't reflected and therefore are being copied rather than referenced.
Have I done something wrong or is this how android works, if so how can i pass by reference? Thanks.
boolean is a value type, Java doesn't use a reference to store it. It's the same for all Java primitive types (int, long, ...). Strings are different, they are reference types but are immutable as explained by Johan Sjöberg.
That's why when using an ArrayList (for example) you will get an error if you try to use a value type like new ArrayList<boolean>(). But new ArrayList<Boolean>() will work because Boolean is a reference type, not a value type.
To avoid your problem you should use your reference everywhere (use gs.str instead of creating a local variable, localStr). I can explain it better if you don't really understand what I'm saying.
That's not only how android works, that's how java works. Strings are immutable, meaning they cannot be changed. Every time you try and modify a string a new string is created; hence the following code will leave the string named first unchanged:
String first = "first"; // First points to mem address e.g., 0x1
String second = first; // Second also does
second = "something else"; // Second now points to 0x2, first still to 0x1
It's exactly the same behaviour for all primitive types (int, boolean etc).
However, when you pass a List of objects, alterations of the list contents will be changed everywhere. This is since your objects share the same list reference (which itself doesn't change, only it's contents).