鍍金池/ 教程/ Java/ I/O
不可變集合
排序: Guava 強(qiáng)大的”流暢風(fēng)格比較器”
強(qiáng)大的集合工具類:java.util.Collections 中未包含的集合工具
新集合類型
常見 Object 方法
I/O
前置條件
字符串處理:分割,連接,填充
散列
原生類型
數(shù)學(xué)運(yùn)算
使用和避免 null
Throwables:簡化異常和錯(cuò)誤的傳播與檢查
google Guava 包的 ListenableFuture 解析
事件總線
緩存
函數(shù)式編程
區(qū)間
集合擴(kuò)展工具類
Google-Guava Concurrent 包里的 Service 框架淺析
google Guava 包的 reflection 解析

I/O

字節(jié)流和字符流

Guava 使用術(shù)語”流” 來表示可關(guān)閉的,并且在底層資源中有位置狀態(tài)的 I/O 數(shù)據(jù)流。術(shù)語”字節(jié)流”指的是 InputStream 或 OutputStream,”字符流”指的是 Reader 或 Writer(雖然他們的接口 Readable 和 Appendable 被更多地用于方法參數(shù))。相應(yīng)的工具方法分別在 ByteStreamsCharStreams 中。

大多數(shù) Guava 流工具一次處理一個(gè)完整的流,并且/或者為了效率自己處理緩沖。還要注意到,接受流為參數(shù)的 Guava 方法不會(huì)關(guān)閉這個(gè)流:關(guān)閉流的職責(zé)通常屬于打開流的代碼塊。

其中的一些工具方法列舉如下:

ByteStreams CharStreams
byte[] toByteArray(InputStream) String toString(Readable)
N/A List<String> readLines(Readable)
long copy(InputStream, OutputStream) long copy(Readable, Appendable)
void readFully(InputStream, byte[]) N/A
void skipFully(InputStream, long) void skipFully(Reader, long)
OutputStream nullOutputStream() Writer nullWriter()

關(guān)于 InputSupplier 和 OutputSupplier 要注意:

在 ByteStreams、CharStreams 以及 com.google.common.io 包中的一些其他類中,某些方法仍然在使用 InputSupplier 和 OutputSupplier 接口。這兩個(gè)借口和相關(guān)的方法是不推薦使用的:它們已經(jīng)被下面描述的 source 和 sink 類型取代了,并且最終會(huì)被移除。

源與匯

通常我們都會(huì)創(chuàng)建 I/O 工具方法,這樣可以避免在做基礎(chǔ)運(yùn)算時(shí)總是直接和流打交道。例如,Guava 有 Files.toByteArray(File) 和 Files.write(File, byte[])。然而,流工具方法的創(chuàng)建經(jīng)常最終導(dǎo)致散落各處的相似方法,每個(gè)方法讀取不同類型的源

或?qū)懭氩煌愋偷膮R[sink]。例如,Guava 中的 Resources.toByteArray(URL)和 Files.toByteArray(File)做了同樣的事情,只不過數(shù)據(jù)源一個(gè)是 URL,一個(gè)是文件。

為了解決這個(gè)問題,Guava 有一系列關(guān)于源與匯的抽象。源或匯指某個(gè)你知道如何從中打開流的資源,比如 File 或 URL。源是可讀的,匯是可寫的。此外,源與匯按照字節(jié)和字符劃分類型。

字節(jié) 字符
ByteSource CharSource
ByteSink CharSink

源與匯 API 的好處是它們提供了通用的一組操作。比如,一旦你把數(shù)據(jù)源包裝成了 ByteSource,無論它原先的類型是什么,你都得到了一組按字節(jié)操作的方法。

創(chuàng)建源與匯

Guava 提供了若干源與匯的實(shí)現(xiàn):

字節(jié) 字符
Files.asByteSource(File) Files.asCharSource(File, Charset)
Files.asByteSink(File, FileWriteMode...) Files.asCharSink(File, Charset, FileWriteMode...)
Resources.asByteSource(URL) Resources.asCharSource(URL, Charset)
ByteSource.wrap(byte[]) CharSource.wrap(CharSequence)
ByteSource.concat(ByteSource...) CharSource.concat(CharSource...)
ByteSource.slice(long, long) N/A
N/A ByteSource.asCharSource(Charset)
N/A ByteSink.asCharSink(Charset)

此外,你也可以繼承這些類,以創(chuàng)建新的實(shí)現(xiàn)。

注:把已經(jīng)打開的流(比如 InputStream)包裝為源或匯聽起來是很有誘惑力的,但是應(yīng)該避免這樣做。源與匯的實(shí)現(xiàn)應(yīng)該在每次 openStream()方法被調(diào)用時(shí)都創(chuàng)建一個(gè)新的流。始終創(chuàng)建新的流可以讓源或匯管理流的整個(gè)生命周期,并且讓多次調(diào)用 openStream()返回的流都是可用的。此外,如果你在創(chuàng)建源或匯之前創(chuàng)建了流,你不得不在異常的時(shí)候自己保證關(guān)閉流,這壓根就違背了發(fā)揮源與匯 API 優(yōu)點(diǎn)的初衷。

使用源與匯

一旦有了源與匯的實(shí)例,就可以進(jìn)行若干讀寫操作。

通用操作

所有源與匯都有一些方法用于打開新的流用于讀或?qū)?。默認(rèn)情況下,其他源與匯操作都是先用這些方法打開流,然后做一些讀或?qū)?,最后保證流被正確地關(guān)閉了。這些方法列舉如下:

  • openStream():根據(jù)源與匯的類型,返回 InputStream、OutputStream、Reader 或者Writer。
  • openBufferedStream():根據(jù)源與匯的類型,返回 InputStream、OutputStream、BufferedReader 或者 BufferedWriter。返回的流保證在必要情況下做了緩沖。例如,從字節(jié)數(shù)組讀數(shù)據(jù)的源就沒有必要再在內(nèi)存中作緩沖,這就是為什么該方法針對(duì)字節(jié)源不返回 BufferedInputStream。字符源屬于例外情況,它一定返回 BufferedReader,因?yàn)?BufferedReader 中才有 readLine()方法。

源操作

字節(jié)源 字符源
byte[] read() String read()
N/A ImmutableList<String> readLines()
N/A String readFirstLine()
long copyTo(ByteSink) long copyTo(CharSink)
long copyTo(OutputStream) long copyTo(Appendable)
long size() (in bytes) N/A
boolean isEmpty() boolean isEmpty()
boolean contentEquals(ByteSource) N/A
HashCode hash(HashFunction) N/A

匯操作

字節(jié)匯 字符匯
void write(byte[]) void write(CharSequence)
long writeFrom(InputStream) long writeFrom(Readable)
N/A void writeLines(Iterable<? extends CharSequence>)
N/A void writeLines(Iterable<? extends CharSequence>, String)

范例


    //Read the lines of a UTF-8 text file
    ImmutableList<String> lines = Files.asCharSource(file, Charsets.UTF_8).readLines();
    //Count distinct word occurrences in a file
    Multiset<String> wordOccurrences = HashMultiset.create(
    Splitter.on(CharMatcher.WHITESPACE)
    .trimResults()
    .omitEmptyStrings()
    .split(Files.asCharSource(file, Charsets.UTF_8).read()));

    //SHA-1 a file
    HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());

    //Copy the data from a URL to a file
    Resources.asByteSource(url).copyTo(Files.asByteSink(file));

文件操作

除了創(chuàng)建文件源和文件的方法,F(xiàn)iles 類還包含了若干你可能感興趣的便利方法。

createParentDirs(File) 必要時(shí)為文件創(chuàng)建父目錄
getFileExtension(String) 返回給定路徑所表示文件的擴(kuò)展名
getNameWithoutExtension(String) 返回去除了擴(kuò)展名的文件名
simplifyPath(String) 規(guī)范文件路徑,并不總是與文件系統(tǒng)一致,請(qǐng)仔細(xì)測試
fileTreeTraverser() 返回 TreeTraverser 用于遍歷文件樹
上一篇:緩存下一篇:不可變集合