最近在公司的系统调优中,发现对double类型数据格式化为字符串运算,占用了不少CPU时间(系统为超高并发和超快响应的应用)。
代码中使用的格式化工具为:NumberFormat,一般的使用方式是:
NumberFormat f = NumberFormat.getNumberInstance(); ... f.format(value);
对于double,会用DecimalFormat实例来处理,默认是保留3位小数,且尾零删除(不要求对齐,这样可减少返回字符的长度)。
本来格式化应该不必耗费太多的计算,但由于DecimalFormat要考虑通用性,以及同步处理,因此有较多性能损耗。(具体可参见JDK7的java.text.DecimalFormat的源代码)
因此自己实现一种简单快速的格式工具,优点:
局限性:
格式化举例:
3.145926---->(保留4位小数后的字符)3.1459
3.224450---->(保留4位小数后的字符)3.2245,注:DecimalFormat会格式化为3.2244!
1.999964---->(保留4位小数后的字符)2
0---->(保留4位小数后的字符)0
相关工具:
性能测试结果:
环境:Win7 64位 i5-3320 8G内存 JDK7u40
Precision-0 and Loop times-1000000:
NumberFormat time cost(ms): 884
Fast Format time cost(ms): 188
BigDecimal time cost(ms): 482
Precision-1 and Loop times-1000000:
NumberFormat time cost(ms): 727
Fast Format time cost(ms): 180
BigDecimal time cost(ms): 485
Precision-2 and Loop times-1000000:
NumberFormat time cost(ms): 473
Fast Format time cost(ms): 164
BigDecimal time cost(ms): 516
Precision-3 and Loop times-1000000:
NumberFormat time cost(ms): 509
Fast Format time cost(ms): 157
BigDecimal time cost(ms): 487
Precision-4 and Loop times-1000000:
NumberFormat time cost(ms): 521
Fast Format time cost(ms): 159
BigDecimal time cost(ms): 480
源代码:
private final static char[][] LEADING_DECIMALS = new char[][] { "0.".toCharArray(), "0.0".toCharArray(), "0.00".toCharArray(), "0.000".toCharArray(), "0.0000".toCharArray(), "0.00000".toCharArray(), "0.000000".toCharArray(), "0.0000000".toCharArray(), "0.00000000".toCharArray(), "0.000000000".toCharArray(), "0.0000000000".toCharArray(), "0.00000000000".toCharArray(), "0.000000000000".toCharArray(), "0.0000000000000".toCharArray(), "0.00000000000000".toCharArray(), "0.000000000000000".toCharArray() }; /** * 快速格式化一个double,尾零去除(非对齐)<br> * 等同于:<br> * NumberFormat f = NumberFormat.getNumberInstance();<br> * f.setGroupingUsed(false);<br> * f.setMaximumFractionDigits(precision);<br> * f.format(d);<br> * 但一般情况效率高于NumberFormat一倍,且精度无丢失。<br> * * @param d * the double value * @param precision * [0,16] * @return * @see NumberFormat */ public static String fastFormat(double d, int precision) { int posPrecision = Math.abs(precision); double roundUpVal = Math.abs(d) * Math.pow(10d, posPrecision) + 0.5d; if (roundUpVal > 999999999999999d || posPrecision > 16) {// double has max 16 precisions return bigDecFormat(d, posPrecision); } long longPart = (long) Math.nextUp(roundUpVal); if (longPart < 1) { return "0"; } char[] longPartChars = Long.toString(longPart).toCharArray(); char[] formatChars = null; if (longPartChars.length > posPrecision) { int end = longPartChars.length - 1; int decIndex = longPartChars.length - posPrecision; while (end >= decIndex && longPartChars[end] == '0') { end--; } if (end >= decIndex) { formatChars = new char[end + 2]; System.arraycopy(longPartChars, 0, formatChars, 0, decIndex); formatChars[decIndex] = '.'; System.arraycopy(longPartChars, decIndex, formatChars, decIndex + 1, end - decIndex + 1); } else { formatChars = new char[decIndex]; System.arraycopy(longPartChars, 0, formatChars, 0, decIndex); } } else { int end = longPartChars.length - 1; while (end >= 0 && longPartChars[end] == '0') { end--; } char[] leadings = LEADING_DECIMALS[posPrecision - longPartChars.length]; formatChars = Arrays.copyOf(leadings, leadings.length + end + 1); System.arraycopy(longPartChars, 0, formatChars, leadings.length, end + 1); } return Math.signum(d) > 0 ? new String(formatChars) : "-" + new String(formatChars); } private static String bigDecFormat(double d, int precision) { String formatStr = new BigDecimal(Double.toString(d)).setScale(Math.abs(precision), RoundingMode.HALF_UP) .toString(); if (precision == 0) { return formatStr; } int end = formatStr.length() - 1; while (end >= 0 && formatStr.charAt(end) == '0') { end--; } formatStr = formatStr.substring(0, end + 1); if (formatStr.charAt(formatStr.length() - 1) == '.') { formatStr = formatStr.substring(0, formatStr.length() - 1); } return formatStr; }