Does it make a copy before adding Strings into a Collection? - java

List<String> list = new ArrayList<String>();
String string = null;
string = "123";
list.add(string);
string = "456";
list.add(string);
for (String s : list)
{
System.out.println(s);
}
This program outputs:
123
456
which is pretty natural.
However, I'm thinking in another way. "string" is the reference(pointer) to the actual String object. When executing add(), it just stores the reference. When "string" refers to another String object, why the list still keeps the original one? Does it make a copy before add()?

The "value" of a String variable is a reference to the (immutable) object that is the string.
So there is no copy of the String but a copy of the reference. Having this reference doesn't allow you to change the original variable (you have no link to it) and doesn't allow you to change the string as it is immutable.
What you have here, inside the array contained by the arrayList after the two calls to add, is two different references. They could point to the same string but changing one reference doesn't change the other one. If you wanted to change the first reference in this case to point to the same string as the second one, the simplest would have been to do list.set(0, list.get(1));

When "string" refers to another String object, why the list still keeps the original one?
What you've added to the list is the reference to the string. Later, when you do
string = "456";
you're not changing the existing string, you're assigning a reference to a different string to the string variable. The original string is unchanged (in fact, strings in Java are immutable).

because the variable 'string' is nothing but a pointer to a address in the memory. when you add 'string' in list the memory address of the 'string' is entered in the list. when you again assign a value , it justs change the address in the memory where the new variable is pointing

Related

String immutability - (concat and assigning a new value)

I have a question about String's immutability, namely, in the first situation the variable name does not change.
String name = "Unknown" ;
name.concat("Boy");
System.out.println (name);
But in the second case, the variable changes its value.
String name = "Unknown" ;
System.out.println (name);
name = "Test";
System.out.println(name);
Why the variable does not change in the first case, but in the second case as much as possible?
Strings are immutable - they do not change.
The name variable is pointing to the string "Unknown"
If you look at the docs for String found here,
you will see that concat() returns a String which is the brand new String that concatenates "Unknown" with "Boy".
The name variable is still pointing to the "Unknown" string.
In order for name to change you need to reassign it to the String returned by concat:
name = name.concat("Boy");
In the second example you're reassigning the reference to "Test".
None of the strings changed, just the references.
Thats how string concatnation works. It doesnt concat to your existing string, but creates a new one.
String name = "Unknown" ;
name.concat("Boy");
This creates and returns a new string "UnknownBoy" which you don't store.
Refer to the String API for how the method behaves. https://docs.oracle.com/javase/9/docs/api/java/lang/String.html#concat-java.lang.String-
It looks you are confused between the reference variable and Object, in your case name is a reference variable of type String which can hold the reference to a String object. Please go through this for the understanding of reference, variable and object.
In your first case, name is a variable which holds the reference to the String Object "Unkown" and when name.concat("Boy") a new String object ("UnkownBoy") will be created but name variable still holds the reference to the old object ("Unkown") because you did not update the reference anywhere in the first case.
In your second case you have updated the reference variable by name = "Test"; , that means now name will hold the reference of a new String Object "Test".

ArrayList modifying value returned by "get" method [duplicate]

This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 6 years ago.
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"?
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 (...);
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. ;)
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
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.
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.
You simply do NOT change the list in your 2nd example.
In the first example, you are doing this:
Get the first object from the list and store it in the variable called 'myObj2'
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:
Get the first element from the list and store it in the variable called 'myStr1'.
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").

Are Strings overwritable?

My textbook says a String is not over-writable or immutable, i.e, once you enter the value of a String you can't change it. But today when I was running the following code, the String str gets muted as the compiler does not give any error and the new String a's value is successfully entered into str.
class Test
{
static void main()
{
String str = "something";
String a ="anything";
str = a; //str is being over written without any error
System.out.println(str);
}
}
The output is : anything
So, is my book wrong ?
If my book is not wrong please give an example to show that Strings are immutable
The book is correct. When you say str = a you are not changing anything about the String 'something'. You should distinguish between str and something, they are not the same. "something" here is a String object in memory, whereas str is just the reference to that string. Same with the reference a.
When you say:
str = a
You are not changing something, you are in fact saying, "change the reference str to point to whatever the reference a is pointing to." The Strings remain the same, the references change.
On a similar note, this is why you may see in your book that concatenating Strings is expensive, as doing something like:
str = str + a
Would again not be changing the existing Strings, but instead creating a new String object which is equal to the concatenation of the String that the reference str is referring to and the String that the reference a is referring to.
You need to understand what immutable means. In your scenario you are just changing references.
str = a;
will make both a and str to point to String "anything". The Text Book is correct. String is immutable and can not be overwritten. If you check the JavaDoc for String. Most of the methods return a String. This is because any operation in a String will not change that String object but will result in a new String being created. Effectively you can never change a String after you create it. By Change I mean append new characters, remove characters without a new String object being created.
As many answers already point out is that you only change references. Immutable means you cannot change the string itself. for example you do:
String a = "anything";
System.out.println(a); // -> anything
a.substring(3);
System.out.println(a); // -> anything : this is because the String itself is
// immutable.
a = a.substring(3);
System.out.println(a); // -> thing : this is what immutable means to edit a string
// you must reassign it or assign it
// to a new variable
You're changing the reference of str to a. So str effectively becomes a's value.
they are just pointer to that string. so when you do str =a , you just assign pointer of a to str.
The contents of the String object is not being changed. What's happening is that a new String object is being assigned to the variable. The old String object still exists in memory but you just can't refer to it any more. The 'str' variable now refers to the String object containing "anything" but the String containing "something" still exists as it did before. Try assigning 'str' to another variable first and then, after assigning 'a' to 'str', check that other variable and you'll see that it still says "something", proving that that String was not overwritten.
In Java, the value of a variable is never an object, but a reference. Relevant to your case, the type String on the a variable says the variable is allowed to contain only references to String objects.
You can update the value in the variable, sure; but that won't touch the object it is referring to.
Strings are constant; their values cannot be changed after they are created.
The String object is created and stored on constant pool or literal pool.
In your case, when you say,
String str="something"; // An object is created on constant pool with value 'something' and reference 'str' is pointing to it.
String a="anything"; // An object is created on constant pool with value 'anything' and reference 'a' is pointing to it.
And when you do, str=a; then 'str' actually start pointed to 'a', but the object with value as 'something' remains on constant pool with the same value.

Pass by Value in Java

import java.util.Arrays;
public class Test {
public static void main(String... args) {
String[] strings = new String[] { "foo", "bar" };
changeReference(strings);
System.out.println(Arrays.toString(strings)); // still [foo, bar]
changeValue(strings);
System.out.println(Arrays.toString(strings)); // [foo, foo]
}
public static void changeReference(String[] strings) {
strings = new String[] { "foo", "foo" };
}
public static void changeValue(String[] strings) {
strings[1] = "foo";
}
}
Can anyone explain these questions?
What is Strings[]. Is it a String Object or String Object containing array of Objects.
What does the changeReference() and changeValue() functions do and return?
Does Java support Pass by Reference?
strings is an array of Strings. Arrays are objects for our purposes here, which means they are a reference type.
changeReference does nothing useful. It receives a reference to strings, but it receives that reference by value. Reassigning strings within the function has no effect on the strings being passed in -- it just replaces the local copy of the reference, with a reference to a new array. changeValue, on the other hand, modifies the array object referred to by strings. Since it's a reference type, the variable refers to the same object.
No, "pass by reference" is not supported. Java can pass references around, but it passes them by value. Summary being, you can change the object being passed in, but you can't replace the object in such a way that the caller will see it.
What is Strings[]. Is it a String Object or String Object containing array of Objects.
It’s neither. It’s an object (well, actually it’s a type) that references an array of strings.
What does the chanfeReference and changeValue function do and return?
Please try it yourself to see the effect.
Does Java support Pass by Reference?
No. Java is always pass by value.
What is String[]. Is it a String
Object or String Object containing
array of Objects.
String[] is an array of String (and String is an Object).
What does the changeReference and
changeValue function do and return?
In changeReference() java changes the reference of strings to an new string array. In changeValue(), java changes the value of the first element of strings array.
Does Java support Pass by Reference?
Java supports Pass by Value. As stated on JavaWorld:
Java does manipulate objects by
reference, and all object variables
are references. However, Java doesn't
pass method arguments by reference; it
passes them by value.
String[] is an array of String objects
changeReference changes the refence to the array strings to a new refence to a new array which, in this case, contains the same thing, but the reference in the memory is in another place.
Pass by Reference is not supported in Java

java immutable object question

String abc[]={"abc"};
String def[]={};
def=abc;
def[0]=def[0]+"changed";
System.out.println(abc[0]);
by changing "def" object, my abc object is changed as well. Beside String[] array has this characteristic what other java object has similar characteristic? can explain more? in order to prevent abc from changed when i changed def, i will have to do def = abc.clone();
You are confusing object mutability/immutability with copying of reference values.
In these diagrams, [var/index] is a reference variable, and {{an Object}} is an object.
String abc[]={"abc"};
String def[]={};
[abc] ------> {{a String[1]}}
[0] --------------> {{a String "abc"}}
[def] ------> {{a String[0]}}
Now you make def reference variable points to the same object as abc reference variable:
def=abc;
[abc] ------> {{a String[1]}}
/ [0] --------------> {{a String "abc"}}
/
[def] ---/ {{a String[0]}}
At this point, the array of length zero is unreferenced, and should be garbage-collectable. We can narrow our discussion to the array of length one. Note that a String[] is an array of references. With this next line, you changed what the only element in the length one array points to.
def[0]=def[0]+"changed";
[abc] ------> {{a String[1]}}
/ [0] ---------\ {{a String "abc"}}
/ \
[def] ---/ \--> {{a String "abcchanged"}}
Note that {{a String "abc"}} itself was not mutated. [abc] and [def] now points to the same {{a String[1]}}, which is mutable (i.e. you can make the elements of the array, which are references to String objects, to point to anything).
in order to prevent abc from changed when i changed def, i will have to do def = abc.clone();
Actually, that's not quite accurate. Let's see what happens if you clone() an array of references to a mutable type StringBuilder.
StringBuilder[] abc = new StringBuilder[] { new StringBuilder("Hello") };
StringBuilder[] def = abc.clone();
def[0].append(" world!");
System.out.println(abc[0]); // prints "Hello world!"
I won't make the diagrams for you this time, but you can easily draw it out on paper. What's happening here is that even though clone() makes a second {{a StringBuilder[1]}} object with its own element (i.e. def != abc), that element is pointing to the same {{a StringBuilder}} object (i.e. def[0] == abc[0]).
In short:
Immutability means that objects of a certain type can not change in any meaningful way to outside observers
Integer, String, etc are immutable
Generally all value types should be
Array objects are mutable
It may be an array of references to immutable types, but the array itself is mutable
Meaning you can set those references to anything you want
Also true for array of primitives
An immutable array will not be practical
References to objects can be shared
If the object is mutable, mutation will be seen through all these references
If you want more in-depth understanding of the issues, I recommend the following:
Is Java pass by reference? -- NO!
Are Java function parameters always passed-by-value? -- YES!
Immutable objects are objects that cannot be changed once created. String is an obvious example. Arrays are mutable. If you want an immutable collection, use a List instead:
List<String> abc = Collections.unmodifiableList(
Arrays.asList("abc")
);
Mutable objects have mutators. A mutator is any method that modifies the state of the object. Setters are an obvious example. A typical immutable object will look like this:
public class Person {
private final String firstName;
private final String lastName;
private final Date dateOfBirth;
public Person(String firstName, String lastName, Date dateOfBirth) {
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = new Date(dateOfBirth.getTime());
}
public String getFirstName() { return firstName; }
public String getLastname() { return lastName; }
public Date getDateOfBirth() { return new Date(dateOfBirth.getTime()); }
}
Generally speaking, for immutable objects, all members are final and immutable. Date is a good example of the issue above. Date is not immutable, which many (myself included) consider a design mistake. As a result of it being mutable you have to do lots of defensive copying.
just to be pedantic, there's no "abc" object or "def" object. There's the single String[] that abc, and then def happen to refer to. That's why "both objects" changed. They were, in fact, referring to the same object.
In simple terms it is like this :-
Lets assume Sample to be a class then,
Sample sam1 = new Sample();
will clearly be explained as sam1 being the reference to the object created.
but
Sample sam2;
just declares sam2 to be a reference variable of Sample type and has no object of Sample class being pointed by it.
now if we do this operation
sam2 = sam1;
then it means both the reference variables point to the same object and now one can refer to that object using any one of the two references.
Obviously one can manipulate the fields employing the valid methods using either of the references.
And this is what has been done here too.
String abc[]={"abc"};
String def[]={};
def=abc;
def[0]=def[0]+"changed";
and so changing def[0] changes abc[0] too.
Now when you clone you are creating a clone of the existent object.
The clone and the cloned objects independently exist
as 2 different objects and so the result of manipulations on one
is not reflected as you stated.
In java, you can always change the elements in an array, regardless of the type of the array. Consider making a separate copy of the data in order to protect the initial value of abc if you would like to keep the data in an array structure:
String abc[]={"abc"};
String def[];
def = Arrays.copyOf(abc, abc.length);
Alternatively, use cletus solution:
List abc = Collections.unmodifiableList(
Arrays.asList("abc")
);

Categories