Reputation: 59576
Let's assume I create the following two strings at runtime (from user input for example):
public void someMethod(String input) {
if ( input == null ) return;
String a = input + input;
String b = input;
...
}
Is Java (and its compiler) smart enough to detect at runtime that b
is contained in a
and therefore it is not necessary to allocate memory for b
? b
could just point at a
with half the length?
In other words, does Java implement a dynamic version of String.intern()
?
EDIT
Considering answers made so far, my example should be:
public void someMethod(String input) {
if ( input == null ) return;
String a = input + input + input;
String b = input + input;
...
}
Upvotes: 1
Views: 745
Reputation: 8245
You're not actually creating two strings in your example: b
is just a reference to input
. So, to do what you're asking, Java would have to somehow go back and alter old strings when a new string is created (such as by saying input + input
).
To answer your broader question, AFAIK the only way for two strings to be sharing memory (besides, as you mention, being intern()
'd) is for one or both to have been created using substring()
. So if you really wanted to save on memory, you could do this:
String a = input + input;
String b = a.substring(input.length());
(To be clear, this will only save memory if the value of b
is being stored somewhere but input
is discarded and winds up garbage-collected.)
EDIT
New and improved example for new and improved question:
String a = input + input + input;
String b = a.substring(2 * input.length());
(Note that this will always save memory over the second example in the question, since we've avoided an allocation altogether. So the previous caveat doesn't apply.)
Upvotes: 3
Reputation: 21306
As others have pointed out, b
just points to the same string as input
.
As for a
, we can use javap to disassemble your code:
public static void someMethod(java.lang.String);
Code:
0: aload_0
1: ifnonnull 5
4: return
5: new #4; //class java/lang/StringBuilder
8: dup
9: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
12: aload_0
13: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: aload_0
17: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: astore_1
24: aload_0
25: astore_2
26: return
The compiler uses StringBuilder.append()
to perform the concatenation, which (in Sun/OpenJDK, at least) just copies everything into a big char
array - no intern()
ing here. Same applies with your edit - the input string is appended 5 times in total.
I can imagine an alternate implementation of String(Builder|Buffer)
could be written to maintain a chain of arrays, which would allow better reuse and efficiency in some cases.
Upvotes: 2
Reputation: 1357
Nop, the compiler is intelligent enough to detect String concatenation at compile time, for instance:
String a = "hello, " + "world";
//Will become
String a = "hello, world";
But substring matching is a bit much to do every time you create a string.
However Java tries to avoid multiple string creation (if two strings are equal), you should check String.intern()
Upvotes: 1
Reputation: 81684
No, this won't happen, because the String passed in as an argument already exists, and Strings are immutable. The double-length one has to be created separately, since the original array won't have room for more characters.
In the reverse case, though -- where you start out with the long string, and use substring()
to create a shorter one -- the two strings will, indeed, share the same char array.
Upvotes: 2