So, lets say I have 2 arrays defined like this:
int[] a = {1,2,3};
int[] b = a;
a[1] = 35; // b[1] becomes 35
b[0] = -5; // why would a[0] become -5?
So, I can reason that changing a value in a will automatically change the value in b (b is pointing to a). But, if I change value in b, why would it affect a? Wouldn't that be as if a is pointing to b (they are interchangeable)?
I was confused with this concept and would like some clarification.
Any advice would be appreciated. Thanks.
Let's do an analogy here. The objects (such as arrays) that you create are like balloons. And the variables (a and b) are like children. And the children can hold a single balloon with a string. And that string is what we call a reference. On the other hand, one balloon can be held by multiple children.
In your code, you created a single balloon i.e. an array {1, 2, 3}. And let a child called a hold it. Now you can tell child a to modify items in the array because the child a is holding it.
In the second line, you tell another child b to hold the balloon that a is holding, like this:
int[] b = a;
Now a and b are actually holding the same balloon!
When you do this:
a[1] = 35;
Not only is the balloon that a is holding been changed, but also is the balloon held by b been changed. Because, well, a and b are holding the same balloon.
The same thing happens when you do:
b[0] = -5;
That's how reference types work in Java. But please note that this doesn't apply to primitive types, such as int or float.
For example,
int a = 10;
int b = a;
b = 20; //a is not equal to 20!!!
When you create the variable int[] a = {1,2,3};, by using the brackets { } just as by saying new int[n] java allocates enough memory for those values and now a references that address in memory.
Then, when you create the new variable int[] b = a;, you aren't allocating memory to that variable b, rather b references the same address which a references.
Now, when you change something in a or b the change occurs at the address referenced by both those variables and will be reflected by both.
Because Java Array is considered Reference Object.
int[] b = a; // variable 'b' points same memory location with 'a'
If you want to copy all value of a, write code below
#Test
public void arrayCopy1() {
int[] a = {1, 2, 3};
int[] b = a;
System.out.println(a); //Point [I#64a294a6
System.out.println(b); //Point [I#64a294a6
printArrayValues(a); //[1,2,3]
printArrayValues(b); //[1,2,3]
a[0] = 10;
printArrayValues(a); //[10,2,3]
printArrayValues(b); //[10,2,3]
}
#Test
public void arrayCopy2() {
int[] a = {1, 2, 3};
int[] b = Arrays.copyOf(a, a.length);
System.out.println(a); //Point [I#64a294a6
System.out.println(b); //Point [I#7e0b37bc
printArrayValues(a); //[1,2,3]
printArrayValues(b); //[1,2,3]
a[0] = 10;
printArrayValues(a); //[10,2,3]
printArrayValues(b); //[1,2,3]
}
private void printArrayValues(int[] arr) {
System.out.print("[");
for(int i = 0 ; i < arr.length ; i++) {
System.out.print(arr[i]);
if (i != arr.length-1)
System.out.print(",");
}
System.out.println("]");
}
Because when you set int[] b = a you didn't simply copied the values of a and gave them to b you gave to b the memory location of a, therefore if you change one, you change the other
Related
If arrays are object as stated in Is an array an object in java then why the output of the code snipped below is [1,1,1]?
I thought after the execution of statement "a=b;" a and b are still pointing to the same content! Isn't it supposed to be shadow copy between objects?
import java.util.Arrays;
public class Local {
int [] a = null;
public Local(){
int [] b = {1,1,1};
int [] c = {5,5};
a=b;
b=c;// 'a' should change too as a and b are both objects! right?
}
public static void main(String[] args) {
Local local = new Local();
System.out.println(Arrays.toString(local.a));
}
}
Let me try to explain it line per line:
int [] b = {1,1,1};
On this line, three things happened.
Created a variable named b.
Created an array of int {1,1,1}
Assigned b to the array of intcreated on step 2.
int [] c = {5,5};
Same thing happened here
Created a variable named c.
Created an array of int {5,5}
Assigned c to the array of int created on step 2.
a=b;
Now we also assigned a to whatever the value of b is, which in this case is the array of int {1,1,1}
Now we have something like
b=c; // 'a' should change too as a and b are both objects! right?
What happened here is that we assigned b to whatever c's value is (int array {5,5}), now b is not pointing to {1,1,1} anymore and since Java is pass by value, a's value remained. It's not like a is pointing to the reference of b that whatever b is pointing to, a will point to it too.
Hope this helps.
// 'a' should change too as a and b are both objects! right?
No both a and b are just reference variables pointing to the same array object {1,1,1}.
With the below line you are making b to refer to altogether different array object where as a would still be pointing to the same array object {1,1,1} as the reference of only b but not a is changed by executing the below line
b = new int[] {2, 2};
Also by making a = b you are making them point to one single array object {1,1,1} and there is no deep/shallow copy happening here.
the variable a would not automatically update itself. mainly because
b = c //is like b = new int[]{5,5};
it is the same concept in your question earlier with
b = new int[]{2,2,2};
a is pointing to b's int array which is [1,1,1]
and you are telling b to point to c which is [5,5]
a => b's array
b => c's array
so a will retain its object and b will have a new one.
At first, b is pointed to the array object {1,1,1}
and you assign b to a, so a is pointed to {1,1,1}
so the out is {1,1,1}
[Background note: I am a new Java programmer with a C++ background, so is a little confused about how arguments are passed around in Java.]
While reading and writing some code, I came to the following case
public class B{
int[] intArr;
Vector<String> strVec;
void mutator(){
// modifies intArr and strVec
}
}
public class A{
B bOfA;
A(B b){
bOfA = b;
}
void foo(){
bofA.mutator();
}
}
foo() in A definitely modifies bOfA, but what about b, the object that is passed in? Will its fields/data members be modified as well?
Are the fields shallow or deep copied?
Are strVec and intArr treated differently, because strVec is a container and inArr is an array, which might be implemented as some kind of pointers, therefore behave quite differently depending on if it is shallow or deep copied.
Thank you.
A (late) update with real code and somewhat surprising (I was assuming the pass-by-value mechanism interpreted in the C/C++ way) results:
import java.util.Vector;
public class B{
int[] intArr = null;
Vector<String> strVec = null;
int x = 0;
String s = null;
B(int sz){
x = 0;
s = new String("ini");
intArr = new int[sz];
strVec = new Vector<String>(sz);
for (int i=0; i<sz; i++){
intArr[i] = 0;
strVec.add( new String("xx") );
}
}
void mutator(){
x = -1;
s = new String("mute");
int sz = intArr.length;
strVec = new Vector<String>(sz);
for (int i=0; i<sz; i++){
intArr[i] = -1;
strVec.add( new String("aa") );
}
}
}
import java.util.Vector;
public class A{
B bOfA=null;
A(B b){
bOfA = b;
}
void foo(){
bOfA.mutator();
}
}
import java.util.Vector;
public class C{
public static void main(String[] args){
B b = new B(3);
A a = new A(b);
System.out.println("Contents of B before:");
System.out.println(b.x);
System.out.println(b.s);
for(int i=0; i<3; i++){
System.out.println(b.intArr[i]);
System.out.println(b.strVec.elementAt(i));
}
a.foo();
System.out.println("\n\nContents of A:");
System.out.println(a.bOfA.x);
System.out.println(a.bOfA.s);
for(int i=0; i<3; i++){
System.out.println(a.bOfA.intArr[i]);
System.out.println(a.bOfA.strVec.elementAt(i));
}
System.out.println("\n\nContents of B after:");
System.out.println(b.x);
System.out.println(b.s);
for(int i=0; i<3; i++){
System.out.println(b.intArr[i]);
System.out.println(b.strVec.elementAt(i));
}
}
}
And the results by cygwin:
Contents of B before:
0
ini
0
xx
0
xx
0
xx
Contents of A:
-1
mute
-1
aa
-1
aa
-1
aa
Contents of B after:
-1
mute
-1
aa
-1
aa
-1
aa
Java passes everything by value. But, the value of Object(s) are references.
Both the array and the Vector are shallow copies.
Arrays are reference objects. When you assign them, they are not copied at all - what happens there is similar to assigning an array to a pointer in C++.
When you copy an array using System.arraycopy, a shallow copy is performed. Constructing a collection from another collection creates a shallow copy as well.
Note: Unlike C++ libraries, Java class libraries uses immutable classes a lot. For example, String is immutable; so are the wrappers for numbers, such as Integer. Immutability makes the shallow vs. deep copying a lot less relevant.
Another note: Vector<T> should be used when you need a synchronized container; if you do not want synchronization, use ArrayList<T> instead.
what about b, the object that is passed in?
Will its fields/data members be modified as well?
Since java by default is pass-by-value, object that is passed in the contructor is pass-by-value but its internally its fields within that object will have reference, therefore when you call bofA.mutator() the B b field will change as well.
To have deep copy you need to copy each of the field in the b object that was passed and use Arrays.copyOf().
After testing the code (see below), I found out that I don't understand some fundamentals.
Class A.
class A {
private String s;
private int[] array;
private B b;
public A(String s, int[] array, B b) {
this.s = s;
this.array = array;
this.b = b;
}
}
Class B.
class B {
public int t;
public B(int t) {
this.t = t;
}
}
I thought that any changes I did after A a = new A(s, array, b); would affect a. Don't all the fields of a and the variables s, array, b refer to the same object?
String s = "Lorem";
int array[] = new int[] {
10, 20, 30
};
B b = new B(12);
A a = new A(s, array, b);
s = "Dolor";
array = new int[] {
23
};
b = new B(777); // Initialized with a new value, a.b keeps the old one. Why?
System.out.println(a);
The output.
String Lorem
Array [10, 20, 30]
B 12
And about this.
B b2 = new B(89);
B b3 = b2;
System.out.println(b3);
b2 = null;
System.out.println(b3); // b2 initialized with null, b3 keeps the old one. Why?
The output.
89
89
However, if I have two lists, this shows that they both refer to same object.
ArrayList<String> first = new ArrayList<String>();
first.add("Ipsum");
ArrayList<String> second = new ArrayList<String>();
second = first;
first.add("The Earth");
System.out.println(second);
The output.
[Ipsum, The Earth]
The difference is assignment versus modification.
Assignment (=) makes the variable point to something else, so this won't change the underlying data. So any other variables pointing to the same data don't change.
Modification (pretty much anything except =) doesn't change what the variable points to, it just modifies the underlying object. So any other variables pointing to the same data do change.
For you example:
b = new B(777); is assignment, so only b is changed to point to something else. a.b won't change.
b2 = null; is assignment, so only b2 is changed to point to something else. b3 won't change.
If you were to say b2.t = 5, this would be modification (we're not assigning a new value to b2, we're modifying it by changing one of its members), so b3 will change as well.
I hope that explains it.
No. The thing is, you are not changing a, you are assigning a new value to s. S is a String, which are immutable, which means you can never make a change to the value of s. You can, however, change the reference in S, which is what you are doing.
To make yourself more clear try these lines of code..
String s = "Lorem";
int array[] = new int[] {10, 20, 30};
B b = new B(12);
//A a = new A(s, array, b);
s = "Dolor";
array = new int[] {23};
b = new B(777);
A a = new A(s, array, b);
System.out.println(a);
ArrayList<String> first = new ArrayList<String>();
first.add("Ipsum");
ArrayList<String> second = new ArrayList<String>();
second = first;
second.add("The Earth");
first.remove("The Earth");
System.out.println("second :"+second);
What are the current values the String s, array and the object b are holding while creating the instance of class A ( at the time of calling class A constructor ) will be printed. After creating class A instance, the String s, array and object b will be referred as a.s, a.array and so. If you assign a new value to s, array and b, it wont affect the class A instance.
And for the array list question, the two array lists will refer the same reference only. If you want different reference then do like this... (But always = assign operator will make same reference only )
ArrayList<String> first = new ArrayList<String>();
first.add("Ipsum");
ArrayList<String> second = new ArrayList<String>(first);
second.add("The Earth");
System.out.println("first :"+first);
System.out.println("second :"+second);
Thanks Dukeling for explaing how assignments works with objects and primitives, I am adding here to explain how the list works when operations performed on them.
When we consider the two array lists created in the above code in the question, both the variables first and second are pointing to the same array object that resides in the memory.
So when an add operation is performed the underlying object itself gets updated. So the print operation prints the second array list which pointed to the same array-list object created during the creation of first array-list.
To make my question understandable, I'm using the following example code. The output of this code is 5, while I wanted it to be 3. I'm guessing that B is working as a pointer for A, but I want A to be copied in B initially, and subsequent changes in A should not affect B.
import java.io.*;
public class fg
{
public static void main(String args[]) throws Exception
{
int[] A = new int[3];
A[0]=1;
A[1]=3;
A[3]=7;
check(A);
}
public static void check(int[] A)
{
int[] B = A;
A[1] = 5;
System.out.println(B[1]);
}
}
You need to explicitly create a copy,
int[] B = Arrays.copyOf(A, A.length);
since int[] is a reference type, so an assignment just copies the reference (basically, pointer).
If the array elements themselves are not primitive types, you probably will need a deep-copy, so
type B[] = new type[A.length];
for(int i = 0; i < A.length; ++i) {
B[i] = makeCopyOf(A[i]);
}
where makeCopyOf() should create a sensible copy of a type instance. If you don't find anything better, type makeCopyOf(type orig) { return orig.clone(); } could serve as a fallback.
B is a reference, and as such is analogous to a pointer (with various differences - you can't do arithmetic and it's not a direct memory pointer).
As such, you'll have to create a copy of the structure (the array) that you're referring to. Note that if you're copying objects, you may need a deep copy. You would likely have to do this in a copy constructor (see this article as to why clone() is considered broken)
Consider this code:
class arraytest {
public static void main(String args[]) {
int[] a = null , b[] = null;
b = a;
System.out.println( b );
}
}
The line
b = a;
is flagged by the compiler saying:
Incompatible types, found int[], required int [][]
Why is b considered two dimensional? I realize the "shortcut" declaration
int[] a = null , b[] = null;
is to blame, but why does it make the array two dimensional when only one set of brackets have been written? I find this syntax unclear and obfuscating.
Take this example:
int a, b;
Then both a and b are ints, right? So now take this:
int[] a, b;
The both a and b are int arrays. So by adding another set of brackets:
int[] a, b[];
you have added another set of brackets to b.
Either of these would be fine:
int[] a = null , b = null;
int a[] = null , b[] = null;
or as you say, simply putting them on separate lines would work too (and be much easier to read).
int[] a = null , b[] =null;
.... it's equal to :
int[] a = null;
int[][]b = null;
You have to do :
int[] a = null , b =null;
In addition to the other answers, Java does not have multi-dimensional arrays. Java has arrays of any type. This "any type" may itself be an array. Multi-dimensional arrays are distinctly different, although subtly.
If you take out the a declaration, this is what you have:
int[] b[] = null;
which is, unfortunately, a legal alternative to
int[][] b = null;
I was lead to believe that the syntax of allowing the [] after the b was there solely for the C-language crowd who would feel more at home with it. Unfortunately, it has the side effect of allowing confusing declarations like
int[] b[];
which ends up the same as
int[][] b;