Converting longs to Strings

- 100 - public String sayHiString title, String name { return hi + title + + name; } The String generated by this method cannot be resolved at compile time because the variables can have any value. The compiler is free to generate code to optimize the String creation, but it does not have to. Consequently, the String -creation line could be compiled as: return new StringBuffer .appendhi . appendtitle.append .appendname.toString ; This is optimal, creating only two objects. On the other hand, the compiler could also leave the line with the default implementation of the concatenation operator, which is equivalent to: return hi .concattitle.concat .concatname; This last implementation creates two intermediate String objects that are then thrown away, and these are generated every time the method is called. So, when the String can be fully resolved at compile time, the concatenation operator is more efficient than using a StringBuffer . But when the String cannot be resolved at compile time, the concatenation operator is less efficient than using a StringBuffer . One further point is that using the String constructor in a String definition forces a runtime string creation: String s = new Stringhi + Mr. + + Buddy; is compiled as: String s = new Stringhi Mr. Buddy; This line uses the compile-time resolved string as a parameter for the String constructor to create a new String object at runtime. The new String object is equal but not identical to the original string: String s = new Stringhi Mr. Buddy; s == hi Mr. Buddy; is false s.equalshi Mr. Buddy; is true

5.3 Conversions to Strings

Generally, the JDK methods that convert objects and data types to strings are suboptimal, both in terms of performance and the number of temporary objects used in the conversion procedure. In this section, we consider how to optimize these conversions.

5.3.1 Converting longs to Strings

Lets start by looking at conversion of long values. In the JDK, this is achieved with the Long.toString method. Bear in mind that you typically add a converted value to a StringBuffer explicitly, or implicitly with the + concatenation operator. So it would be nice to avoid the two intermediate temporary objects created while converting the long , i.e., the one char - 101 - array inside the conversion method, and the returned String object that is used just to copy the char s into the StringBuffer . Avoiding the temporary char array is difficult to do, because most fast methods for converting numbers start with the low digits in the number, and you cannot add to the StringBuffer from the low to the high digits unless you want all your numbers coming out backwards. However, with a little work, you can get to a method that is fast and obtains the digits in order. The following code works by determining the magnitude of the number first, then successively stripping off the highest digit: Up to radix 36 private static final char[] charForDigit = { 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h, i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z }; public static void appendStringBuffer s, long i { if i 0 { convert negative to positive numbers for later algorithm if i == Long.MIN_VALUE { cannot make this positive due to integer overflow, so treat it specially s.append-9223372036854775808; return; } otherwise append the minus sign, and make the number positive s.append-; i = -i; } Get the magnitude of the int long mag = l_magnitudei; long c; while mag 1 { The highest digit c = imag; s.appendcharForDigit[int c]; remove the highest digit c = mag; if c = i i -= c; and go down one magnitude mag = 10; } The remaining magnitude is one digit large s.appendcharForDigit[int i]; } private static long l_magnitudelong i { if i 10L return 1; else if i 100L return 10L; else if i 1000L return 100L; else if i 10000L return 1000L; else if i 100000L return 10000L; else if i 1000000L return 100000L; else if i 10000000L return 1000000L; - 102 - else if i 100000000L return 10000000L; else if i 1000000000L return 100000000L; else if i 10000000000L return 1000000000L; else if i 100000000000L return 10000000000L; else if i 1000000000000L return 100000000000L; else if i 10000000000000L return 1000000000000L; else if i 100000000000000L return 10000000000000L; else if i 1000000000000000L return 100000000000000L; else if i 10000000000000000L return 1000000000000000L; else if i 100000000000000000L return 10000000000000000L; else if i 1000000000000000000L return 100000000000000000L; else return 1000000000000000000L; } When compared to executing the plain StringBuffer.appendlong , the algorithm listed here takes at most 90 of the StringBuffer time see Table 5-1 and creates two fewer objects it can be even faster, but Ill leave the more complicated tuning to the next section. If you are writing out long values a large number of times, this is a useful speedup. Table 5-1, Time Taken to Append a long to a StringBuffer VM 1.2 1.3 HotSpot 1.0 1.1.6 JDK long conversion 100 113 227 272 Optimized long conversion 90 103 146 133 There are several things to note about possible variations of this algorithm. First, although the algorithm here is specifically radix 10 decimal, it is easy to change to any radix. To do this, the reduction in magnitude in the loop has to go down by the radix value, and the l_magnitude method has to be altered. For example, for radix 16, hexadecimal, the statement mag = mag10 becomes mag = mag16 and the magnitude method for radix 16 looks like: private static long l_magnitude16long i { if i 16L return 1; else if i 256L return 16L; else if i 4096L return 256L; else if i 65536L return 4096L; else if i 1048576L return 65536L; else if i 16777216L return 1048576L; else if i 268435456L return 16777216L; else if i 4294967296L return 268435456L; else if i 68719476736L return 4294967296L; else if i 1099511627776L return 68719476736L; else if i 17592186044416L return 1099511627776L; else if i 281474976710656L return 17592186044416L; else if i 4503599627370496L return 281474976710656L; else if i 72057594037927936L return 4503599627370496L; else if i 1152921504606846976L return 72057594037927936L; else return 1152921504606846976L; } Second, because we are working through the digits in written order, this algorithm is suitable for writing directly to a stream or writer such as a FileWriter without the need for any temporary objects. This is potentially a large gain, enabling writes to files without generating intermediate temporary strings. Finally, if you want formatting added in, the algorithm is again suitable, because you proceed through the number in written order, and also because you have the magnitude at the start. You can - 103 - easily create another method, similar to magnitude , that returns the number of digits in the value. You can put in a comma every three digits as the number is being written or apply whatever internationalized format is required. This saves you having to write out the number first in a temporary object and then add formatting to it. For example, if you are using integers to fake fixed- place floating-point numbers, you can insert a point at the correct position without resorting to temporary objects.

5.3.2 Converting ints to Strings