前言
日常开发中,我们经常需要将几个字符串,或者字符串数组、列表之类的数据,拼接成一个以指定符号分隔各个元素的字符串,比如把[1, 2, 3]拼接成"1-2-3",如果自己实现的话,基本上就需要编写循环去实现这个功能,代码就变得晦涩起来。
Joiner
Google Guava提供了一套优雅的API,让我们能够轻而易举的完成字符串拼接。你可以借助Guava的Joiner 类,让代码优雅起来。
skipNulls
它会过滤掉空的参数,其中也包括数组中的null值。
public static void main(String[] args) throws Exception {
List<String> strs = Lists.newArrayList("1", "2", null, "3");
// 当有存在对象数组Object里面的元素不会被拼接到字符串中,而是以地址的形式拼接。
String joinStr = Joiner.on("-").skipNulls().join(strs);
System.out.println(joinStr);
}
注:如果不指定skipNulls, 当要处理的元素中有null值时会抛出空指针异常。
useForNull
它会将设置的参数替换空的数据,其中也包括数组中的null值。
public static void main(String[] args) throws Exception {
List<String> strs = Lists.newArrayList("1", "2", null, "3");
String joinStr = Joiner.on("-").useForNull("0").join(strs);
// 1-2-0-3
System.out.println(joinStr);
}
注:如果不指定skipNulls, 当要处理的元素中有null值时会抛出空指针异常。
MapJoiner
MapJoiner是Joiner的内部类,它没有静态的方法,它执行操作与Joiner相同,但是它主要是针对的Map的KV。
public static void main(String[] args) throws Exception {
Map<String, String> user = new HashMap<String, String>() {
private static final long serialVersionUID = 6396999608476245809L;
{
put("name", "小明");
put("age", "22");
}
};
final String join = Joiner.on("&").withKeyValueSeparator("=").join(user);
// name=小明&age=22
System.out.println(join);
}
它底层就是用了MapJoiner
/**
* Returns a {@code MapJoiner} using the given key-value separator, and the same configuration as
* this {@code Joiner} otherwise.
*/
@CheckReturnValue
public MapJoiner withKeyValueSeparator(String keyValueSeparator) {
return new MapJoiner(this, keyValueSeparator);
}
ps:聪明的同学已经可以看出来上面的这种处理方式在拼接url极度舒服了。
Splitter(详见下一篇)
它与Joiner操作相反的类,它是根据给定的分隔符,把一个字符串分隔成若个子字符串。
public static void main(String[] args) throws Exception {
List<String> ints = Splitter.on("|").splitToList("1|2|3");
// [1, 2, 3]
System.out.println(ints);
}
MapSplitter
MapSplitter是Splitter的内部类,它没有静态的方法,它执行操作与Splitter相同,但是它主要是针对的Map的KV。
public static void main(String[] args) throws IOException {
Map<String, String> params = Splitter.on("&")
.withKeyValueSeparator("=").split("name=小明&age=22");
// {name=小明, age=22}
System.out.println(params);
}
小结
需要注意的是Joiner的构造方法被设置成了私有,所以你需要通过静态的on函数初始化,它也不能二次创建的,如下:
// 此下代码会导致空指针异常
public static void main(String[] args) throws IOException {
Joiner joiner = Joiner.on('-');
joiner.skipNulls(); // 此行代码没有生效的
String joinStr = joiner.join("1", null, "2", "3");
System.out.println(joinStr);
}
这是为什么呢?分步调用joiner的静态函数为什么是不起作用的?
这是因为Joiner创建后就是不可更改的了,所以为了实现useForNull和skipNulls等语义,在底层会再次创建一个匿名内部类,并覆盖相应的方法,如下:
/**
* Returns a joiner with the same behavior as this joiner, except automatically skipping over any
* provided null elements.
*/
@CheckReturnValue
public Joiner skipNulls() {
return new Joiner(this) {
@Override
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
checkNotNull(appendable, "appendable");
checkNotNull(parts, "parts");
while (parts.hasNext()) {
Object part = parts.next();
if (part != null) {
appendable.append(Joiner.this.toString(part));
break;
}
}
while (parts.hasNext()) {
Object part = parts.next();
if (part != null) {
appendable.append(separator);
appendable.append(Joiner.this.toString(part));
}
}
return appendable;
}
@Override
public Joiner useForNull(String nullText) {
// 为了防止重复调用,一旦调用就抛异常,重复调用没有意义。
throw new UnsupportedOperationException("already specified skipNulls");
}
@Override
public MapJoiner withKeyValueSeparator(String kvs) {
throw new UnsupportedOperationException("can't use .skipNulls() with maps");
}
};
}
所以,你如果非想要分步骤处理,那只是如下这样:
public static void main(String[] args) throws IOException {
Joiner joinerTemp = Joiner.on('-');
Joiner joiner = joinerTemp.skipNulls();
String joinStr = joiner.join("1", null, "2", "3");
// 1-2-3
System.out.println(joinStr);
}