-
Notifications
You must be signed in to change notification settings - Fork 10.9k
IOExplained
Guava uses the term "stream" to refer to a Closeable
stream for I/O data which
has positional state in the underlying resource. The term "byte
stream" refers
to an InputStream
or OutputStream
, while "char
stream" refers to a
Reader
or Writer
(though their supertypes Readable
and Appendable
are
often used as method parameter types). Corresponding utilities are divided into
the utility classes ByteStreams
and CharStreams
.
Most Guava stream-related utilities deal with an entire stream at a time and/or handle buffering themselves for efficiency. Also note that Guava methods that take a stream do not close the stream: closing streams is generally the responsibility of the code that opens the stream.
Some of the methods provided by these classes include:
It's common to create I/O utility methods that help you to avoid dealing with
streams at all when doing basic operations. For example, Guava has
Files.toByteArray(File)
and Files.write(File, byte[])
. However, you end up
with similar methods scattered all over, each dealing with a different kind of
source of data or sink to which data can be written. For example, Guava has
Resources.toByteArray(URL)
which does the same thing as
Files.toByteArray(File)
, but using a URL
as the source of data rather than a
file.
To address this, Guava has a set of abstractions over different types of data
sources and sinks. A source or sink is a resource of some sort that you know how
to open a new stream to, such as a File
or URL
. Sources are readable, while
sinks are writable. Additionally, sources and sinks are broken down according to
whether you are dealing with byte
or char
data.
Operations | Bytes | Chars |
---|---|---|
Reading | ByteSource |
CharSource |
Writing | ByteSink |
CharSink |
The advantage of these APIs is that they provide a common set of operations.
Once you've wrapped your data source as a ByteSource
, for example, you get the
same set of methods no matter what that source happens to be.
Guava provides a number of source and sink implementations:
In addition, you can extend the source and sink classes yourself to create new implementations.
Note: While it can be tempting to create a source or sink that wraps an
open stream (such as an InputStream
), this should be avoided. Your
source/sink should instead open a new stream each time its openStream()
method
is called. This allows the source or sink to control the full lifecycle of that
stream and allows it to be usable multiple times rather that becoming unusable
the first time any method on it is called. Additionally, if you're opening the
stream before creating the source or sink you may still have to deal with
ensuring that the stream is closed correctly if an exception is thrown elsewhere
in your code, which defeats many of the advantages of using a source or sink in
the first place.
Once you have a source or sink instance, you have access to a number of operations for reading or writing.
All sources and sinks provide the ability to open a new stream for reading or writing. By default, other operations are all implemented by calling one of these methods to get a stream, doing something, and then ensuring that the stream is closed.
These methods are all named:
-
openStream()
- returns anInputStream
,OutputStream
,Reader
orWriter
depending on the type of source or sink. -
openBufferedStream()
- returns anInputStream
,OutputStream
,BufferedReader
orWriter
depending on the type of source or sink. The returned stream is guaranteed to be buffered if necessary. For example, a source that reads from a byte array has no need for additional buffering in memory. This is why the methods do not returnBufferedInputStream
etc. except in the case ofBufferedReader
, because it defines thereadLine()
method.
// Read the lines of a UTF-8 text file
ImmutableList<String> lines = Files.asCharSource(file, 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, 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));
In addition to methods for creating file sources and sinks, the Files
class
contains a number of convenience methods that you might be interested in.
Method | Description |
---|---|
createParentDirs(File) |
Creates necessary but nonexistent parent directories of the file. |
getFileExtension(String) |
Gets the file extension of the file described by the path. |
getNameWithoutExtension(String) |
Gets the name of the file with its extension removed |
simplifyPath(String) |
Cleans up the path. Not always consistent with your filesystem; test carefully! |
fileTraverser() |
Returns a Traverser that can traverse file trees |
- Introduction
- Basic Utilities
- Collections
- Graphs
- Caches
- Functional Idioms
- Concurrency
- Strings
- Networking
- Primitives
- Ranges
- I/O
- Hashing
- EventBus
- Math
- Reflection
- Releases
- Tips
- Glossary
- Mailing List
- Stack Overflow
- Android Overview
- Footprint of JDK/Guava data structures