g.drawString("myScore "+scoreInt);
You’d be suprised how much code that actually expands to!
At the 1st level, it simply expands to :-
g.drawString((new StringBuilder().append("myScore").append(scoreInt)).toString());
This obviously involves 4 object creations.
- new StringBuilder()
- new char [16] (inside StringBuilder constructor)
- new String() (inside StringBuilder.toString())
- new char [stringLength] (inside String constructor)
However, the object creation realy is only part of the reason for this codes slowness.
Heres the exact path of all the code (WARNING, its fukin HUGE!):-
//StringBuilder constructor
public StringBuffer() {
super(16);
}
//AbstractStringBuilder constructor and related member vars.
char value[];
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
//StringBuilder.append(String)
public StringBuilder append(String str) {
super.append(str);
return this;
}
//AbstractStringBuilder.append(...)
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length(); // not worth expanding this, its a trivial 'getter' method.
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount); //not worth expanding, it isn't executed
str.getChars(0, len, value, count); //expanded below
count = newCount;
return this;
}
//String.getChars(......)
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > count) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, offset + srcBegin, dst, dstBegin,
srcEnd - srcBegin);
}
// now, the String is appended to the StringBuilder
// we trace what is needed to append the int.
//StringBuilder.append(int)
public StringBuilder append(int i) {
super.append(i);
return this;
}
public AbstractStringBuilder append(int i) {
if (i == Integer.MIN_VALUE) {
append("-2147483648");
return this;
}
int appendedLength = (i < 0) ? stringSizeOfInt(-i) + 1
: stringSizeOfInt(i); //this method call is expanded below
int spaceNeeded = count + appendedLength;
if (spaceNeeded > value.length)
expandCapacity(spaceNeeded); //not expanded, it won't be called
Integer.getChars(i, spaceNeeded, value); //expended *further* below
count = spaceNeeded;
return this;
}
//static member var needed for stringSizeOfInt(int) method
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
99999999, 999999999, Integer.MAX_VALUE };
// stringSizeOfInt(int) called from AbstractStringBuilder.append(int)
static int stringSizeOfInt(int x) {
for (int i=0; ; i++)
if (x <= sizeTable[i])
return i+1;
}
//static member vars of Integer class, needed for Integer.getChars(....)
final static char [] DigitTens = {
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
'2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
'3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
'4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
'5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
'6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
'7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
'8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
'9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
} ;
final static char [] DigitOnes = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
} ;
//Integer.getChars(....) also called from AbstractStringBuilder.append(int)
static void getChars(int i, int index, char[] buf) {
int q, r;
int charPos = index;
char sign = 0;
if (i < 0) {
sign = '-';
i = -i;
}
// Generate two digits per iteration
while (i >= 65536) {
q = i / 100;
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
buf [--charPos] = DigitOnes[r];
buf [--charPos] = DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
// assert(i <= 65536, i);
for (;;) {
q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r];
i = q;
if (i == 0) break;
}
if (sign != 0) {
buf [--charPos] = sign;
}
}
//we now have the StringBuilder created, and the String + int components added to it.
//we just have to call toString on it...
//StringBuilder.toString()
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
//String constructor
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// Note: offset or count might be near -1>>>1.
if (offset > value.length - count) {
throw new StringIndexOutOfBoundsException(offset + count);
}
char[] v = new char[count];
System.arraycopy(value, offset, v, 0, count);
this.offset = 0;
this.count = count;
this.value = v;
}
Don’t you just love OO