5

I have below two situations related to ArrayList get method, one with custom class and one with String class:

1. Below is the example of modifying Custom class ArrayList element:

ArrayList<MyClass> mTmpArray1 = new ArrayList<MyClass>();
MyClass myObj1 = new MyClass(10);  
mTmpArray1.add(myObj1);

MyClass myObj2 = mTmpArray1.get(0);  
myObj2.myInt = 20;

MyClass myObj3 = mTmpArray1.get(0);  
Log.d(TAG, "Int Value:"+myObj3.myInt);    // Prints "20" 

2. And below is the example of modifying String ArrayList element:

ArrayList<String> mTmpArray2 = new ArrayList<String>();  
mTmpArray2.add("Test_10");

String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";

String myStr2 = mTmpArray2.get(0);
Log.d(TAG, "Str Value:"+myStr2);  // Prints "Test_10" 

So in case of MyClass ArrayList, when I call get and modify the value, then I see change is reflecting when I do get again.

But same way when I modify String ArrayList, then changes are not reflecting.

What is the different in of the get method in both the scenarios?
Is it that in case of String, String class creating deep copy and returns new object, and in case of Custom class shallow copy is created?

In the first scenario applicable to "LinkedHashMap", "HashMap" and "List"?

User7723337
  • 11,857
  • 27
  • 101
  • 182
  • It will be great help if, explanation is provided for -1. Accordingly I can update/delete question. Please provide explanation for -1. – User7723337 Mar 18 '15 at 10:02
  • There is no difference between the 2 `get()`: they both return a reference to the object contains in the list. But you don't do the same thing in both cases: 1- you modify a member of the referenced object (`myObj2.myInt = 20;`), 2- you try to modify the reference to the object (`myStr1 = "Test_20";`). If you try to modify the reference in your first case, you will get exactly the same result as in your second case. – T.Gounelle Mar 18 '15 at 10:14

6 Answers6

3

Your are not doing the same thing in the two cases.

Here you update the state of an object, so the change affects the object stored in the list :

myObj2.myInt = 20;

Here you are assigning a new object to a local variable, so the list is not affected :

myStr1 = "Test_20";

If String was mutable, you could have modified the String by calling some method, and the change would have been reflected in the object stored in the list :

myStr1.setSomething(...);

On the other hand, if in the first case you would have changed the value of the local variable, the object stored in the list wouldn't have been affected :

myObj2 = new MyClass (...);
Eran
  • 387,369
  • 54
  • 702
  • 768
  • for the case of myStr1 = "Test_20"; if I change this line and add myString.append("_somthing"); then also it is not reflecting. – User7723337 Mar 18 '15 at 10:14
  • @A_user `myString.append` doesn't mutate `myString`. it creates a new String instance and returns that instance. The original `String` remains unchanged, – Eran Mar 18 '15 at 10:15
  • What will happen if i do `myStr1.replace("20","10")` will it reflect the change when we do get? – User7723337 Mar 18 '15 at 10:41
  • @A_user `replace` doesn't mutate `myStr1`. It creates a new String. Again, String is immutable, so any method you call on `myStr1` won't change it. – Eran Mar 18 '15 at 10:43
  • Thanks for the explanation, so the problem is String is immutable and there is no way we can modify original contents of it. – User7723337 Mar 18 '15 at 13:06
2

Strings are immutable. You're not inserting the new string into the array list.

When you do String myStr2 = mTmpArray2.get(0);, even tho you are pointing to a reference in the ArrayList, any attempt to change the value, will (because of String immutability) create a new String (myStr2) that will not reference the ArrayList anymore.

When you do myStr1 = "xxx", you're not actually changing the ArrayList reference, you're changing a new (copy) (now called myStr1) that was grabbed from the ArrayList and it has a local scope.

Read some more about Strings: Immutability of Strings in Java

Now in the first example, you are pointing to a mutable object (your custom class) so you're literally changing the direct value, through the reference. Welcome to Java. ;)

Unrelated: This code: MyClass myObj1 = new MyClass(10); is (arguably) considered bad. It's better to use a factory pattern that is a lot easier to read. In other words, public constructors with parameters are hard to read (for example, I have no idea what I am constructing when I read your code).

A (perhaps) better approach would be: MyClass myObj = MyClass.createInstanceWithCapacity(10); // i've invented the name because I don't know what your 10 is, but look at both, which one do you think is easier to understand upon first glance?

Disclaimer: The above unrelated comment is my personal opinion and not all developers will agree. ;)

Community
  • 1
  • 1
Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • 2
    First part is wrong! in `String myStr2 = mTmpArray2.get(0);` there is no copy and you reference the `String` object that is in the list. Explanation of @Eran is right. – T.Gounelle Mar 18 '15 at 10:07
  • THere, I fixed the wording for your happiness. – Martin Marconcini Mar 18 '15 at 10:12
  • 1
    That's not for my happiness and that's not a wording issue. Look at the code and you will understand there **no** copy of any object when you do a `List#get(int)`. Even for an immutable `String`. – T.Gounelle Mar 18 '15 at 10:16
  • @AbbéRésina Thank you for your clarification, I perfectly understand how the List interface (and many of its implementations) work. Having made the clarification, in this particular case, the difference is irrelevant, since the OP clearly has no understanding of immutability, let alone pointer references; or so it seems. – Martin Marconcini Mar 18 '15 at 10:37
  • Agree with Abbé Résina, @MartínMarconcini for the "Unrelated" part of your answer, I think using factory pattern for simple things will make code more complicated to understand, in case of "new MyClass(10)" 10 is the value passed to the constructor of the MyClass which will assign this value to it's member "myInt" and will create new instance, which we then add to the list, and also the code I have given is just for explaining of the problem statement. – User7723337 Mar 18 '15 at 10:53
  • And how is `MyClass(10)` easier to understand (without having to look up what that 10 is, where it goes and what is it there for) than an explicit method? And what is it that you agree with? You've no basic understanding of Java Strings, therefore I assume you have little Java experience, so it's easy to assume that you're not experienced enough to understand the benefits of clear code vs. "simple things". Anyway, I offered the free, unsolicited advice as a bonus, not as something you had to do. To be honest, in your code, `MyClass(10)` is unneeded, only adds confusion because it's irrelevant – Martin Marconcini Mar 18 '15 at 11:01
  • @MartínMarconcini to clarify you again, **The code given in my question is just for understanding the problem statement** and not for any other use, if you think it is complicated/unreadable and your are unable to understand it (as java expert) please post it your way in your answer, as it does not matter to me, as far as others are able to understand the actual problem statement (not the code how it is written). – User7723337 Mar 18 '15 at 13:04
  • Hence why I put in *bold* the word: **unrelated**. (Emphasis totally mine). – Martin Marconcini Mar 18 '15 at 14:32
1

Strings have very nice property called "Immutablity"

This means that String cannot be mutable (changed), when we create/ try to refer to old string, a new instance string is created. And any changes we do are saved in new instance and it do not affect the old string

For example,

String s = "Old String";
System.out.println("Old String : "+s); // output : Old String

String s2 = s;
s2 = s2.concat(" made New");
System.out.println("New String : "+s2); // output : Old String made New
System.out.println("Old String is not changed : "+s); // output : Old String
Kushal
  • 8,100
  • 9
  • 63
  • 82
  • So In case of `s2 = s` you mean to say `s2` is pointing to `s` and we are modifying the `s`, so why this is not true in case of ArrayList. – User7723337 Mar 18 '15 at 10:57
  • in case of `s2 = s`, `s2` is made reference to `s` but when you change in `s2` then due to **immutability** of strings, the change will be reflected in new resulted string object. `s` will be **unchanged** – Kushal Mar 18 '15 at 11:03
  • But your output says that `s` will out put "Old String made New". so we have changed the value of `s`, which should not happen then. – User7723337 Mar 18 '15 at 12:50
  • i have corrected my error. Thank you for informing me about error – Kushal Mar 18 '15 at 13:05
  • Got your point thanks for the information. – User7723337 Mar 18 '15 at 13:07
0

These is no difference between the two "get" calls. The difference is between the types that the ArrayList is holding, and what you're doing the references the "get" method returns.

In your first example, you do this:

MyClass myObj2 = mTmpArray1.get(0);  
myObj2.myInt = 20;

Here, you're getting a reference to the MyClass instance in the ArrayList in position 0, and you are modifying a field within this instance.

In your second example, you do this:

String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";

Here, you're getting a reference to the String instance in the array list, and then you're giving myStr1 a reference to a different string which you create ("Test_20"). It's as if you did wrote myObj2 = new MyClass(20); in the 2nd line in the 1st example.

So, in short, in the 1st example, you access a field within the object by altering it from the reference you grabbed. In the 2nd example, you simply altered your reference to point at a different String.

I should also mention that in Java, Strings are immutable, meaning once they have been created, they cannot be changed.

Gil Moshayof
  • 16,633
  • 4
  • 47
  • 58
0

String is an immutable class. A line like

myStr1 = "Test_20";

does not change the value of the String object myStr1 is pointing to. Instead a new String is created and myStr1 is modified to point to the new String. The original String is unchanged and can be retrieved from the ArrayList.

Your MyClass object is clearly mutable. Only one instance is created and its state is changed by the assignment

myObj2.myInt = 20;

Hence when this object is retrieved from the ArrayList, its new state is seen.

Neil Masson
  • 2,609
  • 1
  • 15
  • 23
  • While correct, the fact that String is immutable actually is not important here. The whole thing would work exactly the same way if he had done it with some other object and written mySomeOtherObject1 = new MySomeOtherObject(); – Florian Schaetz Mar 18 '15 at 10:11
  • Agreed, but the same would happen with other String modifications such as `myStr1.replace("20","10")` – Neil Masson Mar 18 '15 at 10:17
  • Agreed. As Eran already mentioned, if String were not immutable, something like myStr1.setValue("...") would work. – Florian Schaetz Mar 18 '15 at 10:28
0

You simply do NOT change the list in your 2nd example.

In the first example, you are doing this:

  1. Get the first object from the list and store it in the variable called 'myObj2'
  2. Modify the object stored in variable 'myObj2' by setting the int value of this object

But your second example is completely different:

String myStr1 = mTmpArray2.get(0);
myStr1 = "Test_20";

Let me translate that:

  1. Get the first element from the list and store it in the variable called 'myStr1'.
  2. Set the value of the variable 'myStr1' to "Test_20"

So, in case one you are modifying a variable of the object stored in the list. In case two you are retrieving the object stored in the list - and then re-use the variable you stored that retrieved object in and use it for something new - but that does not change the original list, of course.

To modify your list for a type like string, you would need to use set(x, "Test_20").

Florian Schaetz
  • 10,454
  • 5
  • 32
  • 58