注:本文中使用JDK1.6版本。
Java从一开始就提供了丰富的类库,这也是其受到广泛使用的一个重要原因。一般来说,类库中的方法经过多年的实践,也的确有很多值得学习的地方,但是针对具体的应用而言,类库的方法真的是最优的吗?以String为例,String是Java中最重要的几个类之一,提供了数十个方法,其中的一个格式化方法是:
public static String format(String format, Object ... args)
此方法提供了丰富的使用方法,如格式化为指定进制的数据,千分位分隔等,以及我们最常用的占位符替换的方法:
String.format(“This is my %s”, “house”);
正因为format方法提供了丰富的功能,使得其牺牲了一些性能上的要求,在一些要求极致性能且需求较为简单的地方就并不适合直接使用。下面的例子将通过比较自定义的format方法和类库中的format方法来对二者的性能作一个对比:
/**
*===============================================================
* @author: jiehao
* @date: 2016年6月5日 上午11:48:51
*===============================================================
* 修订日期 修订人 描述
*/
package com.zjh.importnew.articles;
import org.junit.Test;
/**
* <p>测试String的类库方法和自己简单实现方法的性能差异
* @author jiehao
* @date 2016年6月5日 上午11:48:51
* @see
* @since V1.0
* @modified TODO
*/
public class StringTest {
/**
* <p>自定义format方法
* <p>仅针对%s这种占位符作替换
* @param strSource 原始字符串
* @param strFrom 待替换的不定字符数组
* @return 替换后的字符串
* @author jiehao
* @date 2016年6月5日 上午11:50:11
* @since V1.0
* @see
* @modified TODO
*/
public static String format(String strSource, String... strFrom) {
if (strSource == null || strFrom == null) {
return strSource;
}
int i = 0;<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
StringBuffer buffer = new StringBuffer();
int k=0;
while ((i = strSource.indexOf("%s")) >= 0) {
buffer.append(strSource.substring(0, i));
buffer.append(strFrom[k++]);
strSource = strSource.substring(i + 2, strSource.length());
}
buffer.append(strSource);
return buffer.toString();
}
/**
* <p>单元测试
* <p>由于机器、环境、编译优化等差异,测试效果略有差异
* @author jiehao
* @date 2016年6月5日 上午11:58:19
* @since V1.0
* @see
* @modified TODO
*/
@Test
public void testCompareFormat(){
String source = "StringTest replace 1st value= %s, 2nd value = %s.";
String firstValue = "testCompareFormat1";
String secondValue = "testCompareFormat2";
int loopNum = 1000000;
long startTime1 = System.currentTimeMillis();
String afterLib = null;
for (int i = 0; i < loopNum; i++) {
afterLib = String.format(source, firstValue, secondValue);
}
long endTime1 = System.currentTimeMillis();
System.out.println("source = " + source + ", after format in lib = " + afterLib + ", time = " + (endTime1 - startTime1) + "ms");
long startTime2 = System.currentTimeMillis();
String afterMySelf = null;
for (int i = 0; i < loopNum; i++) {
afterMySelf = format(source, firstValue, secondValue);
}
long endTime2 = System.currentTimeMillis();
System.out.println("source = " + source + ", after format in myself = " + afterMySelf + ", time = " + (endTime2 - startTime2) + "ms");
}
}<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
测试结果表明:如果只是要求替换字符串中的”%s”占位符这种应用,自定义的接口方法甚至比format快上五六倍以上。笔者曾经的系统中就遇到过每天数十亿级的替换,每天累积下来的时间并不少,此时用自带的String.format方法显然并非最优方案。另一方面,为了实现类库中format丰富的功能,其内部是由一个复杂的正则表达式去匹配的,在并发时会大量消耗CPU资源,对服务器性能造成一定的影响。
小结一下:
1、Java某些类库(包括一些广泛应用的第三方jar)并非十全十美,如广受诟病的Date和Calander(好消息是,这两个类在JDK1.8中终于重新实现了),所以针对高性能的的场景,不要过度迷信类库的方法,类库方法未必是最优方案,需要具体问题具体解决。
2、但在绝大多数情况下,类库方法都更加灵活和强大,依然是我们的首选。这种首选不仅仅体现在性能(大部分类库方法的性能都很好)和使用范围上,而且能减少项目的维护成本,享受版本更替时类库无形之间带来的改进。正如Joshua Bloch在《Effective Java》中指出的那样需要“谨慎地进行优化”,除非能带来明确地提升,否则类库依然是第一选择。