Interface Stream
A sequence of elements supporting sequential and parallel aggregate operations. The following example illustrates an aggregate operation using Stream and IntStream :
int sum = widgets.stream() .filter(w -> w.getColor() == RED) .mapToInt(w -> w.getWeight()) .sum();
In this example, widgets is a Collection
In addition to Stream , which is a stream of object references, there are primitive specializations for IntStream , LongStream , and DoubleStream , all of which are referred to as «streams» and conform to the characteristics and restrictions described here.
To perform a computation, stream operations are composed into a stream pipeline. A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate) ), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer) ). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.
A stream implementation is permitted significant latitude in optimizing the computation of the result. For example, a stream implementation is free to elide operations (or entire stages) from a stream pipeline — and therefore elide invocation of behavioral parameters — if it can prove that it would not affect the result of the computation. This means that side-effects of behavioral parameters may not always be executed and should not be relied upon, unless otherwise specified (such as by the terminal operations forEach and forEachOrdered ). (For a specific example of such an optimization, see the API note documented on the count() operation. For more detail, see the side-effects section of the stream package documentation.)
Collections and streams, while bearing some superficial similarities, have different goals. Collections are primarily concerned with the efficient management of, and access to, their elements. By contrast, streams do not provide a means to directly access or manipulate their elements, and are instead concerned with declaratively describing their source and the computational operations which will be performed in aggregate on that source. However, if the provided stream operations do not offer the desired functionality, the BaseStream.iterator() and BaseStream.spliterator() operations can be used to perform a controlled traversal.
A stream pipeline, like the «widgets» example above, can be viewed as a query on the stream source. Unless the source was explicitly designed for concurrent modification (such as a ConcurrentHashMap ), unpredictable or erroneous behavior may result from modifying the stream source while it is being queried.
- must be non-interfering (they do not modify the stream source); and
- in most cases must be stateless (their result should not depend on any state that might change during execution of the stream pipeline).
Such parameters are always instances of a functional interface such as Function , and are often lambda expressions or method references. Unless otherwise specified these parameters must be non-null.
A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, «forked» streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream. A stream implementation may throw IllegalStateException if it detects that the stream is being reused. However, since some stream operations may return their receiver rather than a new stream object, it may not be possible to detect reuse in all cases.
Streams have a BaseStream.close() method and implement AutoCloseable . Operating on a stream after it has been closed will throw IllegalStateException . Most stream instances do not actually need to be closed after use, as they are backed by collections, arrays, or generating functions, which require no special resource management. Generally, only streams whose source is an IO channel, such as those returned by Files.lines(Path) , will require closing. If a stream does require closing, it must be opened as a resource within a try-with-resources statement or similar control structure to ensure that it is closed promptly after its operations have completed.
Stream pipelines may execute either sequentially or in parallel. This execution mode is a property of the stream. Streams are created with an initial choice of sequential or parallel execution. (For example, Collection.stream() creates a sequential stream, and Collection.parallelStream() creates a parallel one.) This choice of execution mode may be modified by the BaseStream.sequential() or BaseStream.parallel() methods, and may be queried with the BaseStream.isParallel() method.
- File IO with Java 8 Streams
- 1. Outline
- 2. File IO with Streams
- 2.1. Ingesting a file with Files.lines
- 2.2. Listing a directory with Files.list
- 2.3. Traversing a directory with Files.walk🚶♂️
- 2.4. Finding files with Files.find
- 3. Closing Words
- Java Streams — Java Stream From Files
- Auto close
- Example
- Example 2
- Java Files.newInputStream(Path path, OpenOption . options)
- Example
File IO with Java 8 Streams
The Java 8 Stream API provides comprehensive features for processing file content, listing and traversing directories, and finding files.
1. Outline
The article «Introduction to Stream API through map-reduce» explains how streams provide a way to write SQL-type descriptive queries to process and retrieve data from collections. Like collections contain data in memory, files and directories are containers of data on a disk. Accordingly, the Stream API provides ways to do standard file IO tasks, specifically — consuming files, listing directories, traversing directories, and finding files — through streams.
This article assumes some familiarity with the Stream API. In the next section, we will see some examples of file IO with streams.
2. File IO with Streams
Java 8 added some static methods to Files helper class as part of the Stream API, namely — Files.lines, Files.list, Files.walk, and Files.find. All of these methods do IO operations and can throw IOException, which is a checked exception that the caller must catch or propagate appropriately. Let’s explore these methods further with some examples.
For the examples in this section, we have created a directory structure. The root directory of this structure contains some subdirectories and text files. The tree -hC command displays the directory hierarchy along with the file sizes, as shown below:
We run all the examples in this root directory.
2.1. Ingesting a file with Files.lines
The Files.lines convenience method returns a stream (Stream ) of lines read from a file, which can be lazily processed into useful data. Suppose we want to gather all the unique words from a file; we can do so as follows:
try (Stream lines = Files.lines(Path.of("./File.txt"))) < Setwords = lines .flatMap((l) -> Stream.of(l.split(" "))) //Split & gather words in a flattened stream .collect(Collectors.toSet()); //Collect words in a set //. > catch (IOException e) < e.printStackTrace(); //. >
Note that, the lines from the stream are split into arrays of words which are flattened into one stream (also Stream ) by the flatMap operation. Then the tokenized words from the flattened stream are collected into a set. If the words need to be lexically analyzed and filtered before they are collected, we can do so by applying a filter operation before collecting the words in a set.
Also note the use of try-with-resources statement above, which ensures that the stream is closed at the end of the statement. The Stream‘s base class BaseStream implements the AutoCloseable, so a Stream can be used with the try-with-resources statement as above. The streams returned by the Files.lines, Files.list, Files.walk, and Files.find enclose one or more IO resources that should be closed to avoid the resource leak.
2.2. Listing a directory with Files.list
Listing a directory is like a breeze with streams. The Files.list method returns a stream of paths (Stream ) of all the entries in a directory. For instance, the following code prints all the direct subdirectories under a given directory:
try(Stream paths = Files.list(Path.of("./"))) < paths.filter(Files::isDirectory) //Filter subdirectories .forEach(System.out::println); /* Prints (order could be different): ./FolderA ./FolderB ./FolderC */ >catch (IOException e) < e.printStackTrace(); //. >
2.3. Traversing a directory with Files.walk🚶♂️
The Files.list is quite a helpful method. However, if we want to collect some information from nested files and folders under a directory, we need a more powerful utility. The Files.walk method can traverse down a given folder for a specified depth and return a stream of paths, which can be lazily processed to retrieve sought data.
In the following code, we use Files.walk to calculate the sum of sizes of all the regular files under a folder:
try(Stream paths = Files.walk(Path.of("./"), 3)) < //Traverse for depth 3 long sum = paths.filter(Files::isRegularFile) //Filter regular files .mapToLong((p) ->p.toFile().length()) //Map to file size (long) .sum(); //Calculate sum of file sizes //. /* 'sum' is sum of sizes of these files: ./File.txt ./FolderA/File1.txt ./FolderA/File2.txt ./FolderA/FolderD/File5.txt ./FolderB/File3.txt ./FolderC/File4.txt */ > catch (IOException e) < e.printStackTrace(); //. >
2.4. Finding files with Files.find
A keen reader might have noticed that we can use the Files.walk method to search files under a directory. However, finding files is such a standard operation that it deserves a distinct method for it. The Files.find, as its names suggest, returns a stream of paths that satisfy a given predicate. In the code below, we discover all the regular files that have at least 10KB size:
try(Stream paths = Files.find(Path.of("./"), 3, (p, a) -> < //Predicate return a.isRegularFile() && a.size() >10240; //Regular file with size > 10KB >)) < paths.forEach((p) ->System.out.println(p)); /* Prints: ./FolderA/File1.txt */ > catch (IOException e) < e.printStackTrace(); //. >
In another example below, we search all the files with extension «.txt«:
try(Stream paths = Files.find(Path.of("./"), 3, (p, a) -> < //Predicate return p.toString().endsWith(".txt"); //Extension ".txt" >)) < paths.forEach(System.out::println); /* Prints (order could be different): ./File.txt ./FolderB/File3.txt ./FolderC/File4.txt ./FolderA/File2.txt ./FolderA/File1.txt ./FolderA/FolderD/File5.txt */ >catch (IOException e) < e.printStackTrace(); //. >
3. Closing Words
Stream API provides excellent support for the standard file IO operations in the form of convenient static methods of the Files class. These methods can be used instead of the tedious boilerplate loops to process data on a filesystem.
Java Streams — Java Stream From Files
java.io and java.nio.file packages from Java 8 has added many methods to support I/O operations using streams.
We can read text from a file as a stream of strings. Each element in the stream represents one line of text.
We can also use a stream to read JarEntry from a JarFile and we can read entries in a directory as a stream of Path.
Auto close
Calling the close() method on the stream will close the underlying file.
Alternatively, we can create the stream in a try-with-resources statement so the underlying file is closed automatically.
Example
The following code shows how to read contents of a file using a stream.
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; /*from ww w . ja v a 2 s . com*/ public class Main < public static void main(String[] args) < Path path = Paths.get("./Main.java"); try (Stream lines = Files.lines(path)) < lines.forEach(System.out::println); >catch (IOException e) < e.printStackTrace(); >> >
The code above generates the following result.
Example 2
The following code shows how to read a path using stream.
import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.stream.Stream; /*from w w w.java 2s. c om*/ public class Main < public static void main(String[] args) < Path dir = Paths.get("."); System.out.printf("%nThe file tree for %s%n", dir.toAbsolutePath()); try (Stream fileTree = Files.walk(dir)) < fileTree.forEach(System.out::println); >catch (IOException e) < e.printStackTrace(); >> >
The code above generates the following result.
- Next »
- « Previous
java2s.com | © Demo Source and Support. All rights reserved.
Java Files.newInputStream(Path path, OpenOption . options)
Files.newInputStream(Path path, OpenOption . options) has the following syntax.
public static InputStream newInputStream(Path path, OpenOption . options) throws IOException
Example
In the following code shows how to use Files.newInputStream(Path path, OpenOption . options) method.
/*from w w w . j a va 2 s . c o m*/ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class Main < public static void main(String[] args) < Path rn_demo = Paths.get("C:/tutorial/Java", "demo.txt"); //using NIO.2 unbuffered stream int n; try (InputStream in = Files.newInputStream(rn_demo)) < while ((n = in.read()) != -1) < System.out.print((char) n); > > catch (IOException e) < System.err.println(e); >> >