基本Guava工具

 
使用Joiner类
将任意字符串经过分隔符进行链接到一块儿是大多程序员常常作的事情。他们常用array,list,iterable而且循环变量将每个临时变量添加到StringBuilder当中去,而且中间添加分隔符。这些笨重的处理方式以下:
public String buildString(List<String> stringList, String delimiter){
  StringBuilder builder = new StringBuilder();
  for (String s : stringList) {
    if(s !=null){
      builder.append(s).append(delimiter);
    }
  }
  builder.setLength(builder.length() – delimiter.length());
  return builder.toString();
}
注意要删除在最后面的分隔符。不是很难懂,可是使用Joiner类能够获得简单的代码模板。一样的例子使用Joiner类代码以下:
public String buildString(List<String> stringList, String delimiter){ 
       return  Joiner.on(delimiter).skipNulls().join(stringList);
}
这样更加简明而且不会出错。若是你想将null值替换掉,可使用以下方法:
Joiner.on("|").useForNull("no value").join(stringList);
使用Joiner类有几点须要注意。Joiner类不只仅能够处理字符串的array、list、iterable,他还能够处理任何对象的array、list、iterable。结果就是调用每个元素的toString()方法。所以,若是没有使用skipNulls或者useForNull,就会抛出空指针异常。Joiner对象一旦被建立就是不可变的,因此他们是线程安全的,能够被看成常亮来看待。而后看看下面的代码片断:
  
Joiner stringJoiner = Joiner.on("|").skipNulls();
  //使用useForNull方法将会返回一个新的Joiner实例
  stringJoiner.useForNull("missing");
  stringJoiner.join("foo","bar",null);
在上面的代码实例当中,useForNull方法并无起做用,null值仍然被忽略了。
Joiner不只仅能返回字符串,还能够与StringBuilder一块儿使用:
StringBuilder stringBuilder = new StringBuilder();
Joiner joiner = Joiner.on("|").skipNulls();
//返回的StringBuilder实例当中包含链接完成的字符串
joiner.appendTo(stringBuilder,"foo","bar","baz");
上面的例子,咱们传入一个StringBuilder的参数而且返回一个StringBuilder实例。
Joiner类也可使用实现了Appendable接口的类。
FileWriter fileWriter = new FileWriter(new File("path")):
List<Date> dateList = getDates();
Joiner joiner = Joiner.on("#").useForNulls(" ");
// 返回由字符串拼接后的FileWriter实例 
joiner.appendTo(fileWriter,dateList);
这是一个与上一个类似的例子。咱们传入一个FileWriter实例和一组数据,就会将这组数据拼接后附加到FileWriter当中而且返回。
咱们能够看到,Joiner使一些公共的操做变的很是简单。有一个特殊的方法会实现MapJoiner方法,MapJoiner方法像Joiner同样使用分割符将每组key与value分开,同时key与value之间也有个分隔符。MapJoiner方法的建立以下:
MapJoiner mapJoiner = Joiner.on("#").withKeyValueSeparator("=");
快速回顾一下上面内容:
  • Joiner.on("#")方法会建立一个Joiner的实例。
  • 使用返回的Joiner实例调用withKeyValueSeparator方法将会返回MapJoiner对象。
下面是对MapJoiner方法的单元测试代码:
@Test
public void testMapJoiner() {
  String expectedString = "Washington D.C=Redskins#New York City=Giants#Philadelphia=Eagles#Dallas=Cowboys"; 
  Map<String,String> testMap = Maps.newLinkedHashMap();
  testMap.put("Washington D.C","Redskins");
  testMap.put("New York City","Giants");
  testMap.put("Philadelphia","Eagles");
  testMap.put("Dallas","Cowboys");
  String returnedString = Joiner.on("#"). withKeyValueSeparator("=").join(testMap); 
  assertThat(returnedString,is(expectedString));
}
回顾时间
上面的单元测试开始时建立一个key和value都是字符串的LinkedHashMap实例,值得注意的是咱们使用静态工厂方法newLinkedHashMap()来建立,Maps类是在com.google.common. collect包当中。而后使用Joiner类建立一个使用key与value拼接的字符串。最后咱们断言他是否与咱们指望返回的字符串相同。
 
使用Splitter类
程序员另外一个常常处理的问题是对字符串以特定分隔符进行分割而且获取一个字符串数组。若是你须要读取文本文件,你老是会作这样的事情。可是
String.split方法不够完美,看下面的例子:
String testString = "Monday,Tuesday,,Thursday,Friday,,";
//parts is [Monday, Tuesday, , Thursday,Friday]
String[] parts = testString.split(",");
能够看到,String.split方法省略了最后的2个空串。在有些时候,这个作法是你须要的,可是这些事情是应该由程序员来决定是否省略。Splitter类能够帮助咱们实现与Joiner类相反的功能。Splitter可使用单个字符、固定字符串、正则表达式串、正则表达式对象或者CharMatcher对象(另外一个Guava的类,本章会讲到)来分割字符串。能够给定具体分割符来建立Splitter对象而后使用。一旦拥有了Splitter实例后就能够调用split方法,而且会返回包含分割后字符串的迭代器对象。
Splitter.on('|').split("foo|bar|baz");
Splitter splitter = Splitter.on("\\d+");
在上面的例子当中,咱们看到一个Splitter 实例使用了'|'字符分割,另一个实例使用了正则表达式进行分割。
Splitter有一个能够处理前面空格和后面空格的方法是trimResults。
//Splits on '|' and removes any leading or trailing whitespace
Splitter splitter = Splitter.on('|').trimResults();
与Joiner类同样Splitter类一样是一个不可变的类,因此在使用的时候应该使用调用trimResults方法后返回的Splitter实例。
Splitter splitter = Splitter.on('|');
//Next call returns a new instance, does not
modify the original!
splitter.trimResults();
//Result would still contain empty elements
Iterable<String> parts = splitter.split("1|2|3|||");
Splitter 类,像Joiner与MapJoiner同样也有MapSplitter类。MapSplitter类能够将字符串转换成Map实例返回,而且元素的顺序与字符串给定的顺序相同。使用下面方法构造一个MapSplitter实例:
//MapSplitter is defined as an inner class of Splitter
Splitter.MapSplitter mapSplitter = Splitter.on("#"). withKeyValueSeparator("=");
能够看到MapSplitter的建立方式与MapJoiner同样。首先给Splitter指定一个分隔符,而后指定MapSplitter对象key与value的分隔符。下面是一个关于MapSplitter的例子,实现的是与MapJoiner相反地功能。
@Test
public void testSplitter() {
  String startString = "Washington D.C=Redskins#New York City=Giants#Philadelphia=Eagles#Dallas=Cowboys"; 
  Map<String,String> testMap = Maps.newLinkedHashMap();
  testMap.put("Washington D.C","Redskins");
  testMap.put("New York City","Giants");
  testMap.put("Philadelphia","Eagles");
  testMap.put("Dallas","Cowboys");
  Splitter.MapSplitter mapSplitter = Splitter.on("#").withKeyValueSeparator("="); 
  Map<String,String> splitMap = mapSplitter.split(startSring); 
  assertThat(testMap,is(splitMap));
}
 
回顾时间
上面的单元测试是将一个字符串用MapSplitter分割成一个LinkedHashMap的实例。而后断言这个返回的LinkedHashMap实例是否与咱们指望是LinkedHashMap实例相同。
Joiners 和Splitters 这两个类应该是每一个java开发人员的工具。
 
使用Guava操做字符串
不管你喜欢的哪一种语言,全部跟字符串相关的事情都是无聊乏味的而且容易出错。有时候,咱们须要从文件或者数据当中读取数据而且从新格式化这些数据提交给用户,或者以固定的格式来保存这些数据。幸运的是,Guava提供给咱们很是好用的类使咱们操做字符串更加轻松。这些类以下:
  • CharMatcher
  • Charsets
  • Strings
如今咱们看一看如何在代码中使用它们。
在第一个例子当中,这个单元测试将会展现使用ASCII类方法来肯定一个字符是不是小写。第二个例子将展现将小写字符串转换成大写。
 
使用Charsets类
在java当中,有6个标准字符集在每个java平台都会被支持。这与常常运行下面代码是相关的:
byte[] bytes = someString.getBytes();
可是有一个问题关于上面的代码。没有指定你想返回字节的字符集,你将会得到系统使用运行时默认的字符集返回的字节,这可能会产生问题有能够默认字符集不是你想要的。因此最佳的作法是像下面这样:
try{
  bytes = "foobarbaz".getBytes("UTF-8");
}catch (UnsupportedEncodingException e){
  //This really can't happen UTF-8 must be supported
}
可是仍然有两个问题在上面的代码当中:
  • UTF-8在java平台 必定会被支持,因此UnsupportedEncodingException必定不会被抛出
  • 一旦咱们使用字符串指定字符集的定义,咱们能够产生拼写错误而后致使异常抛出。
Charsets类能够帮助咱们,Charsets类提供了static final 的六个java平台支持的字符集。使用Charsets类咱们可使上面的例子更简单些:
byte[] bytes2 = "foobarbaz".getBytes(Charsets.UTF_8);
值得注意的是在JAVA7当中,StandardCharsets也提供了一样的功能。如今咱们看看Strings类。
 
使用Strings类
Strings类提供一些便利实用的方法处理字符串。你是否写过像下面的代码:
StringBuilder builder = new StringBuilder("foo");
  char c = 'x';
  for(int i=0; i<3; i++){
    builder.append(c);
  }
return builder.toString();
上面例子当中的6行代码咱们可使用一行代码来替换。
Strings.padEnd("foo",6,'x');
第二个参数是很重要的,6指定返回的字符串最小长度为6,而不是指定'x'字符在字符串后面追加多少次。若是提供的字符串长度已经大于了6,则不会进行追加。一样也有一个相相似的padStart方法能够在给定字符串的前面追加字符到指定的长度。
在Strings类当中有三个很是有用的方法来处理空值的:
  • nullToEmpty:这个方法接受一个字符串参数,若是传入的参数不是null值或者长度大于0则原样返回,不然返回空串("");
  • emptyToNull:这个方法相似于nullToEmpty,它将返回null值若是传入的参数是空串或者null。
  • isNullOrEmpty:这个方法会检查传入参数是否为null和长度,若是是null和长度为0就返回true。
或许,这将是一个不错的主意老是在任何使用nullToEmpty方法处理传入的字符串参数。
 
使用CharMatcher类
CharMatcher提供了在必定字符串中匹配是否存在特定字符串的功能。在CharMatcher类当中的方法也可让格式化更加简单。例如,你能够将多行的字符串格式化成一行,而且换行符将会以空格来代替。
CharMatcher.BREAKING_WHITESPACE.replaceFrom(stringWithLinebreaks,' ');
还有一个版本replaceFrom的,须要一个CharSequence的值做为第2个参数值,而不是一个单一的字符。
移除多个空格和tab以单个空格来代替,代码以下:
@Test
public void testRemoveWhiteSpace(){
  String tabsAndSpaces = "String with spaces and         tabs"; 
  String expected = "String with spaces and tabs";
  String scrubbed = CharMatcher.WHITESPACE.collapseFrom(tabsAndSpaces,' ');
  assertThat(scrubbed,is(expected));
}
在上面的测试代码中,咱们把全部多个空格和tab都替换成了一个空格,全部都在一行上。
上面例子在某些状况下可行,可是若是字符串在开头就有空格咱们想移除怎么办?返回的字符串当中前面依然会包含空格,这时可使用trimAndCollapseFrom方法:
@Test
public void testTrimRemoveWhiteSpace(){
  String tabsAndSpaces = "   String with spaces and       tabs"; 
  String expected = "String with spaces and tabs";
  String scrubbed = CharMatcher.WHITESPACE. trimAndCollapseFrom(tabsAndSpaces,' '); 
  assertThat(scrubbed,is(expected));
}
在这个测试当中,咱们再一次将把多个空格和tab替换成一个空格也在一行上。
列出全部在CharMatcher类可用的方法是不切实际的,这里是否是更换一组匹配字符的地方,咱们保留所匹配的字符的例子: 
@Test
    public void retainFromTest()
    {
        String lettersAndNumbers = "foo989yxbar234";
        String expected = "989234";
        String actual = CharMatcher.JAVA_DIGIT.retainFrom(lettersAndNumbers);
        assertEquals(expected, actual);
    }
在这个例子当中咱们找到"foo989yxbar234"字符串当中因此的数字而且保留下来。
往下继续以前,咱们应该看看最后一个CharMatcher类中的强大功能。能够联合多个CharMatcher类实例建立一个新的CharMatcher类实例。
假设你须要创一个匹配数字和空格的CharMatcher类实例:
CharMatcher cm = CharMatcher.JAVA_DIGIT.or(CharMatcher.WHITESPACE);
它如今将会匹配任何数字或者空格。CharMatcher类对于操做字符串来讲功能强大而且很好用。
 
使用Preconditions类
Preconditions类是用来验证咱们的代码状态的静态方法的集合。 Preconditions很是重要,由于他们保证咱们的指望执行成功的代码获得知足。 若是条件与咱们指望的不一样,咱们会及时反馈问题。和之前同样,使用前提条件的重要性是确保咱们代码的行为,并在调试中颇有用。
你能够写你本身的先决条件,像下面这样:
if(someObj == null){
    throw new IllegalArgumentException(" someObj must not be null"); 
}
使用Preconditions当中的方法(须要静态导入),检查一个空值更简单。
checkNotNull(someObj,"someObj must not be null");
接下来,咱们将要展现使用先决条件的例子:
public class PreconditionExample {
  private String label;
  private int[] values = new int[5];
  private int currentIndex;
  public PreconditionExample(String label) {
    //返回label若是不为空
    this.label = checkNotNull(label,"Label can''t be null");
  }
  public void updateCurrentIndexValue(int index, int valueToSet) {
    //检查索引是否有效
    this.currentIndex = checkElementIndex(index, values.length, "Index out of bounds for values"); 
    //检查参数值
    checkArgument(valueToSet <= 100,"Value can't be more than 100"); 
    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直接返回,若是为null会抛出空指针异常。
  • checkElementIndex (int index, int size, Object message):在这方法当中,index是你将要访问的元素下标,size是这个要访问的array,list或者字符串的长度。而后校验是否有效,若是无效抛出IndexOutOfBoundsException。
  • checkArgument (Boolean expression, Object message):这方法传入布尔表达式。 这个布尔表达式若是为true则继续执行,不然抛出IllegalArgumentException。
  • checkState (Boolean expression, Object message):这方法传入 一个布尔表达式涉及对象的状态,而不是参数。 这个布尔表达式若是为true则继续执行,不然抛出IllegalArgumentException。
Object工具
在这个章节当中咱们将介绍帮助检查null值和建立toString和hashCode的方法的实用方法。咱们接着去看看一个有用的类,它实现Comparable接口。 
 
Getting help with the toString method
当咱们要调试的时候,toString方法是必须重写的,重写它的乏味无趣的。然而,Objects类可使用toStringHelper方法让重写更简单。看下面的例子:
public class Book implements Comparable<Book> {
  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();
  }
}
让咱们看看toString方法:
  • 首先咱们传入一个Book对象来建立一个Objects.ToStringHelper实例。
  • 第二步,咱们调用omitNullValues来排除任何null值的属性。
  • 调用add方法来添加每个属性的标签和属性。
检查null
firstNonNull方法接受2个参数而且返回第一个参数若是它不为null。
String value = Objects.firstNonNull(someString, "default value");
firstNonNull方法使用时候若是你不肯定传入的值是否为null你能够提供一个默认值给它。须要注意:若是传入的2个参数都是null,会抛出空指针异常。
 
建立hash codes
为类写hashCode方法是基本的可是乏味无趣。Objects类能够帮助咱们使用hashCode方法更加简单。考虑下Book类有4个属性:title, author, publisher, 和isbn. 下面的代码将展现使用Object.hashCode方法返回hashCode值。
public int hashCode() {
  return Objects.hashCode(title, author, publisher, isbn);
}
实现CompareTo方法
再次使用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,那就是全部的比较返回的都是0。
 
概要
在本章中咱们介绍了许多基础操做。咱们学习了如何使用Guava让分割字符串更简单经过使用Joiner, Splitter,和很是有用的MapJoiner和MapSplitter类。咱们也学习了使用Charsets, CharMatcher, 和 Strings 来操做字符串。
咱们学习了如何让咱们代码更增强壮和便于调试经过使用Preconditions类。在Objects类中,咱们学习了一些使用方法来设置默认值和建立toString和hashCode方法。咱们也学习了使用ComparisonChain类来实现compareTo方法更简单。
在下一章,咱们学习如何使用Guava来进行函数式编程,经过Function和Predicate接口使咱们的编程效率更加高。
相关文章
相关标签/搜索