Are runtime Strings interned? - java

Here's my code:
public class StringExperimenter {
public static void main(String[] args) {
String s1 = "This is" + "limafoxtrottango";
String s2 = "This is" + getName();
String s3 = "This is" + getName();
System.out.println(s1 == s2); // prints false
System.out.println(s3 == s2); // prints false
}
private static String getName() {
return "limafoxtrotango";
}
}
Now, I understand that s1 is evaluated by the compiler at compile-time and it is then interned.
But, what about s2 and s3? Both of these are created at runtime. So, are two different strings created? Do these strings go to the constant string pool?

s1 is completely interned, i.e.
This islimafoxtrottango
as the string concatenation happens at compile-time.
The other two require a method call, only the intermediate strings are interned. The concatenation is not, as it happens at runtime.
You also observed that already, as
System.out.println(s1 == s2); //prints false
System.out.println(s3 == s2); //prints false
would otherwise print true.
You can check the JLS (Java Language Specification) for details on this behavior, every valid Java implementation must do it exactly that way.
From JLS§15.28 about Constant Expressions:
A constant expression is an expression denoting a value of primitive type or a String that does not complete abruptly and is composed using only the following:
The following list contains more or less:
literals
operators like +
constant variables
Importantly, it does not list any method calls, even if they return a constant value.
It also states that:
Constant expressions of type String are always "interned" so as to share unique instances, using the method String.intern.

If you compile your class from the command line like this:
javac.exe -g StringExperimenter.java
And then use javap.exe -v StringExperimenter.class to study the resulting class file, then you will see, that the following byte code is generated:
...
public static void main(java.lang.String...);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS
Code:
stack=3, locals=4, args_size=1
0: ldc #2 // String This islimafoxtrottango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String This is
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
15: invokestatic #7 // Method getName:()Ljava/lang/String;
18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
21: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_2
25: new #3 // class java/lang/StringBuilder
28: dup
29: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
32: ldc #5 // String This is
34: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
37: invokestatic #7 // Method getName:()Ljava/lang/String;
40: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder
43: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: astore_3
47: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
50: aload_1
51: aload_2
52: if_acmpne 59
55: iconst_1
56: goto 60
59: iconst_0
60: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
63: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
66: aload_3
67: aload_2
68: if_acmpne 75
71: iconst_1
72: goto 76
75: iconst_0
76: invokevirtual #10 // Method java/io/PrintStream.println:(Z)V
79: return
...
Based on this, I would say that s1 is interned whereas s2 and s3 are computed (using StringBuilder) at runtime.

As Conspect of String is immutable in java.
Above result are true.
Whenever we append something in one string by concating creates a new reference in the String intern pool.
So if compare all above 3 Strings using equals methods its return True but false if compare using == operator.

Related

How does final keyword work with string immutability? [duplicate]

This question already has an answer here:
How is concatenation of final strings done in Java?
(1 answer)
Closed 2 years ago.
I have the following code. I understand the concept of java string immutability and string constant pool. I don't understand why 'name1 == name2' results false and 'name2 == name3' results true in the following program. How are the string variables name1, name2, and name3 placed in the string constant pool?
public class Test {
public static void main(String[] args) {
final String firstName = "John";
String lastName = "Smith";
String name1 = firstName + lastName;
String name2 = firstName + "Smith";
String name3 = "John" + "Smith";
System.out.println(name1 == name2);
System.out.println(name2 == name3);
}
}
Output:
false
true
The answer is fairly simple: As a shortcut, java treats certain concepts as a so-called 'compile time constant' (CTC). The idea is to entirely inline a variable, at the compilation level (which is extraordinary; normally javac basically just bashes your java source file into a class file using very simple and easily understood transformations, and the fancypants optimizations occur at runtime during hotspot).
For example, if you do this:
Save to UserOfBatch.java:
class BatchOConstants {
public static final int HELLO = 5;
}
public class UserOfBatch {
public static void main(String[] args) {
System.out.println(BatchOConstants.HELLO);
}
}
Run on the command line:
> javac UserOfBatch.java
> java UserOfBatch
5
> javap -c UserOfBatch # javap prints bytecode
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_5
4: invokevirtual #15 // Method java/io/PrintStream.println:(I)V
7: return
Check out line 3 up there. iconst_5. That 5? It was hardcoded!!
No reference to BatchOConstants remains. Let's test that:
on command line:
> rm BatchOConstants.class
> java UserOfBatch
5
Wow. The code ran even though it's missing the very class file that should be providing that 5, thus proving, it was 'hardcoded' by the compiler itself and not the runtime.
Another way to 'observe' CTC-ness of any value is annoparams. An annotation parameter must be hardcoded into the class file by javac, so you can't pass expressions. Given:
public #interface Foo {
long value();
I can't write: #Foo(System.currentTimeMillis()), because System.cTM obviously isn't a compile time constant. But I can write #Foo(SomeClass.SOME_STATIC_FINAL_LONG_FIELD) assuming that the value assigned to S_S_F_L_F is a compile time constant. If it's not, that #Foo(...) code would not compile. If it is, it will compile: CTC-ness now determines whether your code compiles or not.
There are specific rules about when the compiler is allowed to construe something as a 'compile time constant' and go on an inlining spree. For example, null is not an inline constant, ever. Oversimplifying, but:
For fields, the field must be static and final and have a primitive or String type, initialized on the spot (in the same breath, not later in a static block), with a constant expression, that isn't null.
For local variables, the rules are very similar, except, the need for them to be static is obviously waived as they cannot be. Other than that, all the fixins apply: final, primitive-or-String, non-null, initialized on the spot, and with a constant expression.
Unfortunately, CTC-ness of a local is harder to directly observe. The code you wrote is a fine way to indirectly observe it though. You've proven with your prints that firstName is CTC and lastName is not.
We can observe a select few things though. So let's take your code, compile it, and toss it at javap to witness the results. Via Jonas Konrad's online javap tool, let's analyse:
public Main() {
final String a = "hello";
String b = "world";
String c = a + "!";
String d = b + "!";
System.out.println(c == "hello!");
System.out.println(d == "world!");
}
The relevant parts:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: ldc #7 // String hello
6: astore_1
start local 1 // java.lang.String a
7: ldc #9 // String world
9: astore_2
start local 2 // java.lang.String b
10: ldc #11 // String hello!
12: astore_3
start local 3 // java.lang.String c
13: aload_2
14: invokedynamic #13, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
19: astore 4
start local 4 // java.lang.String d
Note how 'start local 2' (which is c; javap starts counting at 0) shows just loading hello! as a complete constant, but 'start local 3' (which is d) shows loading 2 constants and invoking makeConcat to tie em together.
run javap -c Test after you compiled the code,
you will see that
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String Smith
2: astore_2
3: aload_2
4: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
9: astore_3
10: ldc #4 // String JohnSmith
12: astore 4
14: ldc #4 // String JohnSmith
16: astore 5
18: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
21: aload_3
22: aload 4
24: if_acmpne 31
27: iconst_1
28: goto 32
31: iconst_0
32: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
35: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload 4
40: aload 5
42: if_acmpne 49
45: iconst_1
46: goto 50
49: iconst_0
50: invokevirtual #6 // Method java/io/PrintStream.println:(Z)V
53: return
}
As you can see
4: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
in public static void main(java.lang.String[]);, this is the actual compiled byte code when running firstname + lastname. So generated String will not have the same hashcode as "JohnSmith" in constant pool.
where as on
10: ldc #4 // String JohnSmith
and
14: ldc #4 // String JohnSmith
this is the byte code generated by the compiler when doing both
firstname + "Smith" and "John" + "Smith" which means both actually reading from the constant pool.
This is the reason why when you comparing name1 with name2 using == it will return false. since name2 and name3 reference the same string from the constant pool. Hench it return true when compare with ==.
This is the reason why it is not a good idea to compare 2 string with ==. Please use String.equals() when doing String comparison.
since both
Let's look at the bytecode with final:
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #7 // String Smith
2: astore_1
3: aload_1
4: invokedynamic #9, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
9: astore_2
10: ldc #13 // String JohnSmith
12: astore_3
13: ldc #13 // String JohnSmith
15: astore 4
17: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
20: aload_2
21: aload_3
22: if_acmpne 29
25: iconst_1
26: goto 30
29: iconst_0
30: invokevirtual #21 // Method java/io/PrintStream.println:(Z)V
33: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_3
37: aload 4
39: if_acmpne 46
42: iconst_1
43: goto 47
46: iconst_0
47: invokevirtual #21 // Method java/io/PrintStream.println:(Z)V
50: return
}
And the bytecode without final:
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #7 // String John
2: astore_1
3: ldc #9 // String Smith
5: astore_2
6: aload_1
7: aload_2
8: invokedynamic #11, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
13: astore_3
14: aload_1
15: invokedynamic #15, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
20: astore 4
22: ldc #18 // String JohnSmith
24: astore 5
26: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
29: aload_3
30: aload 4
32: if_acmpne 39
35: iconst_1
36: goto 40
39: iconst_0
40: invokevirtual #26 // Method java/io/PrintStream.println:(Z)V
43: getstatic #20 // Field java/lang/System.out:Ljava/io/PrintStream;
46: aload 4
48: aload 5
50: if_acmpne 57
53: iconst_1
54: goto 58
57: iconst_0
58: invokevirtual #26 // Method java/io/PrintStream.println:(Z)V
61: return
}
As you can see, with final, Java recognizes both the left and right hand sides of + to be constant, so it replaces the concatenation with the constant string "JohnSmith" at compile time. The only call to makeConcatWithConstants (to concatenate strings) is made for firstName + lastName, since lastName isn't final.
In the second example, there are two calls to makeConcatWithConstants, one for firstName + lastName and another for firstName + "Smith", since Java doesn't recognize firstName as a constant.
That's why name1 == name2 is false in your example: name2 is a constant "JohnSmith" in the string pool, whereas name1 is dynamically computed at runtime. However, name2 and name3 are both constants in the string pool, which is why name2 == name3 is true.

How many objects will be created and how many objects will be available for garbage collection?

String str = "test";
str = str + "test2";
str = str + "test3";
str = str + "test4";
str = str + "test5";
How many objects will be created from the above code and how many objects will be available for garbage collection?
Can someone please explain this?
How many objects will be created
At runtime, 4, i.e. the four computed values of str, excluding the initial value which comes from the constant pool.
and how many objects will be available for garbage collection?
At the end of this code but before str goes out of scope, three, i.e. the three intermediate values of str.
Note that I am counting Strings. Each String will have an associated char[] which is another object.
However if the surrounding code is such that the JVM can determine that str cannot change between these lines of code it could be as low as one and zero respectively.
The JavaC is very odd regarding String manipulation. For example, why dont they use String.concat when you do "String += otherString"?
Instead, Java creates a StringBuilder (or StringBuffer, depending on the Java version) for each line ended with ; that you have concatenating Strings.
I placed your code in a test program (TCTestWin) and called from command line:
javap -c TCTestWin.class
These are the results:
0: ldc #15 // String test
2: astore_1
3: new #17 // class java/lang/StringBuilder
6: dup
7: aload_1
8: invokestatic #19 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
11: invokespecial #25 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
14: ldc #28 // String test2
16: invokevirtual #30 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: new #17 // class java/lang/StringBuilder
26: dup
27: aload_1
28: invokestatic #19 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
31: invokespecial #25 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
34: ldc #38 // String test3
36: invokevirtual #30 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
42: astore_1
43: new #17 // class java/lang/StringBuilder
46: dup
47: aload_1
48: invokestatic #19 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
51: invokespecial #25 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
54: ldc #40 // String test4
56: invokevirtual #30 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
59: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
62: astore_1
63: new #17 // class java/lang/StringBuilder
66: dup
67: aload_1
68: invokestatic #19 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
71: invokespecial #25 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
74: ldc #42 // String test5
76: invokevirtual #30 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
79: invokevirtual #34 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
82: astore_1
83: return
As you can see, for each line, JavaC creates a StringBuilder, appends the String, and continues.
Such code, specially when used inside a loop, will rush the Garbage Collector.
There are three better ways to do it:
Use concat instead:
String str = "test";
str = str.concat("test2");
str = str.concat("test3");
str = str.concat("test4");
str = str.concat("test5");
Use a single line of concatenation, which will create a single StringBuilder. Remember that each ; will create another StringBuilder. Note that in the concatenation of constant Strings below, the Java compiler will create a single String. However, if you add one or more String variables, then the StringBuilder will be created.
String str = "test" + "test2" + "test3" + "test4" + "test5";
Create a StringBuilder yourself, do the concatenation, and REUSE the StringBuilder. This has the best performance, specially when doing things in a loop.
StringBuilder sb = new StringBuilder(512);
for (int i = 0; i < 10000; i++)
{
sb.setLength(0);
sb.append("test");
sb.append("test2");
sb.append("test3");
sb.append("test4");
sb.append("test5");
sb.append(i);
String s = sb.toString();
}
So, from the code above, 4 different StringBuilders will be created. After the final String, all StringBuilders will be collected.

Which code snippet executes faster?

I can do same thing by two types of code snippet.
First Way:
String makeDate = Integer.toString(now.year) + Integer.toString(now.month) + Integer.toString(now.monthDay);
Or Second Way:
String makeDate = now.year + "" + now.month + "" + now.monthDay;
My question is:
Which method is preferable [First way or Second way]?
Which code snippet will execute faster?
The two snippits you show are nearly identical.
a String in Java is immutable; it can't be changed. When using the concatenation operator (+) the compiler actually generates code using a StringBuilder
For example your second snippit becomes:
String makeDate = new StringBuilder()
.append(now.year)
.append("")
.append(now.month)
.append("")
.append(now.monthDay)
.toString();
You can look at the generated bytecode to see this. Java comes with a program javap that allows you to look at your compiled .class.
I created a simple main() to provide minimal bytecode:
public static void main(String[] args)
{
String makeDate = Integer.toString(1) + Integer.toString(1) + Integer.toString(1);
System.out.println(makeDate);
}
Which produces:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: iconst_1
8: invokestatic #4 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: iconst_1
15: invokestatic #4 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
18: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: iconst_1
22: invokestatic #4 // Method java/lang/Integer.toString:(I)Ljava/lang/String;
25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_1
32: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
35: aload_1
36: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
39: return
Versus:
public static void main(String[] args)
{
int i = 1;
String makeDate = i + "" + i + "" + i;
System.out.println(makeDate);
}
Produces:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: iconst_1
1: istore_1
2: new #2 // class java/lang/StringBuilder
5: dup
6: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
9: iload_1
10: invokevirtual #4 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
13: ldc #5 // String
15: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: iload_1
19: invokevirtual #4 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
22: ldc #5 // String
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: iload_1
28: invokevirtual #4 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
31: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_2
35: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload_2
39: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: return
Technically the latter is probably faster at some scale that is nearly immeasurable (< 1ns) but for all practical purposes it doesn't matter; use whichever you like.

Why is the switch statement faster than if else for String in Java 7?

In Java 7 a string object can be in the expression of a switch statement. Can someone explain the below statement from official documentation?
The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.
Java Code
Having two versions of a class, e.g.
With if-then-else:
public class IfThenElseClass {
public static void main(String[] args) {
String str = "C";
if ("A".equals(str)) {
} else if ("B".equals(str)) {
} else if ("C".equals(str)) {
}
}
}
With switch:
public class SwitchClass {
public static void main(String[] args) {
String str = "C";
switch (str) {
case "A":
break;
case "B":
break;
case "C":
break;
}
}
}
Bytecode
Let's take a look at the bytecode. Getting the bytecode for if-then-else version:
Compiled from "CompileSwitch.java"
public class CompileSwitch {
public CompileSwitch();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String C
2: astore_1
3: ldc #18 // String A
5: aload_1
6: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
9: ifne 28
12: ldc #26 // String B
14: aload_1
15: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
18: ifne 28
21: ldc #16 // String C
23: aload_1
24: invokevirtual #20 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
27: pop
28: return
}
Getting the bytecode for switch version:
Compiled from "CompileSwitch.java"
public class CompileSwitch {
public CompileSwitch();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16 // String C
2: astore_1
3: aload_1
4: dup
5: astore_2
6: invokevirtual #18 // Method java/lang/String.hashCode:()I
9: lookupswitch { // 3
65: 44
66: 56
67: 68
default: 77
}
44: aload_2
45: ldc #24 // String A
47: invokevirtual #26 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
50: ifne 77
53: goto 77
56: aload_2
57: ldc #30 // String B
59: invokevirtual #26 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
62: ifne 77
65: goto 77
68: aload_2
69: ldc #16 // String C
71: invokevirtual #26 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
74: ifne 77
77: return
}
Conclusion
In the first version compares the string by calling the equals method for each condition, until it is found.
In the second version is obtained first hashCode of the string. Then this is compared with the values ​​hashCode each case. See the lookupswitch. If any of these values ​​is repeated just happens to run the code for the case. Otherwise, call the equals method of the cases tied. This is much faster than ever call the equals method only.
switch on strings can be faster for the same reason why a lookup in a hash set of strings may be faster than a lookup in a list of strings: you can do a lookup in O(1) rather than in O(N), where N is the number of strings.
Recall that switch is more efficient than a chain of if-then-else statements because it is a calculated jump: an offset in code is calculated based on the value, and then the jump to that offset is executed. Java can pull a similar trick on strings using the mechanism similar to that employed in hash maps and hash sets.
It's more efficient something like:
switch(yourString) {
case "text1":
// your code
break;
case "text2":
// etc.
}
than the correspondent:
if (yourString.equals("text1")) {
// your code
} else if (yourString.equals("text2")) {
// etc.
}
i guess what it means, or what i understand is that the bytecode (when you compile your java class) that created from an switch statement using string is faster and more efficient than the bytecode that is created from an if-else statement using string. both can do the same job, bit switch is apparently more efficient.
switch (str) {
case "A":
// do something
break;
case "B":
// do something
break;
default:
//do something
break;
}
is better than
if(str.equals("A")) {
//do something
} else if(str-equals("B")) {
//do something
} else {
//do something
}

How many objects are created

I was having a discussion about usage of Strings and StringBuffers in Java. How many objects are created in each of these two examples?
Ex 1:
String s = "a";
s = s + "b";
s = s + "c";
Ex 2:
StringBuilder sb = new StringBuilder("a");
sb.append("b");
sb.append("c");
In my opinion, Ex 1 will create 5 and Ex 2 will create 4 objects.
I've used a memory profiler to get the exact counts.
On my machine, the first example creates 8 objects:
String s = "a";
s = s + "b";
s = s + "c";
two objects of type String;
two objects of type StringBuilder;
four objects of type char[].
On the other hand, the second example:
StringBuffer sb = new StringBuffer("a");
sb.append("b");
sb.append("c");
creates 2 objects:
one object of type StringBuilder;
one object of type char[].
This is using JDK 1.6u30.
P.S. To the make the comparison fair, you probably ought to call sb.toString() at the end of the second example.
In terms of objects created:
Example 1 creates 8 objects:
String s = "a"; // No object created
s = s + "b"; // 1 StringBuilder/StringBuffer + 1 String + 2 char[] (1 for SB and 1 for String)
s = s + "c"; // 1 StringBuilder/StringBuffer + 1 String + 2 char[] (1 for SB and 1 for String)
Example 2 creates 2 object:
StringBuffer sb = new StringBuffer("a"); // 1 StringBuffer + 1 char[] (in SB)
sb.append("b"); // 0
sb.append("c"); // 0
To be fair, I did not know that new char[] actually created an Object in Java (but I knew they were created). Thanks to aix for pointing that out.
You can determine the answer by analyzing the java bytecode (use javap -c). Example 1 creates two StringBuilder objects (see line #4) and two String objects (see line #7), while example 2 creates one StringBuilder object (see line #2).
Note that you must also take the char[] objects into account (since arrays are objects in Java). String and StringBuilder objects are both implemented using an underlying char[]. Thus, example 1 creates eight objects and example 2 creates two objects.
Example 1:
public static void main(java.lang.String[]);
Code:
0: ldc #2; //String a
2: astore_1
3: new #3; //class java/lang/StringBuilder
6: dup
7: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6; //String b
16: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_1
23: new #3; //class java/lang/StringBuilder
26: dup
27: invokespecial #4; //Method java/lang/StringBuilder."<init>":()V
30: aload_1
31: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: ldc #8; //String c
36: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
42: astore_1
43: return
}
Example 2:
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/lang/StringBuilder
3: dup
4: ldc #3; //String a
6: invokespecial #4; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
9: astore_1
10: aload_1
11: ldc #5; //String b
13: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: pop
17: aload_1
18: ldc #7; //String c
20: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: pop
24: return
}
The answer is tied to specific implementations of the language (compiler and runtime libraries). Even to the presence of specific optimization options or not. And, of course, version of the implementation (and, implicitly, the JLS it is compliant with). So, it's better to speak in term of minima and maxima. In fact, this exercise gives a better
For Ex1, the minimum number of objects is 1 (the compiler realizes that there are only constants involved and produces only code for String s= "abc" ; ). The maximum could be just anything, depending on implementation, but a reasonable estimation is 8 (also given in another answer as the number produced by certain configuration).
For Ex2, the minimum number of objects is 2. The compiler has no way of knowing if we have replaced StringBuilder with a custom version with different semantics, so it will not optimize. The maximum could be around 6, for an extremely memory-conserving StringBuilder implementation that expands a backing char[] array one character at a time, but in most cases it will be 2 too.

Categories