<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
姊妹篇:Java效率提升神器jOOR
在我們的開發中經常會用到Guava中的一些功能。但是我們所使用到的只是Guava API中的小的可憐的一個子集。我們大家一起來發掘一下Guava中更多的一些功能。
這是在我們程式碼中出現頻率比較高的一個功能。經常需要將幾個字串,或者字串陣列、列表之類的東西,拼接成一個以指定符號分隔各個元素的字串,比如要將一個用List儲存的字元集合拼起來作為SQL語句的條件,在知道Joiner之前我們會這樣做。
ArrayList<String> conditions = new ArrayList<String>(); conditions.add("condition1"); conditions.add("condition2"); conditions.add("condition3"); private String buildCondition(ArrayList<String> conditions) { StringBuilder sb = new StringBuilder(); for (String condition : conditions) { sb.append(condition); sb.append(" or "); } int index = sb.lastIndexOf(" or "); return index > 0 ? sb.substring(0, index) : sb.toString(); } // condition1 or condition2 or condition3
基本上會手寫回圈去實現,程式碼瞬間變得醜陋起來。並且迴圈完了還得刪除最後一個多餘的or。
使用Guava工具,我們能夠輕而易舉的完成字串拼接這一簡單任務。藉助 Joiner 類,程式碼瞬間變得優雅起來。
Joiner.on(" or ").join(conditions);
被拼接的物件集,可以是寫死的少數幾個物件,可以是實現了 Iterable 介面的集合,也可以是迭代器物件。
除了返回一個拼接過的字串,Joiner 還可以在實現了 Appendable 介面的物件所維護的內容的末尾,追加字串拼接的結果。
StringBuilder sb = new StringBuilder("result:"); Joiner.on("#").appendTo(sb, 1, 2, 3); System.out.println(sb); //result:1#2#3
我們看下面這個例子:
Joiner.on("#").join(1, null, 3)
如果傳入的物件中包含空指標,會直接丟擲空指標異常。Joiner 提供了兩個方法,讓我們能夠優雅的處理待拼接集合中的空指標。
如果我們希望忽略空指標,那麼可以呼叫 skipNulls方法,得到一個會跳過空指標的 Joiner 範例。如果希望將空指標變為某個指定的值,那麼可以呼叫 useForNull 方法,指定用來替換空指標的字串。
Joiner.on("#").skipNulls().join(1, null, 3); //1#3 Joiner.on("#").useForNull("").join(1, null, 3); //1##3
MapJoiner 是 Joiner 的內部靜態類,用於幫助將 Map 物件拼接成字串。
Map<Integer, Integer> test = new HashMap<Integer, Integer>(); test.put(1, 2); test.put(3, 4); Joiner.on("#").withKeyValueSeparator("=").join(test); //1=2#3=4
withKeyValueSeparator 方法指定了鍵與值的分隔符,同時返回一個 MapJoiner 範例。
Joiner.on("#").withKeyValueSeparator("=").join(ImmutableMap.of(1, 2, 3, 4)); //1=2#3=4
原始碼來自Guava 18.0。Joiner類的原始碼一共458行。大部分都是註釋。 Joiner 只能通過 Joiner.on 函數來初始化,它的構造方法是私有的。
/** * Returns a joiner which automatically places {@code separator} between consecutive elements. */ public static Joiner on(String separator) { return new Joiner(separator); } /** * Returns a joiner which automatically places {@code separator} between consecutive elements. */ public static Joiner on(char separator) { return new Joiner(String.valueOf(separator)); }
整個 Joiner 類最核心的函數莫過於 <A extends Appendable> appendTo(A, Iterator<?>)
,一切的字串拼接操作,最後都會呼叫到這個函數。這就是所謂的全功能函數,其他的一切 appendTo 只不過是它的過載,一切的join不過是它和它的過載的封裝。
/** * Appends the string representation of each of {@code parts}, using the previously configured * separator between each, to {@code appendable}. * * @since 11.0 */ 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; }
這段程式碼的第一個技巧是使用 if 和 while 來實現了比較優雅的分隔符拼接,避免了在末尾插入分隔符的尷尬;第二個技巧是使用了自定義的 toString 方法而不是 Object#toString 來將物件序列化成字串,為後續的各種空指標保護開了方便之門。
來看一個比較有意思的 appendTo 過載。
public final StringBuilder appendTo(StringBuilder builder, Iterator<?> parts) { try { this.appendTo((Appendable)builder, (Iterator)parts); return builder; } catch (IOException var4) { throw new AssertionError(var4); } }
在 Appendable 介面中,append 方法是會丟擲 IOException 的。然而 StringBuilder 雖然實現了 Appendable,但是它覆蓋實現的 append 方法卻是不丟擲 IOException 的。於是就出現了明知不可能拋異常,卻又不得不去捕獲異常的尷尬。
這裡的例外處理手法十分機智,異常變數命名為 impossible,我們一看就明白這裡是不會丟擲 IOException 的。但是如果 catch 塊裡面什麼都不做又好像不合適,於是丟擲一個 AssertionError,表示對於這裡不拋異常的斷言失敗了。
另一個比較有意思的 appendTo 過載是關於可變長引數。
public final <A extends Appendable> A appendTo(A appendable, @Nullable Object first, @Nullable Object second, Object... rest) throws IOException { return this.appendTo((Appendable)appendable, (Iterable)iterable(first, second, rest)); }
注意到這裡的 iterable 方法,它把兩個變數和一個陣列變成了一個實現了Iterable 介面的集合,非常精妙的實現!
private static Iterable<Object> iterable(final Object first, final Object second, final Object[] rest) { Preconditions.checkNotNull(rest); return new AbstractList() { public int size() { return rest.length + 2; } public Object get(int index) { switch(index) { case 0: return first; case 1: return second; default: return rest[index - 2]; } } }; }
要想看明白這段程式碼,需要熟悉AbstractList類中迭代器的實現。迭代器內部維護著一個遊標,cursor。迭代器的兩大關鍵操作,hasNext 判斷是否還有沒遍歷的元素,next 獲取下一個元素,它們的實現是這樣的。
public boolean hasNext() { return cursor != size(); } public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } }
hasNext 中關鍵的函數呼叫是size方法,獲取集合的大小。next 方法中關鍵的函數呼叫是get方法,獲取第 i 個元素。Guava 的實現返回了一個被覆蓋了 size 和 get 方法的 AbstractList,巧妙的複用了由編譯器生成的陣列,避免了新建列表和增加元素的開銷。
MapJoiner 實現為 Joiner 的一個靜態內部類,它的建構函式和 Joiner 一樣也是私有,只能通過 withKeyValueSeparator來生成範例。類似地,MapJoiner 也實現了 appendTo 方法和一系列的過載,還用 join 方法對 appendTo 做了封裝。
MapJoiner 整個實現和 Joiner 大同小異,在實現中大量使用 Joiner 的 toString 方法來保證空指標保護行為和初始化時的語意一致。
到此這篇關於Java效率提升神器之Guava-Joiner的文章就介紹到這了,更多相關Java Guava-Joiner內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45