使用Google Guava来编写优雅的代码一字符串处理1

注:本文部分内容来自于Having fun with Guava’s String Helpers 以及Guava官方指南

在我们日常的Java开发中,经常碰到的处理的都是与字符串处理相关的,拼接、分割、替换、字符集、判断和查找等。

首先我们来看实际开发中比较常遇到的两段代码。

下面是字符串拼接的一段处理代码:

public static String toCommaSeparatedString( List<String> strings ) {
    String result = "";
    if( strings != null && !strings.isEmpty() ) {
        StringBuilder stringBuilder = new StringBuilder();
        for( int i = 0; i < strings.size(); i++ ) {
            String string = strings.get( i );
            if( string != null ) {
                stringBuilder.append( string );
                stringBuilder.append( "," );
            }
        }
        stringBuilder.deleteCharAt( stringBuilder.length() - 1 );
        result = stringBuilder.toString();
    }
    return result;
}

还有拆分字符串的一段代码:

public static List<String> fromCommaSeparatedString( String string ) {
    List<String> strings = new ArrayList<String>();
    String[] splitted = string.split( "," );
    for( int i = 0; i < splitted.length; i++ ) {
        String element = splitted[ i ].trim();
        strings.add( element );
    }
    return strings;
}

以上两段经常出现在我们的日常开发的产品中,比如sql语句组装、前端页面传递到后台的组装数据的拆分等。上面的两段本身可以很好的转变为工具类来使用,但是就代码本身来说,维护代码的人可能还是需要阅读代码本身来了解该功能的具体的细节。

如果我们改用Google Guava的话,代码看上去就会很易读了,并且代码行也能得到控制。

public static String toCommaSeparatedString( List<String> strings ) {
    Joiner joiner = Joiner.on( "," ).skipNulls();
    return joiner.join( strings );
}

public static List<String> fromCommaSeparatedString( String string ) {
    Iterable<String> split = Splitter.on( "," ).omitEmptyStrings().trimResults().split( string );
    return Lists.newArrayList( split );
}

代码行从22行减少为9行,并且由于Guava的代码经过了100%的单元测试和Google内部产品的使用,因而我们更容易关注我们自身的业务实现。

上面使用Guava后的两个方法中还是有值得继续探究的地方。

首先让我们来看toCommaSeparatedString方法,其实代码可以直接写成一行 return Joiner.on( "," ).skipNulls().join( strings), 这样就可以比较容易的从方法的字面知道该语句具体做了哪些工作:

  1. on(",") 定义了字符串拼接时使用什么符号进行间隔设定;
  2. skipNulls() 定义了在拼接时跳过空值,即待处理的字符串值如果为null则不会被拼接;
  3. join(strings) 则是将待处理数据

同时以上代码的链式写法也让代码的易读性增加了不少,在以后的介绍Guava关于Function的部分将会继续深入介绍链式写法。

Joiner还提供其它的API,诸如:

  1. useForNull(String nullText) : 用于替换null值的字符串语句;比如sql语句中,我们经常需要将null转换为sql的”,可以使用为useForNull(“”);
  2. withKeyValueSeparator(String keyValueSeparator) : 用于拼接Map中的key与value间的字符串;比如使用Joiner.on("&").withKeyValueSeparator("=").join(hashMaps) 可以得到形如 leton=21&mole=22的结果。
  3. appendTo(*,*) : appendTo方法则提供了向可追加对象后增加数据的做法。诸如可以向已有的StringBuilder对象后追加循环数据等。

下面来看fromCommaSeparatedString方法,分割器Splitter同样也提供了丰富的API来帮助用户实现各种形式的字符串分割方法。我们首先仍然是看工作内容:

  1. on(",") 定义了分割字符串的检查依据。这里除了可以使用字符串还可以使用正则。Splitter还可以将长度对字符串进行分割,比如每隔两个字符进行一次分割,这时可以选择使用fixedLength(2)来实现。
  2. .omitEmptyStrings().trimResults()则用于对分割后的数据进行再处理的过程。

    • .omitEmptyStrings() 用于排除分割后的空字符串数据;比如Splitter.on(',').omitEmptyStrings().split("a,,c,d") returns "a", "c", "d"
    • .trimResults() 用于清除分割后的字符串中的空格;同样还可以使用trimResults(CharMatcher)制定清除符合CharMatcher的字符。
    • 此外还有limit(int limit) 用于限制分割后的数据个数;比如Splitter.on(',').limit(3).split("a,b,c,d") returns "a", "b", "c,d"
  3. split(string) 处理分割数据。

以上两个方法的执行链路顺序都可以视为先定义规则后处理数据,需要注意的事情如果连续两次调用同一方法则只响应后面一个方法。比如.trimResults().trimResults(CharMatcher.is('_')) 则只会执行后面一个方法。

针对Joiner和Splitter的简要介绍就到此结束,如果需要更详细的了解使用方法,可以参考官方手册和API文档。

在下一篇文章里,我们将介绍与字符串处理相关的CharMatcher、Charsets、CaseFormat和Strings类的一些方法。