不管你更喜欢使用哪种语言,程序员都必须和字符串打交道,有时候这是无趣的并且容易出错的。 有的时候,我们需要从文件或者数据库的表中读取数据,并格式化这些数据。 或者为了满足业务需求我们需要排序等操作,幸运的是,Guava给我们提供一些非常有用的类,可以帮助我们处理Strings类更加容易.这些类是:
CharMatcherCharsetsStrings下面让我们看一下在我们的代码中怎么样是使用这些类。在Java平台下有个6个标准的字符集,下面是和字符集相关的非常常见的例子:byte[] bytes = someString.getBytes();但是上面的这个代码片段是有问题的,在获取byte的时候没有指定字符集编码,那么默认就会去系统的默认编码,但是有可能系统默认的编码不是你想要的编码。因此一般的最佳实践是按照如下的方式获取byte数据:`try{bytes = "foobarbaz".getBytes("UTF-8");}catch (UnsupportedEncodingException e){//This really can't happen UTF-8 must be supported}`上面的例子中依然有两个错误的地方:
UTF-8 在Java平台上是肯定会被支持的,所以基本上是不可能会抛出UnsupportedEncodingException异常的。我们是用'UTF-8'这种方式指定编码的,有可能会出现输入错误的情况,例如不小心输入成'UF-8'.上面的这两个问题正是CharSets可以解决。 Charsets类 对于这6个字符集提供了一个 final的静态的引用。 使用Charsets,上面的那段代码可以简化为如下:byte[] bytes2 = "foobarbaz".getBytes(Charsets.UTF_8);不过在Java7中已经提供了类似的功能,在Java 7中的类名是: StandardCharsets.
Strings提供了一些工具方法区处理字符串,你是否写过下面的代码:`StringBuilder builder = new StringBuilder("foo");char c = 'x';for(int i=0; i<3; i++){builder.append(c);}return builder.toString();`在上面的这段代码中,我们使用了6行代码完成了这么一个简单的功能,现在利用Strings 我们可以简单的用一行代码就可以完成:Strings.padEnd("foo",6,'x');这里面有个重要的提示,这里面的6不是指将x循环的增加6遍,而是指将'foo'补充成6位长度。 如果这时候 'fooxxxx'已经是6位长度了,那么这段代码将不起任何作用. 同样 有一个'padStart'方法,它的作用和'padEnd'是相似的,只不过是在字符串前面补充指定的字符。补充到指定的长度。
在处理null值方面,Strings有三个比较好用的方法:
nullToEmpty 这个方法获取一个字符串参数,如果这个参数不为null或长度大于0,那么直接返回该参数,否则返回"" 空字符串emptyToNull 这个方法和nulltoEmpty差不多,只不过如果参数是null或则长度不大于0 那么会返回 nullisNullOrEmpty 当传入的参数书null或则长度为0 那么这个方法的返回值就是 true。最佳实践: 在讲String作为参数传递时,最好先使用nullToEmpty对字符串做一下处理。
Charmatcher 类提供了处理各种characters的方法,能够非常容易的格式化处理文本。 下面的例子展示的是,使用CharMatcher将一个多行的字符串转换成以' '分隔的单行字符串CharMatcher.BREAKING_WHITESPACE.replaceFrom(stringWithLinebreaks,' ');replaceFrom方法不仅可以接受一个char类型参数还可以接受一个charsequence参数。
为了将连续的多个 tab或者空格 压缩成一个空格,我们可以使用下面的代码:`@Testpublic void testRemoveWhiteSpace(){String tabsAndSpaces = "String with spaces andtabs";String expected = "String with spaces and tabs";String scrubbed = CharMatcher.WHITESPACE.collapseFrom(tabsAndSpaces,' ');assertThat(scrubbed,is(expected));}`
上面的test中,我们看到我用一行代码就将多个tab及多个空变成一个空格。 但是上面的这个例子只是在特殊的情况下有效,如果我们想将字符串前面的空格都去掉,只保留字符中间的一个空格呢? 尽管需求这么变态,使用 trimAndCollapseFrom方法就可以实现,示例如下:`@Testpublic void testTrimRemoveWhiteSpace(){String tabsAndSpaces = " String with spaces andtabs";String expected = "String with spaces and tabs";String scrubbed = CharMatcher.WHITESPACE.trimAndCollapseFrom(tabsAndSpaces,' ');assertThat(scrubbed,is(expected));}`
上面的例子我们依然讨论了如何将多个tab或空格变成一个空格,现在让我们来看一下其他的使用方式,
将符合条件的字符提取出来,example:`@Testpublic void testRetainFrom(){String lettersAndNumbers = ""foo989yxbar234"";String expected = ""989234"";String retained = CharMatcher.JAVA_DIGIT.retainFrom(lettersAndNumbers);assertThat(expected,is(retained));}`上面的例子中,我们将所有的数字都提取了出来,仅仅是使用了一行代码哦!
在讲其他的类的使用之前,我们来看一个大招,example:`String lettersAndNumbers = "fo o989yxbar234";String expected = " 989234"; CharMatcher.JAVA_DIGIT.or(CharMatcher.WHITESPACE).retainFrom(lettersAndNumbers);`
上面的例子中既可以提取数字也可以提取空白字符
由此可见,CharMatcher在处理Java字符串方面是非常好用并且非常好用的方法。
Preconditions类里面包含了一系列的静态方法,用来验证我们期望的值。 Preconfitions 是非常重要,因为他可以保证我们的预期和实际发生的是一致的。 一旦预期和实际运行的记过不一致,我们立马就可以得到反馈,因此使用preconditions 可以确保我们的代码在我们预期的情况下运行着。 另外对于debug也是非常有好处的。
在使用preconfitions 之前我们一定写过下面的代码:`if(someObj == null){throw new IllegalArgumentException(" someObj mustnot be null");}`使用preconfitions 后,我们可以如下的方法进行检查:checkNotNull(someObj,"someObj must not be null");其中preconfitions 是可以静态导入的 static imports下面我们来看一个例子:`public class PreconditionExample {private String label;private int[] values = new int[5];private int currentIndex;public PreconditionExample(String label) {//returns value of object if not nullthis.label = checkNotNull(label,"Label can''t be null");}public void updateCurrentIndexValue(int index, int valueToSet) {//Check index valid firstthis.currentIndex = checkElementIndex(index, values.length,"Index out of bounds for values");//Validate valueToSetcheckArgument(valueToSet <= 100,"Value can't be more than100");values[this.currentIndex] = valueToSet;}public void doOperation(){checkState(validateObjectState(),"Can't perform operation");}private boolean validateObjectState(){return this.label.equalsIgnoreCase("open") && values[this.currentIndex]==10;}}`从上面的例子中,我们总结一下这个几个方法的使用:
checkNotNull(T object,Object message) : 如果object是null 那么就会抛出一个NullPointException异常,否则返回这个Object.checkElementIndex(int index,int size,Object message): 这个方法中 index 参数是你想要访问的位置,size 是array,list,或者string的长度, 如果 index的范围大于size 那么将会抛出 IndexOutOfBoundexception 否则返回index的值checkArgument(Boolean expression,Object message): 根据expression表达式算出来的值做判断,如果值为false那么就会抛出IllegalArgumentException异常checkState(Boolean expression,Object message): 作用和checkArgument一样,代码实现也是一样,使用场景不一致,checkArgument 一般用来检查输入参数这一个小章节中,我们将覆盖 空值检查,生成 toString,hashCode 等方法。 我们还将学习一个新的类帮助我们从实现Comparable接口痛苦摆脱出来。
toString 方法在我们debug或者查日志的时候是十分方便的,但是写一个toString方法却是一个比较无趣的事情,但是使用Objects的toStringHelper方法你可以很方便的实现,下面我们看一个例子:`public class Book implements Comparable {private Person author;private String title;private String publisher;private String isbn;private double price;....public String toString() {return Objects.toStringHelper(this).omitNullValues().add("title", title).add("author", author).add("publisher", publisher).add("price",price).add("isbn", isbn).toString();}`让我们来探究一下这段代码的意思
首先我们传递一个Book的引用,创建一个ToStringHelper对象调用omitNullValues 排除属性是空值的分别调用 add 方法将 'label' 和对应的值不过貌似这个还没有 apache 下面的 ToStringBuilder好用
firstNonNull 方法接受两个参数,如果参数是空,就返回设置的默认值,否则指定的第一次参数,如果传入的两个参数都是null 那么就会抛出NullPointException异常
方法很简单,如下:`Objects.hashCode(Object...);`
我们任然使用上面的Book类,下面是一个实现CompareTo接口典型实现:`public int compareTo(Book o) {int result = this.title.compareTo(o.getTitle());if (result != 0) {return result;}result = this.author.compareTo(o.getAuthor());if (result != 0) {return result;}result = this.publisher.compareTo(o.getPublisher());if(result !=0 ) {return result;}return this.isbn.compareTo(o.getIsbn());}`
下面我们来看一下使用ComparisonChain的方式实现compareTo接口:`public int compareTo(Book o) {return ComparisonChain.start().compare(this.title, o.getTitle()).compare(this.author, o.getAuthor()).compare(this.publisher, o.getPublisher()).compare(this.isbn, o.getIsbn()).compare(this.price, o.getPrice()).result();}`上面的这个例子中,使用ComparisonChain的方式更加简单,也更加易读,只要其中的一个比较不为0,那么就直接返回。
在这一章中我们已经介绍了很多。
我们学习了怎样使用Joiner,Splitter,MapJioner,MapSplitter,Charsets,CharMatcher,Strings 让我们在处理格式化的字符串时更加容易我们学习了使用Preconfitions类让我们的代码更加强壮,学习使用了 Objects类的 toString,hashCode 方法让我们的debug更加容易,我们还学习了ComparisonChain类,让我们实现CompareTo方式更加容易在下一章,我们将学习怎样使用guava的函数式变成,让我们的代码更加清晰和强壮。我们将涉及两个接口: Function 和 Predicate 接口