[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判斷的問題。