Understanding Reference Behaviour in Java - java

I was trying something, and I came across this interesting scenario. I just wanted to understand what is the major difference in these two snippets.
Initially, I took two sets, initialized one and assigned the reference to other. When I cleared set A, I noticed that Set B size changed to zero as well
Set<String> A = new HashSet<String>();
A.add("hello");
A.add("HEY");
A.add("hey");
Set<String > B = A;
System.out.println("initial sizes :" + A.size() + " " + B.size());
A.clear();
System.out.println("final sizes :" + A.size() + " " + B.size());
The output of this was something like this :
initial sizes :3 3
final sizes :0 0
Now, I tried to depict the same behavior for objects as follows:
Object a1 = new Object();
Object b1 = a1;
System.out.println("initial sizes :" + b1.toString() + " " + a1.toString());
a1 = null;
System.out.println("initial sizes :" + b1.toString() + " " + a1);
The output for this was something like :
initial sizes :java.lang.Object#54182d86 java.lang.Object#54182d86
initial sizes :java.lang.Object#54182d86 null
What is the exact difference here, I was expecting to get a NullPointerException when i tried to print b1.toString()

For primitives such as int, double, float etc, a copy of the value is made and that is passed by value:-
int x = 10;
public void foo(int k){
}
foo(x)
Here k will get a copy of the value stored in x so k will now have a value of 10. However, x and y are in two different memory locations. Changing the value of x will not change the value of k.
For object references a copy of the reference is made and that is passed by value (a reference is nothing more than the address of some memory). So in essence both references will now point to the same object (that is, the same memory location).
Myobject m = new Myobject();
public void bar (Myobject j){
}
bar(m)
A copy of the value of the reference m will be made and assigned to j. Both m and j will now point to the same object.

The difference here, is that a1 and b1 are not the objects themselves but references to those objects. So, if you modify the object referenced by a1 the object referenced by b1 (which is the same object) will change too. If however you tell a1 to point to another instance (or null in this case) it will no longer reference the same object so changes to that won't effect b1.
To go into a little more detail: Java is pass by value. However when you try to pass an object (rather than a primitive value) you're actually passing the value of the reference (also sometimes called the handle). That's why it can sometimes be a bit confusing when trying to determine whether Java is pass by handle or pass by reference.

Check this image. A2/A3 are REFERENCES to bojects. In first case these are references to Set (a1 on image). When one reference modifies object second reference sees the same change
On the other hand if you just set reference = null then you "erase one arrow" from the picture. The reference stops pointing to object, but the other reference still points to it.

You just noticed that java is completly pass by value, in which even references to other objects are passed as value.
If you null an object you are actually not nulling objects, which methods would be executed on the same object. You are only nulling the value of the reference to the object.
Check this example, you are just nulling the value of the reference on list 1, while you are still able to execute the methods on the original list.
ArrayList<Integer> list1 = new ArrayList<Integer>(0);
list1.add(1);
list1.add(2);
ArrayList<Integer> list2 = list1;
list1.clear();
list1 = null;
System.out.println(list2.size());
System.out.println(list1.size()); // will cause an nullpointerexception
The calls of methods on list1 does also effect list2, but nulling the object list1 wont affect the list 1

My answer is an additional element to #I.K. answer. Primitives (copy of value) and Non-Primitives (copy of reference values) - that's the key understanding.
Look at this code where I have put the comments (hosted on IDEONE):
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
String a1 = "blablabla";
String b1 = a1;
System.out.println(b1);
System.out.println(a1);
System.out.println(b1.hashCode());
System.out.println(a1.hashCode());
a1 = "wow";
System.out.println(b1.hashCode());
System.out.println(a1.hashCode());
System.out.println(b1);
System.out.println(a1);
Set<String> a = new HashSet<String>();
Set<String> b = a;
a.add("hey");
a.add("HellO");
System.out.println(b.size());
System.out.println(b.hashCode()); // e.g. 69712814
System.out.println(a.hashCode()); // same - 69712814 (It's important to know)
a.clear();
System.out.println(a.size()); // same hashcode i.e. gets affected by the change.
System.out.println(b.size());
You can see that they are effectively hashed using the same code.
String class is a bit special in Java as you probably know that they use the String pool for "Intern"ing the values. If you run the code above you can see that as soon as you do a1 = "wow"; it will create a new value "wow" in the pool and therefore, the hashCode() changes.

Related

store object's name and reference it in the code

I have two lists, after find which one has the highest value I store the other one's name into a string variable, like this:
iterateList = "lstDrives"; //lstDrives
So, I want to use that stored name in the next lines:
for(Integer currentElement : iterateList){
if((mostExpensiveValue + currentElement) > b){
result = -1;
}
}
right now I obtained an error because I'm trying to iterate the String var and I want to refer to its content, my question is: how can I achieve this?
This is the entire piece of code:
if(mostExpensiveKb > mostExpensiveDv ){
iterateList = "lstDrives"; //lstDrives
mostExpensiveValue = lstKb.get(keyboards.length-1);
} else{
iterateList = "lstKb"; //listKb
mostExpensiveValue = lstDrives.get(keyboards.length-1);
}
for(Integer currentElement : iterateList){
if((mostExpensiveValue + currentElement) > b){
result = -1;
}
}
That's not how java works. Object do not have names; variable names do not survive the compilation process.
Object o = new Object();
This does not mean that your object's name is 'o'. After all:
Object o = new Object();
Object v = o;
There is only one object here (to make an object, new must be invoked; it is only invoked once here, therefore, there cannot possibly be 2 objects). You have 2 variables that are both referring to the same object. It's like having 2 post-it notes with the same house address written on each. That doesn't mean you have 2 houses.
So, does that mean this one object's name is both o and v?
Object o = new Object();
Object v = o;
o = null;
v = null;
Does that mean that the name of the object used to be o, but then it became both o and v, and then it became just v, and then it became nameless?
Hopefully this gives you the insight as to why objects do not have names and java is not now and not ever going to let you do something like obj.getName().
Java compiles code to an intermediate step (.class files) before you run them. In this intermediate step, local variable names disappear; they can remain for debug purposes but the actual act of running class files ignores this, and you can see this in class files: The local var names don't need to be there in the first place (depends on whether you're using -g when compiling or not to save debug vars), and if they are there, it's in a separate attachment to a method which the JVM just skips completely when reading a class file.
As a consequence, something like getVar("varName") does not now and will never exist in java either. Let alone that java is strongly typed and getVar wouldn't let you type it, given that "varName" could as far as you know, be supplied by the user on the fly.
In other words, you're not 'thinking in java' here. What you're trying to do doesn't work, never has, and never will.
What you can instead do is simply make another variable. After all, all non-primitive variables are merely references (postit notes that contain an address to an object, NOT the object itself). Postit notes are cheap. Here:
List<String> someList = readTheCollectedWorksOfShakespeare();
Let's assume here that this method makes a humongous list with all the lines of everything shakespeare ever wrote. That's a sizable list! Let's say 100MB's worth, so your VM's memory load spikes considerably after this line. Then you do:
List<String> list2 = someList;
List<String> list3 = someList;
List<String> list4 = someList;
List<String> list5 = someList;
and check memory load again: No effect. These are 4 references (pointers). The above 4 lines execute pretty much instantaneously, and cost no memory. It's just that you now have 5 post it notes, all with the address to some ginormous castle on it. The computer did not have to painstakingly copy the castle brick by brick. You can do the same thing in your code:
List<Integer> iterateList;
if (mostExpensiveKb > mostExpensiveDv) {
iterateList = lstDrives;
mostExpensiveValue = lstKb.get(keyboards.length-1);
} else {
iterateList = lstKb;
mostExpensiveValue = lstDrives.get(keyboards.length-1);
}
for (Integer currentElement : iterateList) {
if ((mostExpensiveValue + currentElement) > b) {
result = -1;
}
}
You can use an hashMap to store the list name like key and the list object like value:
Map<String, List<Integer>> namesAndLists= new HashMap<String, List<Integer>>();
namesAndLists.put("lstDrives",lstDrives);
namesAndLists.put("lstKb",lstKb);

Polymorphism. Problems with copy arrays

Empleado[] empleados = new Empleado[3];
empleados[0] = new Empleado("Alfredo", 20000, 2020, 6, 01);
empleados[1] = new Empleado("Alejandro", 21000, 2020, 7, 04);
empleados[2] = new Jefatura("Laura", 25000, 2010, 3, 6);
Jefatura jefeRRHH = (Jefatura) empleados[3]; // CASTEO
jefeRRHH.setIncentivo(1000);
Why is it that when i set the incentive for jefeRRHH, empleados[2] is changed? I thought if jefeRRHH was modified this didn't affect the array empleados.
I mean, when I walk the vector with a foreach loop, empleados[2] give me back with the incentive applied.
package poo;
public class test {
public static void main(String[] args) {
String Nombre = "José";
int j = 1;
String[] nombres = new String[1];
nombres[0] = Nombre;
int a = j;
Nombre= "Maria";
System.out.println(Nombre);
System.out.println(nombres[0]);
j=2;
System.out.println(j);
System.out.println(a);
}
}
Why isn't the same thing here?
When I declare the new variables I'm not assigning addresses, I'm assigning copies, right?
The following will hold true for all Objects in Java.
Assuming a simple class:
public class Foo {
}
When you then instantiate an object of class Foo, you will actually allocate memory on heap, construct a Foo-Object and retrieve the adress to this location, which will then be stored in the object variable:
Foo f = new Foo(); // f points to a Foo-Object
So, instead of copying the actual object, the reference will be copied...
Foo farr[] = new Foo[1]; // create array with a capacity of 1
Foo f = new Foo(); // f now holds the adress to an actual Foo-Object
farr[0] = f; // farr[0] now holds the adress to the SAME Foo-Object
Here is a nice article about references and objects,
and here is a nice answer as follow-up-reading
Furthermore there are primitive datatypes and String-literals.
Primitive datatypes are always simply copied, whatever happens. So there will never be the case, where you change one primitive variable and another one changes.
Strings on the other hand are a bit trickier. They are objects wrapped around a char[], and therefore are (as already stated by others here) immutable and share a so called String constant pool. Modifying a String will therefore result in a new String with the result, and the original String remains unmodified.
You probably have a typo in the code, you should reference the element with index 2 here:
Jefatura jefeRRHH = (Jefatura) empleados[3]; // CASTEO
So, you create a new object with
new Jefatura("Laura", 25000, 2010, 3, 6);
and store a reference to it in the element of the empleados array with index 2. Then you say that jefeRRHH will be another reference to the same object. Now you have two references pointing at the same object. Think about them like different names (or nicknames if you like) of the same person. When you access/modify the object using any of the references you change the same object, so the changes are visible through any other names/references.

How set is working for duplicates entry and remove operations in java

I have written following code
class Test {
public static void main(String... args) throws Exception{
Set<Integer> s = new HashSet();
Integer i1 = new Integer(1);
s.add(i1);
Integer i2 = new Integer(2);
s.add(i2);
i1 = 5;
s.remove(i1);
System.out.println("size= "+s.size());
}
}
Output: 2
I have added two integers(i1 and i2) in set and i1 is modified.
When s.remove(i1) is there, output is 2
when I comment out s.remove(i1) still output is 2.
Why this is so.How it is working in background.
Thnaks in advance!
Integer objects are immutable objects and hence any change to them will create a new object. So when you change
i1 = 5;
the original i1 which was stored in set is not changed. As i1 now holds the reference of a different object, which is not present in Set so calling
s.remove(i1);
will have no effect i.e. it does not remove anything and the size of the set remains 2. To confirm whether remove has removed anything or not, you can use boolean return value of remove method. It will return true if the value is removed. So try this:
if(s.remove(i1)) {
System.out.println("i1 removed");
} else {
System.out.println("i1 NOT removed");
}
This line
i1 = 5;
is actually compiled to
i1 = Integer.valueOf(5);
So after execution, i1 is holding another (new) reference, with a value that is not in the HashSet, so nothing is removed.
Java primitives types can only have VALUES.
While Java boxed types have VALUE and IDENTITY.
Two Java boxed types can have same VALUE but different IDENTITY.
Java boxed types are IMMUTABLE; any change in their VALUE will also change the IDENTITY.
If IDENTITY of an object is changed; Java collections cant work effectively.
Statement: i1 = 5; => is actually i1 = Integer.ValueOf(5) => which meant new VALUE of i1.
Collections store only IDENTITY but not VALUE.
Removal by non existing IDENTITY (i1 = 5) from collection, will result nothing.
Source ..... Effective Java by Joshua Bloch
Collections (Set is a collection) in java works with the 'equals()' method, not with the '=' reference operator. That is, when you set 'i1=5', and then 's.remove(i1)', the remove method will compare with all elements.
a) Beginning with the first: is 5 equal to 1?. No, so don't remove that element.
b) The second: is 5 equal to 2?. No, so don't remove that element.
Adding duplicate works the same, on an empty set:
i1=1;
i2=1;
s.add(i1);
s.add(i2)
This will result on a set with only 1 element, because 'i1.equals(i2)' is true, and doesn't matter that 'i1=i2' is NOT true.
It is very important to distinguish between objects and reference variables. A reference expression, including a reference variable, is either null or a pointer to some object.
The HashSet contains its own pointers to the objects in the set it represents. Working through the code:
Set<Integer> s = new HashSet();
// s is a pointer to a new, empty, HashSet.
Integer i1 = new Integer(1);
// i2 is a pointer to an Integer object with value 1
s.add(i1);
// The set contains one Integer object, value 1
Integer i2 = new Integer(2);
s.add(i2);
// The set contains two Integer objects, values 1 and 2
i1 = 5;
// i1 is a pointer to an Integer object with value 5
s.remove(i1);
// The set does not contain any object that is equal
// to an Integer with value 5 so it is unchanged
Becase after you do "i1 = 5;", i1 starts pointing to another address. And it is no more the same Integer than is stored in your HashSet.

Why can the immutable class Integer have its value reset?

I was reading earlier that wrapper classes are all immutable. Immutable means that the value cannot be changed. Below I tried this simple example that can just be pasted in to any main method. first I create a Integer that wraps the value five. Immutable means that they cannot be changed so why can I set I to 89. I think that it is because it changes where (I) points to but I am not certain why this is the case.
In my next little example i create an Integer of x which will throw an error if I try and change it. The x seems to be immutable in this specific case but not in the case of the (i) variable.
It seems that I can change the value of (i) whenever I want to so in reality Integer without the final keyword is not immutable???? If i can be set to 89 then to me this seems that the variable can be changed.
I have read other post on this and I still am not certain why i can be changed to another variable. Also in writing code what is the best way to declare primitive types. Why not use the wrapper classes all of the time to create variables.
int y = 5;
Integer i = new Integer(y);
i = 89;
final Integer x = Integer.valueOf(5);
System.out.println("Integer:(i) " + i.intValue());
System.out.println("Integer:(i) " + i.byteValue());
System.out.println("Integer:(x) " + x.intValue());;
System.out.println("Integer:(x) " + x.byteValue());;
i = i - 5;
Using all wrapper classes to declare variables: (Would this be better than declaring these variable with the primitive variable types)
Integer a = new integer(MyNewValue);
Integer b = new integer(MyNewValue);
Integer c = new integer(MyNewValue);
Integer d = new integer(MyNewValue);
Float fa = new integer(MyNewValue);
You are conflating two things: changing the value of an "object" itself and changing the object a reference points to. Saying i = 89 just points the variable i to a new object; it doesn't change the Integer object which originally was pointed to by i.
Pre-pending variable declarations with final just ensures that reassigned is prohibited, it is in no way a declaration of the mutability/immutability of the object it points to. Maybe off-topic, but I personally think the article Java is Pass-by-Value, Dammit! is a good read.
When you call i = 89;, your not changing the value of the Integer object stored in memory. Instead, you're assigning a brand new int with value 89 to i. So the immutable rule isn't being broken.
Remember that i is simply a reference that points to the Integer, not the actual Integer itself.
Yes, it does look like the integer is changing, but all that is happening on line 3 is its being converted to i = new Integer(89) by the compiler. If you wanted to see, you could do
Integer i1 = i;
i = 83;
println(i); \\ prints out the original value 5
println(i1); \\ prints out a new value, 83
When you declare something as final, you cannot change the definition of the variable, though you can still mutate anything inside it. JavaRanch has a very nice analogy to help
You should not use wrapper objects when you can avoid it because they are a small amount less efficient to than primitives and take up a few extra bytes.

java pass by value example help me to understand

I have the following code
class sample
{
public void update(List l)
{
l = null;
}
public static void main (String[] args)
{
List m = new ArrayList();
m.add("suresh");
m.add("sankar");
new sample().update(m);
System.out.println(m);
}
}
The answer will be {["suresh,"sankar"]}. The m is a pointer to the arraylist object, it contains an memory address value (for ex consider 0xf34 ). when we pass m to update method ,the local variable l will be set to 0xf34 that points to arraylist object in memory .when we set null to this variable l , the memory address replaces the arraylist with null ,hence the variable m should also refer null.am i right.please help.
No, the compiler has it right. :)
The parameter l contains a reference to the ArrayList object assigned to m. l gets updated to null, and indeed any later use of lwithin the update() method would see it as null. But l is a separate variable that has scope only within that method -- it's not linked to m in any way (other than the fact that they originally contained references to the same object).
The key to understanding this is to remember that all objects are accessed indirectly via a reference. When an object is passed as an argument the method, the "value" actually being passed is a reference, and not the object itself.
When you null out the l parameter in the update method, you are setting that specific reference to null - the original reference m remains unchanged, and the object referred to by both references is also unchanged.
If you know C/C++, then this can be paraphrased as:
void update(List* l)
{
l = NULL; // set the pointer to null - the object (*list) is unmodified
}
void main()
{
List* m = ...;
update(m);
printf(m->values());
}
The pointer m is copied by value. The object pointed to (the list *m) is not altered in any way. The value of the pointer is copied from m to l. When l is set to NULL, that is a local change that only affects the value of the l pointer.
Here's an example where pass by reference involves a non-local change,
class NonLocalChange
{
public void change(int[] i) {
i[0] = 2;
i = null;
}
public static void main(String[] s) {
int[] m = new int[1];
m[1] = 3;
change(m);
System.out.println(i[0]);
}
}
The result printed is 2. This is because the change method changes the object referenced by l, and not just the reference itself.
Note that this doesn't throw a NullPointerException, even though l is assigned to null. As before, it's a reference, and so it's a local assignment to the value of that reference.
Your update method just sets the local l reference to null without changing anything about the passed-in object.
just imagine that you have a address (X for example) in your heap.
if you set m to refer to X as well as l to refer to X. we have two variable referring to same address, if you change one of them to null, the other one will remain as old value.
Lets say, new ArrayList() returns address 2000 in which the new ArrayList object is stored.
List m = new ArrayList():
lets say, m # 9999 = 2000, here let the number following '#' indicates the address at which 'm' is stored and the number following the '=' represents the value of 'm' (that would be an address as well since it is a reference type). So now 'm' at address 9999 holds 2000 which is the address of the newly created ArrayList object.
update(m):
Since Java is call-by-value always, it calls the update() method copying the value stored in m which is 2000. So now the parameter 'l' in update() definition holds the value 2000 (which is also the address of the previouly created ArrayList object). So we can say,
l # 8888 = 2000 ('l' at address 8888 holds the value 2000)
l = null:
So now, l # 8888 = null
System.out.println(m):
What is the value of m now? it is still 2000. The update() method didn't change the value of m. The method has just changed its local variable 'l' value. so 'm' still refers the previously created ArrayList object and is being printed.
No, you're not right. Java, as many other high level languages, employs call by sharing.
You could think of it like that: the reference to the actual argument is passed by value. Which means that within the called function, the reference initially points to the same value, thus any changes to the value itself are visible outside the scope of the called function, but any changes to the local reference are not. It is as if a copy of the address was passed.
Some additional reading for those interested.
objects and primitives
java+pass+by+value search

Categories

Resources