get method return value - java

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.

Related

Java List Collections: Why does get(new Integer(i)) work and not when I declare an integer object first and then pass it through

I'm having trouble wording the question, so hopefully the example helps. I am trying to figure out why within the List collection in Java, where the method get() is overloaded by get(O Object) and get(int index), that one of these snippets work and the other doesn't.
edit: some context for the code, I'm trying to come up with an alternative way to check hits/miss on a LRU cache. ListB is a list containing data values, and listA is the cache of arbitrary length. I'm trying to figure out how come the first snippet works when coming across an int from listB that is contained within the cache (listA) which in turn removes, and then adds the element to bring it to the front of the list.
If I declare an Integer object and pass it through the get method it does not work. But if I declare a new Integer object within the get method, it does. For example:
This works This snippet will remove the first occurrence of Integer i it comes accorss
List<Integer> ListA= new ArrayList<Integer>();
for(int i: ListB){
if(ListA.contains(i)){
ListA.remove(new Integer(i));
}}
This does not work even though an Integer object is being passed through, this does not work
List<Integer> ListA= new ArrayList<Integer>();
for(int i = 0; i < ListB.size(); i++){
Integer obj = new Integer(ListB.get(i));
if(ListA.contains(obj)){ //or list.contains(i), neither work
ListA.remove(obj); //Does not work
}}
You shoudn't be creating a new Integer instance when retreive it from the list:
List<Integer> ListA= new ArrayList<Integer>();
for(int i = 0; i < ListB.size(); i++){
Integer obj = ListB.get(i); // Get the object from List B, do not create a new object from a reference in List B
if(ListA.contains(obj)) {
ListA.remove(obj);
}}

Check if an ArrayList contains the same element as another ArrayList

I have used some other answers to get a solution to my problem. But I am wondering if there is a way to improve this further?
// Copy the masterList ArrayList and then sort in ascending order and then make a third
// ArrayList and loop through to add the 8 lowest values to this list.
ArrayList<Integer> sortedList = new ArrayList<>(Calculator.masterList);
Collections.sort(sortedList);
ArrayList<Integer> lowEight = new ArrayList<>();
for (int i = 0; i < 8; i++) {
lowEight.add(sortedList.get(i));
}
// Set TextView as the value of index 0 in masterList ArrayList, check if lowEight
// ArrayList contains the element that is the same as masterList index 0 and if
// so highlight s1 textview green.
s1.setText("Score 1 is " + String.format("%d", Calculator.masterList.get(0)));
if (lowEight.contains(Calculator.masterList.get(0))) {
s1.setBackgroundColor(Color.GREEN);
}
This works to an extent by highlighting the values that are in both masterList and lowEight but for example if the number 7 is in lowEight and appears 9 times in masterList it will highlight all 9 occurences. Is there a way to move the exact object from masterList to sortedList and then to lowEight and then a method to check the object and not just the value?
Let me provide a more concise example of what you're asking. Let us take the following code:
ArrayList<Integer> list1 = new ArrayList<Integer>() {
{
add(5);
add(5);
}
};
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(list1.get(0));
list1.forEach((i) -> System.out.println(list2.contains(i)));
The output is:
true
true
But you would expect it to be:
true
false
Because the first and second element are different objects. The problem here is that although they are different objects, they are equal objects. The way the Integer class is written in Java, any Integer is equal to another Integer if they represent the same value. When you run the contains() method, it sees that the list does indeed contain an object equal to the one you provided (in this case they both represent a value of 5), and so it returns true. So how do we solve this problem? How do we tell one Integer object from another? I would write your own "Integer" class. Something like "MyInteger". Here's a very simple implementation you could use:
public class MyInteger {
private final int i;
public MyInteger(int i) {
this.i = i;
}
public int toInt() {
return i;
}
}
And then when we use it in our ArrayList code:
ArrayList<MyInteger> list1 = new ArrayList<MyInteger>() {
{
add(new MyInteger(5));
add(new MyInteger(5));
}
};
ArrayList<MyInteger> list2 = new ArrayList<>();
list2.add(list1.get(0));
list1.forEach((i) -> System.out.println(list2.contains(i)));
We get our expected output:
true
false
This works because our new MyInteger class implicitly uses the default equals() method, which always returns false. In other words, no two MyInteger objects are ever equal. You can apply this same principle to your code.

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 references not updated

I have the following code
List<String> strings = new ArrayList<String>();
strings.add("a");
strings.add("b");
strings.add("c");
for (String s : strings) {
s = new String("x");
}
System.err.println(strings);
which prints [a, b, c]
I thought it would print [x, x, x] because I iterate over the Strings, which returns a reference to a String Object. In the loop I assign a new Object to the reference, accordingly s should point to the new String Object?
Where is my fault?
What is the best way to update the strings in my array?
In the loop I assign a new Object to the reference
Well, you assign a new reference as the value for s. s is just a local variable though, which was initialized with the value of the element. It's not tied to the element in the list though - that was just how it was initialized. It's a bit like changing the value of a method parameter - that doesn't change whatever variable was used as an argument:
void method(String y) {
y = "bar"; // This doesn't change x.
}
...
String x = "foo";
method(x);
System.out.println(x); // foo
If you want to update the strings in your list (not an array - it's worth being clear about the difference) you should use a regular for loop:
for (int i = 0; i < strings.size(); i++) {
strings.set(i, "x");
}
It prints "a b c " because you are not changing(adding) anything in the list.
for (String s : strings) {
s = new String("x");
}
The above code can be read as :
For each String s in List strings set s to a new String value "x". You are not doing anything to the list. You get the value from the list, store it in s and overwrite s.
You only change the value of the local s variable, not the elements in the List.
You can change elements in the list by List.set(int index, E element).
s you specified here has scope only in the for loop
for (String s : strings) {
s = new String("x");
}
value of new String object is passed to s on each iteration, but strings is not getting affected at all.

java collections stores values or references? [duplicate]

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.

Categories

Resources