StringUtil.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*
  2. * Copyright (C) 2004-2014 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.util;
  20. import javolution.text.TextBuilder;
  21. import com.l2jserver.Config;
  22. /**
  23. * String utilities optimized for the best performance.<br>
  24. * <h1>How to Use It</h1> <h2>concat() or append()</h2> If concatenating strings<br>
  25. * in single call, use StringUtil.concat(), otherwise use StringUtil.append()<br>
  26. * and its variants.<br>
  27. * <br>
  28. * <h2>Minimum Calls</h2><br>
  29. * Bad:
  30. *
  31. * <pre>
  32. * final StringBuilder sbString = new StringBuilder();
  33. * StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId));
  34. * StringUtil.append(&quot;text 2&quot;);
  35. * </pre>
  36. *
  37. * Good:
  38. *
  39. * <pre>
  40. * final StringBuilder sbString = new StringBuilder();
  41. * StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
  42. * </pre>
  43. *
  44. * Why?<br/>
  45. * Because the less calls you do, the less memory re-allocations have to be done<br>
  46. * so the whole text fits into the memory and less array copy tasks has to be<br>
  47. * performed. So if using less calls, less memory is used and string concatenation is faster.<br>
  48. * <br>
  49. * <h2>Size Hints for Loops</h2><br>
  50. * Bad:
  51. *
  52. * <pre>
  53. * final StringBuilder sbString = new StringBuilder();
  54. * StringUtil.append(sbString, &quot;header start&quot;, someText, &quot;header end&quot;);
  55. * for (int i = 0; i &lt; 50; i++)
  56. * {
  57. * StringUtil.append(sbString, &quot;text 1&quot;, stringArray[i], &quot;text 2&quot;);
  58. * }
  59. * </pre>
  60. *
  61. * Good:
  62. *
  63. * <pre>
  64. * final StringBuilder sbString = StringUtil.startAppend(1300, &quot;header start&quot;, someText, &quot;header end&quot;);
  65. * for (int i = 0; i &lt; 50; i++)
  66. * {
  67. * StringUtil.append(sbString, &quot;text 1&quot;, stringArray[i], &quot;text 2&quot;);
  68. * }
  69. * </pre>
  70. *
  71. * Why?<br/>
  72. * When using StringUtil.append(), memory is only allocated to fit in the strings in method argument. So on each loop new memory for the string has to be allocated and old string has to be copied to the new string. With size hint, even if the size hint is above the needed memory, memory is saved
  73. * because new memory has not to be allocated on each cycle. Also it is much faster if no string copy tasks has to be performed. So if concatenating strings in a loop, count approximately the size and set it as the hint for the string builder size. It's better to make the size hint little bit larger
  74. * rather than smaller.<br/>
  75. * In case there is no text appended before the cycle, just use <code>new
  76. * StringBuilder(1300)</code>.<br>
  77. * <br>
  78. * <h2>Concatenation and Constants</h2><br>
  79. * Bad:
  80. *
  81. * <pre>
  82. * StringUtil.concat(&quot;text 1 &quot;, &quot;text 2&quot;, String.valueOf(npcId));
  83. * </pre>
  84. *
  85. * Good:
  86. *
  87. * <pre>
  88. * StringUtil.concat(&quot;text 1 &quot; + &quot;text 2&quot;, String.valueOf(npcId));
  89. * </pre>
  90. *
  91. * or
  92. *
  93. * <pre>
  94. * StringUtil.concat(&quot;text 1 text 2&quot;, String.valueOf(npcId));
  95. * </pre>
  96. *
  97. * Why?<br/>
  98. * It saves some cycles when determining size of memory that needs to be allocated because less strings are passed to concat() method. But do not use + for concatenation of non-constant strings, that degrades performance and makes extra memory allocations needed.<br>
  99. * <h2>Concatenation and Constant Variables</h2> Bad:
  100. *
  101. * <pre>
  102. * String glue = &quot;some glue&quot;;
  103. * StringUtil.concat(&quot;text 1&quot;, glue, &quot;text 2&quot;, glue, String.valueOf(npcId));
  104. * </pre>
  105. *
  106. * Good:
  107. *
  108. * <pre>
  109. * final String glue = &quot;some glue&quot;;
  110. * StringUtil.concat(&quot;text 1&quot; + glue + &quot;text2&quot; + glue, String.valueOf(npcId));
  111. * </pre>
  112. *
  113. * Why? Because when using <code>final</code> keyword, the <code>glue</code> is marked as constant string and compiler treats it as a constant string so it is able to create string "text1some gluetext2some glue" during the compilation. But this only works in case the value is known at compilation
  114. * time, so this cannot be used for cases like <code>final String objectIdString =
  115. * String.valueOf(getObjectId)</code>.<br>
  116. * <br>
  117. * <h2>StringBuilder Reuse</h2><br>
  118. * Bad:
  119. *
  120. * <pre>
  121. * final StringBuilder sbString1 = new StringBuilder();
  122. * StringUtil.append(sbString1, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
  123. * ... // output of sbString1, it is no more needed
  124. * final StringBuilder sbString2 = new StringBuilder();
  125. * StringUtil.append(sbString2, &quot;text 3&quot;, String.valueOf(npcId), &quot;text 4&quot;);
  126. * </pre>
  127. *
  128. * Good:
  129. *
  130. * <pre>
  131. * final StringBuilder sbString = new StringBuilder();
  132. * StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
  133. * ... // output of sbString, it is no more needed
  134. * sbString.setLength(0);
  135. * StringUtil.append(sbString, &quot;text 3&quot;, String.valueOf(npcId), &quot;text 4&quot;);
  136. * </pre>
  137. *
  138. * Why?</br> In first case, new memory has to be allocated for the second string. In second case already allocated memory is reused, but only in case the new string is not longer than the previously allocated string. Anyway, the second way is better because the string either fits in the memory and
  139. * some memory is saved, or it does not fit in the memory, and in that case it works as in the first case. <h2>Primitives to Strings</h2> To convert primitives to string, use String.valueOf().<br>
  140. * <br>
  141. * <h2>How much faster is it?</h2><br>
  142. * Here are some results of my tests. Count is number of strings concatenated. Don't take the numbers as 100% true as the numbers are affected by other programs running on my computer at the same time. Anyway, from the results it is obvious that using StringBuilder with predefined size is the
  143. * fastest (and also most memory efficient) solution. It is about 5 times faster when concatenating 7 strings, compared to TextBuilder. Also, with more strings concatenated, the difference between StringBuilder and TextBuilder gets larger. In code, there are many cases, where there are concatenated
  144. * 50+ strings so the time saving is even greater.<br>
  145. *
  146. * <pre>
  147. * Count: 2
  148. * TextBuilder: 1893
  149. * TextBuilder with size: 1703
  150. * String: 1033
  151. * StringBuilder: 993
  152. * StringBuilder with size: 1024
  153. * Count: 3
  154. * TextBuilder: 1973
  155. * TextBuilder with size: 1872
  156. * String: 2583
  157. * StringBuilder: 1633
  158. * StringBuilder with size: 1156
  159. * Count: 4
  160. * TextBuilder: 2188
  161. * TextBuilder with size: 2229
  162. * String: 4207
  163. * StringBuilder: 1816
  164. * StringBuilder with size: 1444
  165. * Count: 5
  166. * TextBuilder: 9185
  167. * TextBuilder with size: 9464
  168. * String: 6937
  169. * StringBuilder: 2745
  170. * StringBuilder with size: 1882
  171. * Count: 6
  172. * TextBuilder: 9785
  173. * TextBuilder with size: 10082
  174. * String: 9471
  175. * StringBuilder: 2889
  176. * StringBuilder with size: 1857
  177. * Count: 7
  178. * TextBuilder: 10169
  179. * TextBuilder with size: 10528
  180. * String: 12746
  181. * StringBuilder: 3081
  182. * StringBuilder with size: 2139
  183. * </pre>
  184. * @author fordfrog
  185. */
  186. public final class StringUtil
  187. {
  188. private StringUtil()
  189. {
  190. }
  191. /**
  192. * Concatenates strings.
  193. * @param strings strings to be concatenated
  194. * @return concatenated string
  195. */
  196. public static String concat(final String... strings)
  197. {
  198. final TextBuilder sbString = TextBuilder.newInstance();
  199. for (final String string : strings)
  200. {
  201. sbString.append(string);
  202. }
  203. String result = sbString.toString();
  204. TextBuilder.recycle(sbString);
  205. return result;
  206. }
  207. /**
  208. * Creates new string builder with size initializated to <code>sizeHint</code>, unless total length of strings is greater than <code>sizeHint</code>.
  209. * @param sizeHint hint for string builder size allocation
  210. * @param strings strings to be appended
  211. * @return created string builder
  212. */
  213. public static StringBuilder startAppend(final int sizeHint, final String... strings)
  214. {
  215. final int length = getLength(strings);
  216. final StringBuilder sbString = new StringBuilder(sizeHint > length ? sizeHint : length);
  217. for (final String string : strings)
  218. {
  219. sbString.append(string);
  220. }
  221. return sbString;
  222. }
  223. /**
  224. * Appends strings to existing string builder.
  225. * @param sbString string builder
  226. * @param strings strings to be appended
  227. */
  228. public static void append(final StringBuilder sbString, final String... strings)
  229. {
  230. sbString.ensureCapacity(sbString.length() + getLength(strings));
  231. for (final String string : strings)
  232. {
  233. sbString.append(string);
  234. }
  235. }
  236. public static int getLength(final Iterable<String> strings)
  237. {
  238. int length = 0;
  239. for (final String string : strings)
  240. {
  241. if (string == null)
  242. {
  243. length += 4;
  244. }
  245. else
  246. {
  247. length += string.length();
  248. }
  249. }
  250. return length;
  251. }
  252. /**
  253. * Counts total length of all the strings.
  254. * @param strings array of strings
  255. * @return total length of all the strings
  256. */
  257. public static int getLength(final String[] strings)
  258. {
  259. int length = 0;
  260. for (final String string : strings)
  261. {
  262. if (string == null)
  263. {
  264. length += 4;
  265. }
  266. else
  267. {
  268. length += string.length();
  269. }
  270. }
  271. return length;
  272. }
  273. public static String getTraceString(StackTraceElement[] trace)
  274. {
  275. final TextBuilder sbString = TextBuilder.newInstance();
  276. for (final StackTraceElement element : trace)
  277. {
  278. sbString.append(element.toString()).append(Config.EOL);
  279. }
  280. String result = sbString.toString();
  281. TextBuilder.recycle(sbString);
  282. return result;
  283. }
  284. }