java collections stores values or references? [duplicate] - java

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 8 years ago.
Beginner Java question: when I have
Integer i = 6;
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);
then I write i = 8, ar.get(0) returns 6.
But if I trie the same thing with a class of mine:
class MyC
{
Integer i;
}
MyC myc = new MyC();
myc.i = 6;
ArrayList<MyC> ar = ArrayList<MyC>();
ar.add(myc);
then do myc.i = 8, ar.get(0) returns 8.
Can you please explain this behavior?

The problem has nothing to do with autoboxing, as some answers say.
In the first example you create an Integer and put it in the ArrayList.
Then you change the pointer to the Integer, so that i is pointing to another Integer.
This don't affects the Integer of the ArrayList.
In the second example you create an object and put it in the ArrayList.
Then you change the state of this object by myc.i = 8.
This way the object in the ArrayList is changed.

This is because i = 8 is turned by the compiler into i = new Integer(8), since i is of Integer type, not int, so you have 2 references now. In your second example, there's still one reference referenced from both myc and first element of ar.
As pointed out by Sotirios in comment, if i was primitive, you'll get the same result. But because of slightly different reason:
int i = 6;
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);
i = 8; // changes not a ref, but value on stack
System.out.println(ar.get(0)); // prints out 6
Here you have not two references, but one (to autoboxed Integer inside ar).

All variables, with the exception of primitive types, store references, not values.
First example
Integer i = 6;
Create a new Integer object (lets call it I1) and store a reference to it in i
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);`
Create an ArrayList, and store a reference to I1 in it
i = 8;
Create a new (different) Integer object (lets call it I2) and store a reference to it in i
So now, i = I2, and ar.get(0) = I1
Second example
MyC myc = new MyC();
Create a new MyC (let's call it C) and store a reference to in in myc
myc.i = 6;
Create a new Integer object (lets call it I1) and store a reference to it in C.i
ArrayList<MyC> ar = ArrayList<MyC>();
ar.add(myc);
Create an ArrayList, and store a reference to C in it.
myc.i = 8
Create a new Integer object (lets call it I2) and store a reference to it in C.i
So now myc = C, myc.i = I2, ar.get(0) = C, and therefore ar.get(0).i = I2.
Nothing is referencing I1 and it will be garbage collected.

Integer is an Object-wrapper around the primitive type of int.
In order to store them in a Collection for example List<Integer> list = new ArrayList<Integer>(), the stored element type needs to be a subclass of Object. Therefore, they are stored by reference, as all Objects are stored as references (and all methods receive references by value, see Parameter passing in Java )
It is important to note that in the case of
List<Integer> list = new ArrayList<Integer>();
list.add(5);
int number = list.get(0);
System.out.println("" + number);
The reason why 5 can be used to add the number is because of auto-boxing. When you get the number from the list, it also implicitly calls .intValue() and returns the value of the wrapper as a primitive int. Then in the println() function, the number is implicitly boxed to Integer and then toString() is called.

Pointing i to a different object does not mutate the (original) object. This is not special to primitives. Say you have a List<List<Object>> or a List<List<String>>:
List<List<String>> wrapper = new ArrayList<List<String>>();
List<String> l1 = new ArrayList<String>();
wrapper.add(l1);
l1 = new ArrayList<String>();
l1.add("hello");
for(List<String> list : wrapper)
{
for(String string : list)
{
System.out.println(string);
}
}
If you now iterate over wrapper you find it contains 1 empty list. This is equivalent to your first example. Your second example looks like:
List<List<String>> wrapper = new ArrayList<List<String>>();
List<String> l1 = new ArrayList<String>();
wrapper.add(l1);
l1.add("hello");
for(List<String> list : wrapper)
{
for(String string : list)
{
System.out.println(string);
}
}
In this case the wrapper now contains 1 list with one value in it.

All variables in Java are references that refer to objects that live out on the heap.
The wrapper classes (e.g. Integer) are special cases. Those implement the Flyweight pattern and are immutable.

Related

get method return value

i run following codes in eclipse:
ArrayList<StringBuilder> list = new ArrayList<StringBuilder>();
ArrayList<Integer> alist = new ArrayList<Integer>();
// add some elements ti list
list.add(new StringBuilder("hello"));
list.add(new StringBuilder("2"));
list.add(new StringBuilder("hi"));
list.add(new StringBuilder("this"));
// add some elements to alist
alist.add(4);
alist.add(9);
//get method
StringBuilder a = list.get(3);
a.append(" is a good day");
int b = alist.get(1);
b = 7;
// print the list
System.out.println("LinkedList:" + list);
System.out.println("ArrayList:" + alist);
and result is here
LinkedList:[hello, 2, hi, this is a good day]
ArrayList:[4, 9]
It looks like get method returns a shallow copy of list element (in the case of StringBuilder) to the a, but returns a deep copy (in the case of integer) to the b!
why it happened? Do the get method return a deep or shallow copy of list's elements?
get returns a reference to the List element, not a copy (neither deep nor shallow).
In the first snippet you mutate the object referenced by variable a, so the List is also affected:
StringBuilder a = list.get(3);
a.append(" is a good day");
In the second snippet you assign a new value to the variable b, which doesn't affect the List:
int b = alist.get(1);
b = 7;
In order for your first snippet to behave as the second, you should write:
StringBuilder a = list.get(3);
a = new StringBuilder(" is a good day");
and the List won't be affected.
On the other way, you can't make the second snippet behave as the first. Even if you assigned the List element to an Integer variable, you can't call any method that would mutate it, since Integer is immutable.
In the case of StringBuilder a = list.get(3); you get a reference to element at index 3 assigned to variable a , modifying using a will affect element at index 3. where as int b = alist.get(1); you get a copy of element at 1 assigned to variable b so modifying it will not affect element at 1. there is not connection between b and element at 1 after assignment .
StringBuilder a = list.get(3);
a.append(" is a good day");
int b = alist.get(1);
b = 7;
StringBuilder a = list.get(3);
a.append(" is a good day");
int b = alist.get(1);
b = 7;
Indeed you create a shallow copy of your list element in int b =.... As your List contains Objects of Type Integer you make a new assignment to a primitive integer here, while with the StringBuilder you work on exactly the same object.

How does using add() to one ArrayList interfere the size of the other one?

I created two ArrayList and I want to add some Integers to ArrayList a.
In Java code I did this:
ArrayList < Integer > a = new ArrayList < Integer >();
ArrayList b = a;
a.add(new Integer(4));
b.add(new Integer(5));
a.add(new Integer(6));
a.add(new Integer(7));
System.out.println(a.size());
My quesiton is why does b.add(new Integer(5));also add one object to ArrayList a?
Consider the following image where the illustration is given with Date objects. A variable declaration such as ArrayList<Integer> a creates a reference which is then assigned the adress of the object created by the right part of the statement new ArrayList < Integer >();with the help of the assignment operator =
Now when you declare ArrayList b=ayour are stating that the 2nd reference called b is assigned the same adress as the first reference a. Therefore a and b both refer to the same object in memory
The problem is that a and b are pointing to the same object.
You are confusing primitive values with object references. When you run something like this:
int a = 10;
int b = a;
a = 15;
System.out.println("a = " + a);
System.out.println("b = " + b);
The output will be:
a = 15
b = 10;
When you use a primitive (int, String, long, byte, double, boolean, short...), you are COPYING the value of that variable into another, like in the example above. b looks at the value of a, and assigns it to b creating two separate variables.
What you did with ArrayLists, on the other hand, since they are OBJECTS, both a and b are pointing to the same thing. If you want to create separate objects, you would need to do:
ArrayList<Integer> a = new ArrayList<Integer>();
ArrayList<Integer> b = new ArrayList<Integer>();
Then you would get the results you want.
Hope this helps. =)
ArrayList b = a;
From here both a and b point the same memory. So both the objects having same values.
a = new ArrayList<>(); a points to an object (an ArrayList instance) at memory address 0x1234 (the address is made up).
b = a; b points to the same address as a points to.
So every operation on a or b affects the same object in memory. Hence b.add(new Integer(5)) has the same result as a.add(new Integer(5))
ArrayList b is a reference to ArrayList a. There is really only one list with two pointers to it.
Arraylist b is referencing arraylist a. If you want the split, look up java's clone() method.
When you pass value of a to b. You actually the pass the value of the reference to the object. which means that a and b are pointing to the array.
Explaining the code as is:
ArrayList b = a;
on this line you create a variable able to hold an ArrayList, and you make this b variable you just created point at a which is another variable able to hold an ArrayList.
a points on an Arraylist object (the new ArrayList you created on line one).
This does not mean that after line two you have two ArrayLists. There only exist one ArrayList object and two variables that point to the memory this object is stored.
So by calling the add method on any of the variables a or b you add on the same ArrayList.

Java Object class MYSTERY

While Porting a Game I come to a below statement
Object o = new Object[]{"A","B"};
It's really weird!
But when I try the same with "String" then compiler report me an Error msg
String s = new String[] {"A", "B", "C"}; Error: Type mismatch:
cannot convert from String[] to String
Can you please reveal the Mystery of it ?
You have a trivial error in your code. The fact that every class extends Object makes the error more difficult to find.
Since every class (including arrays) extends Object, conversion from A[] to Object is possible.
You wrote int i = new int[] but that's a mistake, you should have written int[] i.
Probably. Object a = new Object[] is not what you wanted to do.
In Java, Array is an Object too. So you can do
Object o = new Object[]{"A","B"};
or
Object o = new String[]{"A","B"};// But array of String not a String
or
Object o = new int[]{1,2};// But array of int not an int
An "Array" of objects is also an object. But an "Array" of ints is NOT an int.
i.e, an int reference cannot point to an Array but an object reference can.
Object[] obj = new Object[5];// works fine
An object can be anything. It can be an array, it can be an single variable.
Object O = new Object[]{"S","A"};
When you define object you can type cast it to your desired data type.
You can not assign an array of a data type to a single variable of that 'same' datatype.
Every array is an Object[] AND an Object, that's why the following are equally valid:
Object[] o = new Object[]{"A","B"};
Object o = new Object[]{"A","B"};
However a String[] is not a String (and vice-versa) and a int[] is not a int (and vice-versa).
Indeed, you would blend primitives with objects in this latter case.

Assign Object array with Integer elements to Integer array

I searched the internet but didn't found any appropriate solution.
In my application I've got an array of integers. I need to access (assign to) the array via reflection. The application creates an object array that contains Integer elements. Java doesn't allow to assign this Object array to the Integer array.
Is it not possible in Java? My application only knows the Class Object of the Integer array field. The code is dynamically. The type may be an arbitrary type.
private final Integer[] destArray = new Integer[2];
public static void main(final String[] args) throws Exception {
final ReloadDifferentObjectsTest o = new ReloadDifferentObjectsTest();
final Object[] srcArray = {Integer.valueOf(1), Integer.valueOf(2)};
final Field f = o.getClass().getDeclaredField("destArray");
f.setAccessible(true);
// first trial
// f.set(o, srcArray);
// second trial
// Object tmpArray = Array.newInstance(f.getType().getComponentType(), srcArray.length);
// tmpArray = Arrays.copyOfRange(srcArray, 0, srcArray.length);
// f.set(o, tmpArray);
// third trial
Object tmpArray = Array.newInstance(f.getType().getComponentType(), srcArray.length);
tmpArray = f.getType().getComponentType().cast(Arrays.copyOfRange(srcArray, 0, srcArray.length));
f.set(o, tmpArray);
}
No, you can't cast a value which is actually a reference to an instance of Object[] to an Integer[] variable - and that's a good thing. Imagine if that were valid... consider:
Object[] values = { new Integer(5), new Integer(10) };
Integer[] integers = values;
Integer x = integers[0]; // Okay so far
values[0] = new Object(); // Sneaky!
Integer y = integers[0]; // ??? This would have to fail!
If you want to cast something to Integer[], it has to actually be an Integer[]. So this line:
final Object[] srcArray = {Integer.valueOf(1), Integer.valueOf(2)};
... needs to change to create an instance of Integer[].
Yes, the type of a Java array is covariantly linked to its element type. Specifically, Object[] is a supertype of Integer[] and as such is not assignment-compatible with it. You must create an Integer[] at the outset to be able to assign it to an Integer[]-typed variable. From your posted code I can see no reason why you would not do that.
Ok, found the solution... I've got to set each single element via reflection:
// fourth trial
final Object tmpArray = Array.newInstance(f.getType().getComponentType(), srcArray.length);
for (int i = 0; i < srcArray.length; i++) {
Array.set(tmpArray, i, srcArray[i]);
}
f.set(o, tmpArray);

Creating an array of objects in Java

I am new to Java and for the time created an array of objects in Java.
I have a class A for example -
A[] arr = new A[4];
But this is only creating pointers (references) to A and not 4 objects. Is this correct? I see that when I try to access functions/variables in the objects created I get a null pointer exception.
To be able to manipulate/access the objects I had to do this:
A[] arr = new A[4];
for (int i = 0; i < 4; i++) {
arr[i] = new A();
}
Is this correct or am I doing something wrong? If this is correct its really odd.
EDIT: I find this odd because in C++ you just say new A[4] and it creates the four objects.
This is correct.
A[] a = new A[4];
...creates 4 A references, similar to doing this:
A a1;
A a2;
A a3;
A a4;
Now you couldn't do a1.someMethod() without allocating a1 like this:
a1 = new A();
Similarly, with the array you need to do this:
a[0] = new A();
...before using it.
This is correct. You can also do :
A[] a = new A[] { new A("args"), new A("other args"), .. };
This syntax can also be used to create and initialize an array anywhere, such as in a method argument:
someMethod( new A[] { new A("args"), new A("other args"), . . } )
Yes, it creates only references, which are set to their default value null. That is why you get a NullPointerException You need to create objects separately and assign the reference. There are 3 steps to create arrays in Java -
Declaration – In this step, we specify the data type and the dimensions of the array that we are going to create. But remember, we don't mention the sizes of dimensions yet. They are left empty.
Instantiation – In this step, we create the array, or allocate memory for the array, using the new keyword. It is in this step that we mention the sizes of the array dimensions.
Initialization – The array is always initialized to the data type’s default value. But we can make our own initializations.
Declaring Arrays In Java
This is how we declare a one-dimensional array in Java –
int[] array;
int array[];
Oracle recommends that you use the former syntax for declaring arrays.
Here are some other examples of legal declarations –
// One Dimensional Arrays
int[] intArray; // Good
double[] doubleArray;
// One Dimensional Arrays
byte byteArray[]; // Ugly!
long longArray[];
// Two Dimensional Arrays
int[][] int2DArray; // Good
double[][] double2DArray;
// Two Dimensional Arrays
byte[] byte2DArray[]; // Ugly
long[] long2DArray[];
And these are some examples of illegal declarations –
int[5] intArray; // Don't mention size!
double{} doubleArray; // Square Brackets please!
Instantiation
This is how we “instantiate”, or allocate memory for an array –
int[] array = new int[5];
When the JVM encounters the new keyword, it understands that it must allocate memory for something. And by specifying int[5], we mean that we want an array of ints, of size 5.
So, the JVM creates the memory and assigns the reference of the newly allocated memory to array which a “reference” of type int[]
Initialization
Using a Loop – Using a for loop to initialize elements of an array is the most common way to get the array going. There’s no need to run a for loop if you are going to assign the default value itself, because JVM does it for you.
All in One..! – We can Declare, Instantiate and Initialize our array in one go. Here’s the syntax –
int[] arr = {1, 2, 3, 4, 5};
Here, we don’t mention the size, because JVM can see that we are giving 5 values.
So, until we instantiate the references remain null. I hope my answer has helped you..! :)
Source - Arrays in Java
You are correct. Aside from that if we want to create array of specific size filled with elements provided by some "factory", since Java 8 (which introduces stream API) we can use this one-liner:
A[] a = Stream.generate(() -> new A()).limit(4).toArray(A[]::new);
Stream.generate(() -> new A()) is like factory for separate A elements created in a way described by lambda, () -> new A() which is implementation of Supplier<A> - it describe how each new A instances should be created.
limit(4) sets amount of elements which stream will generate
toArray(A[]::new) (can also be rewritten as toArray(size -> new A[size])) - it lets us decide/describe type of array which should be returned.
For some primitive types you can use DoubleStream, IntStream, LongStream which additionally provide generators like range rangeClosed and few others.
Here is the clear example of creating array of 10 employee objects, with a constructor that takes parameter:
public class MainClass
{
public static void main(String args[])
{
System.out.println("Hello, World!");
//step1 : first create array of 10 elements that holds object addresses.
Emp[] employees = new Emp[10];
//step2 : now create objects in a loop.
for(int i=0; i<employees.length; i++){
employees[i] = new Emp(i+1);//this will call constructor.
}
}
}
class Emp{
int eno;
public Emp(int no){
eno = no;
System.out.println("emp constructor called..eno is.."+eno);
}
}
The genaral form to declare a new array in java is as follows:
type arrayName[] = new type[numberOfElements];
Where type is a primitive type or Object. numberOfElements is the number of elements you will store into the array and this value can’t change because Java does not support dynamic arrays (if you need a flexible and dynamic structure for holding objects you may want to use some of the Java collections).
Lets initialize an array to store the salaries of all employees in a small company of 5 people:
int salaries[] = new int[5];
The type of the array (in this case int) applies to all values in the array. You can not mix types in one array.
Now that we have our salaries array initialized we want to put some values into it. We can do this either during the initialization like this:
int salaries[] = {50000, 75340, 110500, 98270, 39400};
Or to do it at a later point like this:
salaries[0] = 50000;
salaries[1] = 75340;
salaries[2] = 110500;
salaries[3] = 98270;
salaries[4] = 39400;
More visual example of array creation:
To learn more about Arrays, check out the guide.
Yes it is correct in Java there are several steps to make an array of objects:
Declaring and then Instantiating (Create memory to store '4' objects):
A[ ] arr = new A[4];
Initializing the Objects (In this case you can Initialize 4 objects of class A)
arr[0] = new A();
arr[1] = new A();
arr[2] = new A();
arr[3] = new A();
or
for( int i=0; i<4; i++ )
arr[i] = new A();
Now you can start calling existing methods from the objects you just made etc.
For example:
int x = arr[1].getNumber();
or
arr[1].setNumber(x);
For generic class it is necessary to create a wrapper class.
For Example:
Set<String>[] sets = new HashSet<>[10]
results in: "Cannot create a generic array"
Use instead:
class SetOfS{public Set<String> set = new HashSet<>();}
SetOfS[] sets = new SetOfS[10];
Suppose the class A is such:
class A{
int rollno;
int DOB;
}
and you want to create an array of the objects for the class A. So you do like this,
A[] arr = new A[4]; //Statement 1
for (int i = 0; i < 4; i++) {
arr[i] = new A(); //Statement 2
}
which is absolutely correct.
Here A is the class and in Statement 1 Class A is a datatype of the array. When this statement gets executed because of the new keyword an object is created and dynamically memory is allocated to it which will be equal to the space required for the 4 blocks of datatype A i.e, ( for one block in the array space required is 8 bytes (4+4), I am assuming int takes 4 bytes of space. therefore total space allocated is 4*4 bytes for the array ).
Then the reference of the object is given to the arr variable. Here important point to note is that Statement 1 has nothing to do with creating an object for class A ,no object is created for this class it is only used as a datatype which gives the size of the class A required for the memory allocation of the array.
Then when for loop is run and Statement 2 is executed JVM now allocates the memory for the Class A (i.e creates an object) and gives its reference to the arr[i]. Every time the loop is called an object is created and the reference of it is given to arr[i].
Thus, arr[0] which holds a space of 8 bytes is given the reference of the object of the Class A and everytime loop is run new object is created and reference is given to that object so that it can now access the data in that object .

Categories

Resources