[TOC]java
经过Join类,咱们能够方便实现将容器中的数据按照自定义的方式拼接成一个字符串,并且这是一种线程安全的方式。设计模式
ArrayList<Integer> integers = Lists.newArrayList(1,2,3,4);
String join = Joiner.on("---").join(integers);
System.out.println(join);
复制代码
经过查看Joiner的源码能够发现Join的源码是私有的,也就是不让咱们直接去新建对象,而是让咱们经过提供的共有静态方法去构建对象:数组
public static Joiner on(char separator) {
return new Joiner(String.valueOf(separator));
}
private Joiner(Joiner prototype) {
this.separator = prototype.separator;
}
复制代码
猜想经过这种方法构造对象的主要目的是让使用者明白这里传入的separator参数是用做容器之间额链接符的。安全
在对象中保存了分隔符以后又是如何进行划分的呢?多线程
public final String join(Iterable<?> parts) {
return join(parts.iterator());
}
public final String join(Iterator<?> parts) {
return appendTo(new StringBuilder(), parts).toString();
}
public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) {
try {
appendTo((Appendable) builder, parts);
} catch (IOException impossible) {
throw new AssertionError(impossible);
}
return builder;
}
复制代码
上面代码是一种重载的思想,最终是经过appendTo方法进行的字符创拼接,注意到这里传入的第一个参数是一个StringBuilder对象,为了就是保证线程的安全性,防止多线程环境下可能出现的问题。app
最后在appendTo函数是如何作字符串拼接的?ide
public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
checkNotNull(appendable);
if (parts.hasNext()) {
appendable.append(toString(parts.next()));
while (parts.hasNext()) {
appendable.append(separator);
appendable.append(toString(parts.next()));
}
}
return appendable;
}
复制代码
能够看到这里首先检查了一下这个StringBuilder是否为空,若是为空就会抛出异常。再而后就是迭代器的遍历了,注意到这里有一个toString()
方法,很明显,这里重写了Object类的这个方法:函数
CharSequence toString(Object part) {
checkNotNull(part); // checkNotNull for GWT (do not optimize).
return (part instanceof CharSequence) ? (CharSequence) part : part.toString();
}
复制代码
一样这里作了类型检查,就是为了防止容器中有null对象,其次就是若是part自己就是字符数组了,就不用调用tostring方法了,节约系统资源。学习
除了第一个元素外,其他每个元素都是分隔符与元素的拼接,很简单的逻辑。就这样完成了一次拼接操做。ui
除了基本的字符串拼接外,Joiner类还包含了一些优秀的设计模式,在前面已经说过,若是容器中包含null对象,那么将会抛出空指针异常,咱们能够经过以下方法去避免:
ArrayList<Integer> integers = Lists.newArrayList(1,2,3,4,null);
String join = Joiner.on("---").skipNulls().join(integers);
System.out.println(join);
复制代码
这里的skipNulls能够避免凭借那些null值,不过拼接的逻辑在以前已经写好了,咱们并无看到有什么避免空值的方法呀,那究竟是如何实现的呢?
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");
}
};
}
复制代码
这个方法看上去稍微有点长,其实逻辑是很简单的,就是经过从新返回了一个Joiner对象,这个对象的分隔符和以前的是同样的,不过对于appendTo
,useForNull
,withKeyValueSeparator
这个几个方法进行了重写,因此最后调用join方法的时候就可以对Null值进行不一样的判断了。
一样的,Joiner中还有一个替换Null的方法
ArrayList<Integer> integers = Lists.newArrayList(1,2,3,4,null);
String join = Joiner.on("---").useForNull("XXX").join(integers);
System.out.println(join);
复制代码
这里的useForNull
方法和上面的skipNulls
方法很像,都是经过返回重写了方法的Joiner对象实现不一样的判断策略,其实这就是一种策略模式的体现,对于不一样的实现策略有不一样的实现,针对实际应用中的需求应用不一样的策略,避免了在实现方法中加入大量case判断的问题。