This question already has answers here:
Arrays in Java and how they are stored in memory
(3 answers)
Create an array of references
(3 answers)
Closed 24 days ago.
In java array carries reference to a memory location and objects does that too. So when we create an array of objects, does that mean that the array carries reference to more references?
I asked this question to my professor, but didn't quite understand what he explained.
When you write:
Object[] array = new Object[10];
the variable array contains a reference. This reference points to an array object, which can contain 10 references. These references are all null at the point that the array is created.
Once you write:
array[0] = new Object();
array[0] now contains a reference to an Object instance.
Yes.
Suppose you have code like this:
class Foo { ...
public int getBar () { ...
public void setBar (int bat) { ...
Now, elsewhere you have code like this:
Foo [] glorblarr = new Foo [12];
Foo flarg = new Foo ();
...
flarg.setBar (100);
glorblarr [7] = flarg;
System.out.println (flarg.getBar () + " " + glorblarr [7].getBar ());
glorblar [7].setBar (987);
System.out.println (flarg.getBar () + " " + glorBlarr [7].getBar ());
You should expect the output to be this:
100 100
987 987
At this point, globarr [7] refers to the same Object as flarg. That is, one Object, but it is accessible via two names / references. At least until one of the references is changed to point to something else (or null), one is an alias for the other.
Related
This question already has answers here:
Does Java return by reference or by value
(4 answers)
Closed 4 years ago.
I am quite confused about everything being a reference in most cases but sometimes not, and containers. Consider this piece of code:
HashMap<Integer,String> x_map = new HashMap<>();
x_map.put(42,"foo");
String g = x_map.get(42);
g = "bar";
System.out.println(g.equalsIgnoreCase(x_map.get(42)));
Why does this print false ?
I found the correct way to do it:
How to update a value, given a key in a java hashmap?, but I am totaly lost why the above does not work as expected. Also when the objects in the map are rather large then I would like to be able to modify only a single member of one of the values in the map and currently I dont know how to do that without creating a new instance and put that into the map.
Pass-by-value
Java is pass-by-value (see Is Java “pass-by-reference” or “pass-by-value”?).
Which means that when you do
Person first = new Person("John");
Person second = first;
you have two different variables, both pointing to the instance new Person("John") in memory. But again, both variables are different:
first ---|
|---> new Person("John")
second ---|
If you manipulate the object itself the change is reflected to both variables:
second.setName("Jane");
System.out.println(first.getName()); // Jane
The diagram is now:
first ---|
|---> new Person("John").setName("Jane")
second ---|
But if you change where a variable points to, it obviously does not affect the other variable since you didn't touch the object they point to:
second = new Person("Jane");
System.out.println(first.getName()); // John
After that statement you have two variables pointing to different objects:
first ---> new Person("John")
second ---> new Person("Jane")
Explanation
Let's transfer that knowledge to your code. You wrote:
String g = x_map.get(42);
g = "bar";
and are asking why g = "bar" did not affect what the map stores.
As said, you only change where the variable g is pointing to. You don't manipulate the object inside the map. The diagram is first:
|---> x_map.get(42)
g ---|
"bar"
and then
x_map.get(42)
g ---|
|---> "bar"
Solution
If you want to change the object, you would need to manipulate it directly instead of only changing the variable reference. Like first.setName("Jane"). However, in Java Strings are immutable. Meaning that there is no way of manipulating a String. Methods like String#replace don't manipulate the original string, they always create a new String object and leave the old one unchanged.
So in order to change what the map stores for they key 42 you just use the Map#put method:
x_map.put(42, "bar");
This will change what x_map stores for the key 42 to "bar".
Because you didn't update the value of the 42 key in the x_map.
for that you should do this
X_map.put(42, g)
The reason is given in a comment by #khelwood:
Variables are assigned by value in Java. g = x_map.get(42) just
assigns the value from the map into the variable g. If you reassign g,
then you're repointing g to a different value.
If instead of String I put something that actually is modifiable in the map then it works just expected:
public static class Test{
public int x = 5;
}
and then...
HashMap<Integer,Test> x_map = new HashMap<>();
x_map.put(42,new Test());
Test g = x_map.get(42);
g.x = 12;
System.out.println((g.x == x_map.get(42).x) + " " + g.x + " " + x_map.get(42).x);
prints true 12 12.
This question already has answers here:
Correct way of thinking about primitive assignment
(3 answers)
Closed 5 years ago.
I've come across a problem which simplifies down to something like this:
public static void main(String[] args) {
ArrayList<String> firstList = new ArrayList<>();
firstList.add("Hello");
firstList.add("World");
firstList.add("This");
firstList.add("Is");
firstList.add("A");
firstList.add("Test");
System.out.println("Size of firstList: " + firstList.size());
ArrayList<String> tempList = firstList;
System.out.println("Size of tempList: " + tempList.size());
firstList.clear();
System.out.println("Size of firstList: " + firstList.size()); // should be 0
System.out.println("Size of tempList: " + tempList.size());
}
And the output is:
Size of firstList: 6
Size of tempList: 6
Size of firstList: 0
Size of tempList: 0
I would expect the size of tempList the second time round to be 6 instead of 0.
There have already been some questions relating to this effect such as this one and another one.
From the answers, I've found that this is because tempList refers to the same reference as firstList so when firstList changes so does tempList (correct me if I'm wrong here).
So a better solution to this would be something like:
ArrayList<String> tempList = new ArrayList<String>(firstList);
If the information about references mentioned above is true, then why does this code:
public static void main(String[] args) {
int firstValue = 5;
System.out.println("firstValue: " + firstValue);
int tempValue = firstValue;
System.out.println("tempValue: " + firstValue);
firstValue = 3;
System.out.println("firstValue: " + firstValue);
System.out.println("tempValue: " + tempValue);
}
give this output:
firstValue: 5
tempValue: 5
firstValue: 3
tempValue: 5
?
Should tempValue not also be 3 the second time it is printed?
I feel like I'm misunderstanding how references work, so could someone explain why the temporary list and original list in the first example are affected together, whereas the temporary integer and original integer give different results as in the second example?
Java has two variable types: primitives and references. When you assign one variable to another, as in
ArrayList<String> tempList = firstList;
you are assigning the reference stored in firstList to be the value of the variable tempList. It's important to note that you are not creating a new ArrayList object with this assignment; you are simply copying a reference value.
When you do assignment with primitives:
int tempValue = firstValue;
you are doing the same thing, but with a primitive value.
In general, think of it this way. Variables do not share memory locations; each variable has its own value. When the value is a reference, however, two reference variables may contain the same reference (just as two primitive variables may contain the same primitive value, such as 5). For reference types, if you use one variable to change the contents of the object being referenced, then you will see the change in that object when accessed through the second variable. In the computer science literature, this behavior is called aliasing. For primitives, this aliasing doesn't happen.
int tempValue is a primitive type which means that it stores directly by value. ArrayList tempListis not a primitive type and thus stores by reference.
What you see happening with int would also happen with all other Java static-type primitive variables. Meanwhile, Java non-primitive variables will change upon mutation whenever the instance to which they refer changes in value.
(This leads to a follow-up question: what happens to tempList when you use firstList = new ArrayList() instead of firstList.clear()? Is its value the same between re-assignment and mutation and why?)
For the ArrayList example, both variables point to the same object elsewhere in heap memory, thus changing the contents of one reference affects the other.
As for the integer example, the current behaviour is expected as they're primitive types and not reference types. When it comes to primitive types, the variables store the value itself rather than a reference to an object in memory.
Because primitive values do not have references, and the wrapper types are immutable (so Integer behaves the same way). Arrays, as a counter example, are reference types (even arrays of primitives). So,
int[] arr = { 1 };
int[] arrCopy = Arrays.copyOf(arr, arr.length); // <-- deep copy
int[] b = arr; // <-- shallow copy
b[0] = 2; // <-- same as arr[0] = 2
System.out.printf("arr = %s, arrCopy = %s, b = %s%n", //
Arrays.toString(arr), Arrays.toString(arrCopy), //
Arrays.toString(b));
Outputs
arr = [2], arrCopy = [1], b = [2]
See also, What is the difference between a deep copy and a shallow copy?
I got a question about the following piece of code.
class MemArr{
int[] mem = {1,2};
}
public class Test{
public static void main(String[] args){
MemArr[] x = new MemArr[2]; //line 1
x[0] = new MemArr(); //line 2
x[1] = null; //line 3
//line 4
}
}
How many objects are created in total and how many objects are eligible for garbage collection when line 1 is reached?
I think, that at line 1, there were 5 objects created (1 array x, 2 objects of MemArr in the array x, 2 member variables of integer array for the objects of MemArr)
At line 2, two objects were created (one MemArr object and its member variable mem).
At line 3 when x[1] is set to null, I think that there were in total 4 objects which are eligible for the GC, but I am not very sure about this.
MemArr[] x = new MemArr[2];
This creates a new MemArr array of 2 elements but it doesn't initialize any of the 2 elements. After that declaration, x[0] and x[1] are both null: the array was instantiated but not its content.
x[0] = new MemArr();
This really assigns a new MemArr to the first element of the array. So it creates one MemArr and another array for the internal int[] mem that it contains.
x[1] = null;
This line doesn't do anything; the value was already null, as said before.
The first line creates a single array object of 2 elements. Both elements are null by default, so no MemArr objects are created.
The second line creates one MemArr object and one internal array object inside that object.
The third line doesn't create any object. In fact, it makes no difference since x[1] is already null.
C++:
Student* sp = new Student[10];
Now, please correct me if wrong. I believe this creates an array of 10 instances of Student, each calling the default constructor with no arguments.Please use arrays and not vectors.
Java:
Student [] sa = new Student[10];
Again, correct me if I'm wrong but I think in case of Java though, no instances are created when this statement executes and instead only memory is reserved to hold 10 references to Student instances.
And it is necessary that we follow it up with:
sa[0] = new Student(*arguments*);
sa[1] = new Student(*arguments*);
.
.
.
sa[9] = new Student(*arguments*);
I'm trying to see the difference and quite frankly, I'm confused. Kindly clarify please.
What do you mean, you're trying to see the difference and you're confused? The difference is exactly what you've stated. The C++ code creates an array of 10 Student objects that are default-initialized [1], and the Java code creates an array of 10 references so no Student constructors are called until you do it yourself.
Maybe your question is why C++ and Java differ. That's because a Student[] in Java is an array of Student references, so the true C++ equivalent would be an array of Student pointers,
Student** sp = new Student*[10]
which you would indeed have to follow up with
sp[0] = new Student(args);
...
sp[9] = new Student(args);
analogously to your Java example.
Now, if you want an array of 10 Student objects in C++ initialized using a constructor other than a default constructor, you have to do it in two steps: first allocate memory for 10 Student objects, then manually construct the Student objects over that memory using placement new. I do not endorse this style of coding. It is very confusing even to experienced C++ programmers.
typedef Student SA[10]; // SA means array of 10 Students
SA* psa = static_cast<SA*>(::operator new(sizeof(SA)));
SA& sa = *psa; // sa refers to an array of 10 Students
new (&sa[0]) Student(args); // construct the Student object
// ...
new (&sa[9]) Student(args);
// later
for (int i = 0; i < 10; i++) {
sa[i].~Student(); // destroy the Student object
}
::operator delete(psa); // deallocate the memory for the array
Use a damn vector.
[1] Note: In C++, for a class, default-initialized means that the default constructor is called; for an array, it means each element is default-initialized; and for an arithmetic, pointer, reference, or enum type, it means it's not initialized at all.
Short answer:
Those two declarations are different. The C++ one allocates an array of Student objects, while the Java one allocates an array of references to Student objects.
Long answer:
The difference is that, in Java when you declare an object:
Student s;
It creates a reference (similar to a pointer in C++) to a Student and initializes the reference to null. You can then later assign a Student object to that reference variable with s = new Student().
But in C++ when you declare an object:
Student s;
It creates an actual object (not a reference to an object as in Java). And thus the object must be immediately instantiated.
So in Java, when you do this:
Student [] sa = new Student[10];
It actually creates an array of references to Student objects (not an array of actual Student objects), and they are all initalized to null references.
...I think in case of Java though, no instances are created when this statement executes and instead only memory is reserved to hold 10 references to Student instances.
So yes, you're right.
But in C++ this:
Student* sp = new Student[10];
dynamically allocates an array of actual Student objects. Again, not references as in Java, so each object will be instantiated immediately.
I believe this creates an array of 10 instances of Student, each calling the default constructor with no arguments.
Correct as well.
So in Java after declaring the array of references you can then instantiate the actual objects and assign them to those references in the array with this:
sa[0] = new Student(*arguments*);
But in C++ the Student objects in the array were already instantiated, so this is not needed.
Bonus: Array of pointers in C++
In C++, you can also declare an array of pointers to postpone object instantiation, similar to the Java array of references:
Student** spp = new Student*[10];
This will create an array of 10 pointers to Student objects. You can then instantiate the objects when needed and assign their memory addresses to the pointers:
spp[0] = new Student(*arguments*);
This looks a lot like Java, but is actually not generally recommended in C++. In fact, using new in C++ should be avoided most of the time.
Here's a round about way of accomplishing what you are hoping to using the new operator.
#include <new>
struct Student
{
Student(int ){}
};
int main()
{
// Allocate memory for 10 Students.
// Don't construct Student objects yet.
char* cp = new char[10*sizeof(Student)];
// Now construct the Student objects using
// placement new
Student* sp[10];
for ( int i = 0; i < 10; ++i )
{
sp[i] = new (cp + sizeof(Student)*i) Student(10);
}
// Use the Student objects
// ...
// ...
// Call the destructor on the Students explicitly.
for ( int i = 0; i < 10; ++i )
{
sp[i]->~Student();
}
// Deallocate the memory.
delete [] cp;
}
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 9 years ago.
I mean that when we return from that function, the address of the array remains the same address it was before we enterd the function.
an example:
class sdasd {
public static void main(String[] args){
int[] obj = new int [7];
//obj has an address
sdasd t = new sdasd();
t.func(obj);
//obj has the same address as before!!
}
void func(int[] obj){
int[] otherObj = new int[13];
obj=otherObj;``
//we changed the address of the array 'obj'?
}
}
thanks for helping
In the called function obj is copy of the reference to the array. So you have, just when you enter func, what can be described as
main::obj --> int[7];
func::obj --> int[7];
After assigning the new array, you have
main::obj --> int[7];
func::obj --> int[13];
Parameters are passed by value, so a copy of them is created. When it is a reference, the copy of the reference points to the original object; changes to the reference are local to that method (but changes to the object itself are shared by all the references!)
To put the last point in clear, if you had done, instead of assigning a new reference, something like
void func(int[] obj) {
obj[1] = 69;
}
then the change would be visible from main::obj.
That's because the reference to obj is passed by value into func. So in your method func, you've changed the local reference to a new array, but main's obj still refers to the original array. In Java, arrays are objects too, so arrays are declared as references just as normal objects are.
Thats because when you passed obj array to func() it actually created
one copy of your array 'obj' and then passed it as parameter.
So any changes to copy didn't affect your original 'obj'.