This week's book giveaway is in the Kotlin forum.
We're giving away four copies of Kotlin in Action and have Dmitry Jemerov & Svetlana Isakova on-line!
See this thread for details.
Win a copy of Kotlin in Action this week in the Kotlin forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

String confusion  RSS feed

 
Jason Attin
Ranch Hand
Posts: 234
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys, I'm having a look at strings now, and specifically at some examples on the OCA book which are really puzzling.
Let me first give you one of my examples.
Take this code:


This line  , since strings are immutable, takes s1, makes a copy of it, adds "2" and copies the return value into s2.
This line doesn't really do anything, again, because I'm trying to change s2 which has already a value, and, being immutable, I can't change it. But this is OK because it's assigning the return value (an uppercase version of s2) to s3.

Now, let's have a quick look at the example on the book:


With this line we take a copy of string a, change the case, and copy it into b, which now should be immutable. Thing is, we then do : now if you ask me, this should do nothing because we're attempting to change string b, but in fact this prints A23 and not ABC as I thought it would.
What I'm trying to understand is, comparing the two examples, if this in the previous example doesn't execute because we're trying to modify the string, why on earth this which is trying to change b executes successfully?!?!?!

 
Henry Wong
author
Sheriff
Posts: 23283
125
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jason Attin wrote:
This line doesn't really do anything, again, because I'm trying to change s2 which has already a value, and, being immutable, I can't change it.


Yes, on the result, but no for the reason. The toUpperCase() method, if there are any lower case letters, will create and return a new String object that is all upper case. The reason nothing seems to happen is because the object reference that is returned is never used.

Jason Attin wrote:
But this is OK because it's assigning the return value (an uppercase version of s2) to s3.


In this second case, the object reference returned is used -- it is assigned to s3. So, the s3 reference points to the object that is returned from the method.

Notice that the toUpperCase() method does the exact same thing in both cases.

Henry
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jason Attin wrote:. . . This line  , since strings are immutable, takes s1, makes a copy of it, adds "2" and copies the return value into
What makes you think a copy is taken? Something in the background creates a new String object which is the same as s1 with 2 added. I believe a StringBuilder is used for that, but that is an implementation detail which may change in future.

This line doesn't really do anything, again, because I'm trying to change s2 which has already a value, and, being immutable, I can't change it. But this is OK because it's assigning the return value (an uppercase version of s2) to s3.
You are right that you cannot change the original String, but the upper case method creates a new String which is an upper case equivalent.
. . . Thing is, we then do : now if you ask me, this should do nothing . . .
Method chaining. Remember you go from left to right. The first method call goes through the String and creates a new String object with B replaced by 2. That new String is the object on which the second replace call is made; that replaces C by 3 and creates a third String object. The String "A2C" is no longer accessible, so it disappears into cyber‑limbo never to be seen again, and is eligible for garbage collection.
 
Jason Attin
Ranch Hand
Posts: 234
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
thanks guys.
The reason nothing seems to happen is because the object reference that is returned is never used.

OK fair enough, so I've changed that line to and now I can see the change. Still, they say strings are immutable, so I still don't get why we can do that.

When I said a copy, I meant in fact a new object which contains s1, sorry I didn't express myself correctly.

so this creates a new string which is upper case and the original string is lost, so I can happily copy this into s2 even if s2 already had a value. By "strings are immutable" I thought they mean the fact that, if a string is assigned a value, you can't change it. Evidently not. So what does that mean?
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jason Attin wrote:. . . and now I can see the change. Still, they say strings are immutable, so I still don't get why we can do that.
You are not mutating the String. You are creating a new String and assigning it to the original reference, replacing the lower‑case equivalent. The lower‑case equivalent now disappears into my cyber‑limbo etc etc.

When I said a copy, I meant in fact a new object which contains s1, sorry I didn't express myself correctly.
That is an implementation detail which might change. It is quite conceivable that you can create a means of catenation which does not create any object representing s1. You might be able to catenate the two char[] arrays underlying the two Strings without the new array ever being equivalent to s1. But as far as I know, the catenation uses a different technique, which can change from one version of Java® to another.

. . . By "strings are immutable" I thought they mean the fact that, if a string is assigned a value, you can't change it. . . .
The String is immutable but the variable is not a String. It is a variable which hides the memory location of a String. Unless that variable is final, you can always assign a different String reference to it.Remember s1 is final.
 
Julian West
Ranch Hand
Posts: 91
3
Chrome Java Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Echoing what Campbell said:

String s1 = "Hi there";

"s1" is a *reference* to an object.
"Hi there" is an immutable string object in the string pool.

String s1 = s1.toUpperCase();

"HI THERE" is a new string object.
s1 is a *reference* to the newly created string object

s1.toLowerCase();

"hi there" is an orphan string object with no reference to it.
s1 still references "HI THERE" as it has not been reassigned with =.

Contrast with mutable object type StringBuilder:
StringBuilder sb = new StringBuilder("Hi");
sb.append(" there");

Since sb is a mutable object, the append method mutates that object: System.out.print(sb); would output "Hi there".
 
Campbell Ritchie
Marshal
Posts: 55678
161
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Julian West wrote:. . . mutable object type StringBuilder: . . . sb is a mutable object . . .
That shows the usefulness of StrinugBuilder objects. Find out about them, and also the correct spelling
 
Julian West
Ranch Hand
Posts: 91
3
Chrome Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Haha...that's funny because when I typed "StringBuilder", I had to re-do it 5 times.  It's like my keyboard isn't immutable as the keys kept changing right underneath my fingers.

I don't understand why StringBuilder's .equals method is the same as the ineffectual == operator; why didn't they make it actually evaluate the objects rather than forcing us to write an override?  Or is there some other direct way to go about comparing two strings?  (I have no experience, so it may be one of those things one realises after doing it a while.)
 
Fred Kleinschmidt
Bartender
Posts: 560
9
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
b = b.replace("B","2").replace('C','3');
: now if you ask me, this should do nothing because we're attempting to change string b,


This  line does not change the original string b. It is creating a new String, and then reassigning the reference b to that new String. The original String now has no references, so is available for garbage collection. Consider this:

Note that the last line does not alter the original c (which is "abc"), but merely points it to a different object which contains "def".
So you can think of the first example as being the same thing as

 
Knute Snortum
Sheriff
Posts: 4070
112
Chrome Eclipse IDE Java Postgres Database VI Editor
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I don't understand why StringBuilder's .equals method is the same as the ineffectual == operator; why didn't they make it actually evaluate the objects rather than forcing us to write an override?  Or is there some other direct way to go about comparing two strings?  (I have no experience, so it may be one of those things one realises after doing it a while.)

If you think about it, StringBuilder is just an object.  It happens that a person might want string1 == string2 to compare the strings, but what about other objects?  What if you had a Person object?  How would Java know how to compare them?  That is why there is an equals method in Object to override, so one can compare objects in a uniform fashion.
 
Jason Attin
Ranch Hand
Posts: 234
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Right, thanks guys, I think it's clear now
Cheers
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!