文章目录
Joiner
Joiner可以通过一个给定的分隔符将多个字符串连接起来。
Joiner.on("|").skipNulls().join(stringList);
Joiner.on("|").useForNull("no value").join(stringList);
关于使用Joiner类,需要强调几点:Joiner类不仅限于使用strings。 还可以传入an array, iterable, or varargs of any object。 最终的结果是通过调用传入的每个元素的Object.toString()。因此,如果未使用skipNulls或useForNull方法,在遇到空对象元素时将抛出NullPointerException错误。另外,一旦创建,Joiner对象是不可变的,因此是线程安全的,并且可以用作static final 变量。 考虑到这一点,请考虑以下代码段:
Joiner stringJoiner = Joiner.on("|").skipNulls();
//userForNull方法返回一个新的Joiner实例。stringJoiner实例保持不变
stringJoiner.useForNull("missing");
stringJoiner.join("foo","bar",null);
在上面的代码中,useForNull()
方法的调用对原来的Joiner实例没有影响。调用stringJoiner.join()
方法的调用依然会忽略null值。
Joiner类不但可以返回strings,还有可以与StringBuilder类配合使用的方法:
StringBuilder stringBuilder = new StringBuilder();
Joiner joiner = Joiner.on("|").skipNulls();
//returns the StringBuilder instance with the values foo,bar,baz appeneded with "|" delimiters
joiner.appendTo(stringBuilder,"foo","bar","baz")
在上面给出的例子中,我们给Joiner类的入参为一个StringBuilder实例,返回参数也是一个StringBuilder对象。
Joiner类可以和实现了Appendable接口的类一起使用:
FileWriter fileWriter = new FileWriter(new File("path")):
List<Date> dateList = getDates();
Joiner joiner = Joiner.on("#").useForNulls(" ");
//returns the FileWriter instance with the values appended into it
joiner.appendTo(fileWriter,dateList);
在这里给出的例子中,appendTo的返回参数为FilteWriter实例。
MapJoiner
MapJoiner方法的工作方式与Joiner类类似,但它将给定的字符串作为具有指定分隔符的键值对连接。 MapJoiner方法创建如下:
mapJoiner = Joiner.on("#").withKeyValueSeparator("=");
这行代码可分为两步:
- Joiner.on("#")方法创建了一个Joiner对象。
- 通过Joiner实例的withKeyValueSeparator方法会构造一个MapJoiner对象。
Splitter
给定一个字符串和一个分隔符,Splitter可以利用给定的分隔符将字符串分割成多个子字符串。
相对于String.split方法的好处
String testString = "Monday,Tuesday,,Thursday,Friday,,";
//parts is [Monday, Tuesday, , Thursday,Friday]
String[] parts = testString.split(",");
从上面的例子中可以看到String.split方法将最后的两个实体丢弃了。这种结果在某些情况下是你想要的,但有些情况下却不是。因此这类操作应该留给编程人员决定。
Splitter.on("|").split("foo|bar|baz");
//以1个或多个连续的数字为分割符
Splitter splitter = Splitter.on("\\d+");
//Splits on '|' and removes any leading or trailing whitespace(与上面String.split方法操作结果相同)
Splitter splitter = Splitter.on('|').trimResults();
与Joiner的不可变性类似,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|||");
MapSplitter
//MapSplitter is defined as an inner class of Splitter
Splitter.MapSplitter mapSplitter = Splitter.on("#").withKeyValueSeparator("=");
Strings
Charsets
反例:
try{
byte[] bytes = "foobarbaz".getBytes("UTF-8");
}catch (UnsupportedEncodingException e){
//This really can't happen UTF-8 must be supported
}
分析这段代码我们可以得知:由于Java平台必须支持UTF-8字符集,所以实际上这段代码永远不会抛出UnsupportedEncodingException错误。但由于我们这里使用字符串来指定字符集定义,因此我们可能会出现拼写错误,这会导致异常被抛出。而使用CharSets类可以更好的完成这项工作。值得注意的是:自从Java7开始,JDK中提供了StandardCharsets类来完成相同的功能。
byte[] bytes2 = "foobarbaz".getBytes(Charsets.UTF_8);
Strings
Strings类提供了许多方便的工具方法来操纵字符串。首先看一下使用这个类的一个例子:
StringBuilder builder = new StringBuilder("foo");
char c = 'x';
for(int i=0; i<3; i++){
builder.append(c);
}
return builder.toString();
以上是没有guava时,在一个字符串后面追加3个x字符的实现。使用guava后,只需一行代码就可以完成。
Strings.padEnd("foo",6,'x');
这里需要注意的是第二个参数-6,它指定了调用方法返回的字符串长度的最小值而不是在原始的字符串上追加多少次x字符。如果提供的字符串长度已经等于或大于6,那么不会追加任何字符。Strings类还有一个对应的padStart
方法,这个方法在给定字符串的前面插入字符。
除此以外,Strings类中有三个非常有用的方法,专门用于处理可能的null值:
- nullToEmpty
- emptyToNull
- isNullOrEmpty
It would probably be a good idea to always use the nullToEmpty method on any string objects passed as argument.
CharMatcher
下面的例子将多行的字符串变成一行,以空格分隔开每一行。
CharMatcher.BREAKING_WHITESPACE.replaceFrom(stringWithLinebreaks,' ');
下面给出的代码是移除多个连续的tab和space,并替换成一个space。
@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));
}
//将字符串前端的连续空格也删除调
@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));
}
除了替换一组匹配的字符集合外,CharMatcher还可以提取出匹配的字符集合。看下面的例子:
@Test
public void testRetainFrom(){
String lettersAndNumbers = "foo989yxbar234";
String expected = "989234";
String retained = CharMatcher.JAVA_DIGIT.retainFrom(lettersAndNumbers);
assertThat(expected,is(retained));
}
最后,CharMatcher还有一个强大的功能:通过组合CharMatcher类来构建一个新的CharMatcher。下面让我们来看看如何构造出一个匹配数字(numbers)或者whitespace的matcher。
CharMatcher cm = CharMatcher.JAVA_DIGIT.or(CharMatcher.WHITESPACE);
Preconditions
Preconditions类是用于验证我们代码状态的静态方法集合。 Preconditions非常重要,因为他们确保代码的成功执行符合我们的预期。 如果条件与我们的预期不同,我们会立即获得有关问题所在位置的反馈。 和以前一样,使用前置条件对于确保代码的行为很重要,并且在调试时非常有用。
反例:我们常常会写出下面的代码
if(someObj == null){
throw new IllegalArgumentException(" someObj must not be null");
}
如果使用Preconditions类。对于检查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) {
//returns value of object if not null
this.label = checkNotNull(label,"Label can''t be null");
}
public void updateCurrentIndexValue(int index, int valueToSet) {
//Check index valid first
this.currentIndex = checkElementIndex(index, values.length, "Index out of bounds for values");
//Validate valueToSet
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;
}
}
Objects
com.google.common.base.Objects中的方法都可以在java.util.Objects或者com.google.common.base.MoreObjects中找到替代方法
Getting help with the toString method
在debugging时,toString方法非常必要。然而自己来写一个toString方法非常单调无味。而Objects类通过toStringHelper方法可以使得这个工作非常简单(已经被MoreObjects.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方法共完成了3项工作:
- 首先我们将Book对象的引用传入toStringHelper方法,生成了一个Objects.ToStringHelper实例。(最新版本的guava已经将这个方法标记为deprecated。使用MoreObjects.toStringHelper方法替代)
- omitNullValues方法的调用会排除添加任何null属性值。
- 每次调用add都会提供一个标签和属性,以包含在Book对象的字符串表示中。
Checking for null values
//已经被MoreObjects.firtNonNull取代
String value = Objects.firstNonNull(someString,""default value"");
firstNonNull方法传入两个参数,返回第一个不是null的参数。该方法常用于给一个不确定是否为null的对象提供一个默认值。需要谨慎一些的是:如果两个参数都为null,方法会抛出NullPointerException。
Generating hash codes
Objects利用hashCode方法可以很方便的实现一个对象的hashCode方法。
public int hashCode() {
//已经被java.util.Objects的hash方法取代
return Objects.hashCode(title, author, publisher, isbn);
}
ComparisonChain:Implementing CompareTo
以下是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();
}
第二种写法不但更紧凑而且更容易阅读。