字符串切分工具比较

在日常开发中,我们经常需要处理字符串。字符串处理通常有两个主要的场景:一个是字符串的格式化,通常我们通过String.format方法或者构建一个StringBuilder,然后通过append方法来完成;另一个是字符串的切分。在这篇文章中,通过String.split、Apache Commons中的StringUtils.split和Google工具包Gauva中提供的Splitter三个字符串切分工具的测试来体会一下三者在使用方法和性能上的差异。

在进行实际讨论之前,我们先花一些小的篇幅介绍一下Google的Gauva工具包。实际上,Guava几乎可以看成是一个全新的用更“时髦”(更符合现代编程理念)的Apache Commons的替代品。最新的Guava版本提供了集合、并发、基本类型操作、反射、比较、I/O、哈希、网络通讯、字符串处理、数学处理、基于内存的缓存、基于内存的发布/订阅等等一个庞大而全面的数据访问和处理工具库。

好了,下面就让我们看看在字符串切分上不同的工具会给我们带来什么样的惊喜吧。

我们知道,String类本身实际上已经提供了一个split的方法用来实现字符串的切分。比如,如果我们想把字符串“I am from Newtouch”分成I,am,from,Newtouch四个独立的字符串时,通过以下代码就可以简单的实现。

  final String message = "I am from Newtouch";
String[] items = message.split(" ");

如果仔细观察,我们会发现,在按空格切分字符串时,虽然空格(‘ ’)只是一个字符,但是仍需要传入一个字符串作为切分匹配的参数。其实,split方法的参数不仅仅只是一个简单的字符串,而且还支持正则表达式的所有语义。仍然以上例为例,如果在两个单词之间(如:I和am)存在两个或者三个以上的空格,我们仍然希望切分结果是I,am,from,Newtouch四个独立的字符串的话,只需按照正则表达式的语法稍微修改一下split的参数。代码如下:

  final String message = "I am from Newtouch";
String[] items = message.split(" +");

通过这个简单的小例子,我们可以体会到正则表达式的威力。但是,在一些对性能要求非常高的场合,当只需要按照单个字符进行切分时,JDK这种支持正则表达式的切分方法无疑又会成为一种“鸡肋”;而另一种情况则正好相反,对于复杂的切分方式,开发人员很多时候更希望采用一种比正则表达式更好用、也更直白、更易于理解和掌握的方式来书写代码。还好,很多工具包都提供了基单个字符的切分方法,如本文重点讨论的Apache Commons中的StringUtils和Google的Guava工具包提供的Splitter。StringUtils和Splitter使用起来都非常简单。如下所示:
  
  //Apache commons-lang StringUtils.split
final String message = "I am from Newtouch";
String[] items = StringUtils.split(message,' ');
  


  //Google Guava Splitter
final String message = "I am from Newtouch";
  Iterator<String> items = Splitter.on(' ').split(message).iterator();
  
那么,我们在处理一个字符串切分问题时,究竟选择哪种切分工具更好呢?要了解这个问题,我们先看看下面这个性能测试。

public class StringSplitPerformanceTest
{
//测试循环次数
static int testDataCount = 5000;
//测试字符串的长度
static int testDataStringSize = 100;
//测试数据集
static List<String> testData = new LinkedList<String>();
//测试结果记录
static StopWatch stopWatch = new StopWatch(String.format("Test the performance of splitting a string which size is %d, %d times.",testDataStringSize,testDataCount));

//创建测试数据
@BeforeClass
public static void generateTestData() {
for(int i=0; i < testDataCount; i++){
String data = RandomStringUtils
.random(testDataStringSize, "ABCDEFGHIJKLMNOPQRSTUVXYZ,");
testData.add(data);
}
}

//输出测试结果
@AfterClass
public static void printTestSummery(){
System.out.println(stopWatch.prettyPrint());
}

//测试Apache commons-lang的StringUtils.split
@Test
public void apacheCommonLangSplit() throws Exception {
stopWatch.start("Apache Common Lang Split");
for(String data: testData){
String[] elements = StringUtils.split(data, ',');
for (String element : elements) {
assertTrue(element.length() <= testDataStringSize);
}
}
stopWatch.stop();
}

//测试Goolge Guava的Splitter
@Test
public void guavaSplitterSplit() throws Exception {
Splitter spiltter = Splitter.on(',');
stopWatch.start("Google Guava Splitter");
for(String data: testData){
Iterable<String> elements = spiltter.split(data);
for (String element : elements) {
assertTrue(element.length() <= testDataStringSize);
}
}
stopWatch.stop();
}

//测试String自身的split方法
@Test
public void jdkStringSplit() throws Exception {
stopWatch.start("JDK String Split");
for(String data: testData){
String[] elements = data.split(",");
for (String element : elements) {
assertTrue(element.length() <= testDataStringSize);
}
}
stopWatch.stop();
}
}

在这个单元测试中,分别使用String.split、Apache commons-lang中的StringUtils.split和Google Guava的Splitter类对随机产生的长度为100的字符串进行5000次切分,并使用了Spring的StopWatch来收集和统计测试结果。三次测试结果如下:

第一次:
StopWatch 'Test the performance of splitting a string which size is 100, 5000 times.': running time (millis) = 239
-----------------------------------------
ms % Task name
-----------------------------------------
00037 015% Apache Common Lang Split
00042 018% Google Guava Splitter
00160 067% JDK String Split

第二次:
StopWatch 'Test the performance of splitting a string which size is 100, 5000 times.': running time (millis) = 394
-----------------------------------------
ms % Task name
-----------------------------------------
00053 013% Apache Common Lang Split
00094 024% Google Guava Splitter
00247 063% JDK String Split

第三次:
StopWatch 'Test the performance of splitting a string which size is 100, 5000 times.': running time (millis) = 317
-----------------------------------------
ms % Task name
-----------------------------------------
00039 012% Apache Common Lang Split
00045 014% Google Guava Splitter
00233 074% JDK String Split

从测试结果中我们发现,JDK自身的split方法性能上远不如其他两个工具;而在需要进行切分的字符串长度比较短(如本测试中的字符串长度为100)的情况下,Apache commons-lang的StringUtils.split的切分效率最高;Google Guava的Splitter在性能上和StringUtils有1倍左右的差距。

本文的最后,我们再讨论一下Splitter的适用场景。有时,我们需要切分的字符串往往不是特别规整。比如我们希望把字符串“I am from Newtouch.”切分为“I”,“am”,“from”,“Newtouch”四个单元时,使用正则表达式对开发人员来说就是一个不小的挑战,而且通过上面的测试我们知道正则表达式的处理效率来说并不是很好。使用Splitter可以很灵活的处理了这些问题。

  @Test
public void testGoogleGuavaSplitter() {
//Google Guava Splitter
final String message = "I am from Newtouch.";
Iterator<String> items = Splitter.on(' ') //按空格切分
.trimResults(CharMatcher.is('.')) //去掉结尾的.
.omitEmptyStrings() //去掉多余的空字符串
.split(message).iterator();

assertEquals("I", items.next());
assertEquals("am", items.next());
assertEquals("from", items.next());
assertEquals("Newtouch", items.next());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值