TLDR

long dirCount = files.peek(System.out::println).count();

The forEach method requires a Consumer as it's parameter:

void forEach(Consumer<? super T> action);

Where the consumer has a contract of:

public interface Consumer<T> {
    void accept(T t);
    ...
}

So in order to satisfy the contract of Consumer you have to provide a method which takes a T t parameter.

In the first example you are using a method reference:

files.forEach(System.out::println);

System.out::println is able to satisfy the contract as it is defined as:

void println(Object obj);

In the second example, you do not provide an expression which can satisfy the parameter, it is just a statement. You might think "Okay, I'll use a lambda that satisfies the contract to do this" but that would also have errors:

files.forEach(file -> dirCount++);
// Fails to compile because dirCount is not final

For an object to be used inside a lambda from the outer scope then it must be final, meaning that you cannot change the reference or primitive value that variable points to. Another problem is that you can only iterate through the stream once. As soon as you iterate through it, then it will be empty.

So given the problem, I would solve this using peek and count. You can use peek to perform an action when each item comes through the stream and sends the value on to the next operator. After that count will evaluate how many items are in the stream (consuming the stream to do so):

long dirCount = files.peek(System.out::println).count();
Answer from flakes on Stack Overflow
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › nio › file › Files.html
Files (Java Platform SE 8 )
April 21, 2026 - Java™ Platform Standard Ed. 8 ... This class consists exclusively of static methods that operate on files, directories, or other types of files.
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › io › File.html
File (Java Platform SE 8 )
April 21, 2026 - Java™ Platform Standard Ed. 8 ... An abstract representation of file and directory pathnames.
🌐
W3Schools
w3schools.com › java › java_files.asp
Java Files
If you don't know what a package is, read our Java Packages Tutorial. The File class has many useful methods for creating and getting information about files.
Top answer
1 of 1
1

TLDR

long dirCount = files.peek(System.out::println).count();

The forEach method requires a Consumer as it's parameter:

void forEach(Consumer<? super T> action);

Where the consumer has a contract of:

public interface Consumer<T> {
    void accept(T t);
    ...
}

So in order to satisfy the contract of Consumer you have to provide a method which takes a T t parameter.

In the first example you are using a method reference:

files.forEach(System.out::println);

System.out::println is able to satisfy the contract as it is defined as:

void println(Object obj);

In the second example, you do not provide an expression which can satisfy the parameter, it is just a statement. You might think "Okay, I'll use a lambda that satisfies the contract to do this" but that would also have errors:

files.forEach(file -> dirCount++);
// Fails to compile because dirCount is not final

For an object to be used inside a lambda from the outer scope then it must be final, meaning that you cannot change the reference or primitive value that variable points to. Another problem is that you can only iterate through the stream once. As soon as you iterate through it, then it will be empty.

So given the problem, I would solve this using peek and count. You can use peek to perform an action when each item comes through the stream and sends the value on to the next operator. After that count will evaluate how many items are in the stream (consuming the stream to do so):

long dirCount = files.peek(System.out::println).count();
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › nio › file › package-summary.html
java.nio.file (Java Platform SE 8 )
April 21, 2026 - The java.nio.file.spi package is used by service provider implementors wishing to extend the platform default provider, or to construct other provider implementations.
🌐
Medium
kannappanchidambaram.medium.com › file-io-in-java-8-part-i-6b3cfd40588a
File IO in Java 8 — Part I
April 23, 2022 - Path p1 = Paths.get("eg-file.txt"); Path p2 = Paths.get("/usr/temp/eg-file.txt"); Path p3 = Paths.get("C:\\temp\\eg-file.txt"); note: as java has predefined meaning for single backlash hence adding the double backslash.
🌐
Baeldung
baeldung.com › home › java › java io › list files in a directory in java
List Files in a Directory in Java | Baeldung
January 8, 2024 - Now we’ll see how to list files using the Stream API. Java 8 introduced a new list() method in java.nio.file.Files.
Find elsewhere
🌐
Mkyong
mkyong.com › home › java8 › java 8 stream – read a file line by line
Java 8 Stream - Read a file line by line - Mkyong.com
October 29, 2015 - package com.mkyong.java8; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; public class TestReadFile2 { public static void main(String args[]) { String fileName = "c://lines.txt"; List<String> list = new ArrayList<>(); try (Stream<String> stream = Files.lines(Paths.get(fileName))) { //1. filter line 3 //2. convert all content to upper case //3. convert it into a List list = stream .filter(line -> !line.startsWith("line3")) .map(String::toUppe
🌐
DigitalOcean
digitalocean.com › community › tutorials › java-files-nio-files-class
Java Files - java.nio.file.Files Class | DigitalOcean
August 4, 2022 - Pre Visit Directory: D:\pankaj Pre Visit Directory: D:\pankaj\java6 Pre Visit Directory: D:\pankaj\java6\Files Visit File: D:\pankaj\java6\Files\file.txt.txt Post Visit Directory: D:\pankaj\java6\Files Post Visit Directory: D:\pankaj\java6 Pre Visit Directory: D:\pankaj\java7 Pre Visit Directory: D:\pankaj\java7\Files Visit File: D:\pankaj\java7\Files\file.txt.txt Post Visit Directory: D:\pankaj\java7\Files Post Visit Directory: D:\pankaj\java7 Pre Visit Directory: D:\pankaj\java8 Pre Visit Directory: D:\pankaj\java8\Files Visit File: D:\pankaj\java8\Files\file.txt.txt Post Visit Directory: D:\pankaj\java8\Files Post Visit Directory: D:\pankaj\java8 Post Visit Directory: D:\pankaj · Notice that all the files and folders are processed recursively.
🌐
Baeldung
baeldung.com › home › java › java io › introduction to the java nio2 file api
Introduction to Java NIO2 File API | Baeldung
January 8, 2024 - Since its introduction in Java 8, the Stream API has become a staple of Java development.
🌐
GitHub
github.com › openjdk › jdk › blob › master › src › java.base › share › classes › java › nio › file › Files.java
jdk/src/java.base/share/classes/java/nio/file/Files.java at master · openjdk/jdk
JDK main-line development https://openjdk.org/projects/jdk - jdk/src/java.base/share/classes/java/nio/file/Files.java at master · openjdk/jdk
Author   openjdk
🌐
Java Code Geeks
javacodegeeks.com › home › core java
Java 8 - How To Read A File? - Java Code Geeks
May 1, 2021 - Next, use forEach() method to get the each line of file and print into the console. ... Finally, learn another new method of java 8 api is BufferedReader.lines() method which returns the Stream<String>.
🌐
GeeksforGeeks
geeksforgeeks.org › java › file-class-in-java
Java File Class - GeeksforGeeks
November 3, 2025 - Example 1: Program to check if a file or directory physically exists or not. ... import java.io.File; // Displaying file property class CheckFileExist { public static void main(String[] args){ // Accept file name or directory name through // command line args String fname = args[0]; // pass the filename or directory name to File // object File f = new File(fname); // apply File class methods on File object System.out.println("File name :" + f.getName()); System.out.println("Path: " + f.getPath()); System.out.println("Absolute path:" + f.getAbsolutePath()); System.out.println("Parent:" + f.getParent()); System.out.println("Exists :" + f.exists()); if (f.exists()) { System.out.println("Is writable:" + f.canWrite()); System.out.println("Is readable" + f.canRead()); System.out.println("Is a directory:" + f.isDirectory()); System.out.println("File Size in bytes " + f.length()); } } }
Top answer
1 of 5
6

A general note on the use of FileReader: FileReader uses internally a FileInputStream which overrides finalize() and is therefore discouraged to use beacause of the impact it has on garbarge collection especially when dealing with lots of files.

Unless you're using a Java version prior to Java 7 you should use the java.nio.files API instead, creating a BufferedReader with

 Path path = Paths.get(filename);
 BufferedReader br = Files.newBufferedReader(path);

So the beginning of your stream pipeline should look more like

 filenames.map(Paths::get)
          .filter(Files::exists)
          .map(p -> {
        try {
            return Optional.of(Files.newBufferedReader(p));
        } catch (IOException e) {
            return Optional.empty();
        }
    }) 

Now to your problem:

Option 1

One way to preserve the original Reader would be to use a Tuple. A tuple (or any n-ary variation of it) is generally a good way to handle multiple results of a function application, as it's done in a stream pipeline:

class ReaderTuple<T> {
   final Reader first;
   final T second;
   ReaderTuple(Reader r, T s){
     first = r;
     second = s;
   }
}

Now you can map the FileReader to a Tuple with the second item being your current stream item:

 filenames.map(Paths::get)
  .filter(Files::exists)
  .map(p -> {
        try {
            return Optional.of(Files.newBufferedReader(p));
        } catch (IOException e) {
            return Optional.empty();
        }
    }) 
  .filter(Optional::isPresent)
  .map(Optional::get)
  .flatMap(r -> new ReaderTuple(r, yourOtherItem))
  ....
  .peek(rt -> {
    try { 
      rt.first.close()  //close the reader or use a try-with-resources
    } catch(Exception e){}
   })
  ... 

Problem with that approach is, that whenever an unchecked exception occurrs during stream execution betweem the flatMap and the peek, the readers might not be closed.

Option 2

An alternative to use a tuple is to put the code that requires the reader in a try-with-resources block. This approach has the advantage that you're in control to close all readers.

Example 1:

 filenames.map(Paths::get)
  .filter(Files::exists)
  .map(p -> {
        try (Reader r = new BufferedReader(new FileReader(p))){

            Stream.of(r)
            .... //put here your stream code that uses the stream

        } catch (IOException e) {
            return Optional.empty();
        }
    }) //reader is implicitly closed here
 .... //terminal operation here

Example 2:

filenames.map(Paths::get)
  .filter(Files::exists)
  .map(p -> {
        try {
            return Optional.of(Files.newBufferedReader(p));
        } catch (IOException e) {
            return Optional.empty();
        }
    }) 
 .filter(Optional::isPresent)
 .map(Optional::get)
 .flatMap(reader -> {
   try(Reader r = reader) {

      //read from your reader here and return the items to flatten

   } //reader is implicitly closed here
  }) 

Example 1 has the advantage that the reader gets certainly closed. Example 2 is safe unless you put something more between the the creation of the reader and the try-with-resources block that may fail.

I personally would go for Example 1, and put the code that is accessing the reader in a separate function so the code is better readable.

2 of 5
3

Perhaps a better solution is to use a Consumer<FileReader> to consume each element in the stream.

Another problem you might be running into if there are a lot of files is the files will all be open at the same time. It might be better to close each one as soon as it's done.

Let's say you change the code above into a method that takes a Consumer<BufferedReader>

I probably wouldn't use a stream for this but we can use one anyway to show how one would use it.

public void readAllFiles( Consumer<BufferedReader> consumer){
    Objects.requireNonNull(consumer);

    filenames.map(File::new)
             .filter(File::exists)
             .forEach(f->{

                 try(BufferedReader br = new BufferedReader(new FileReader(f))){
                     consumer.accept(br);
                 } catch(Exception e) {
                     //handle exception
                 }
             });
}

This way we make sure we close each reader and can still support doing whatever the user wants.

For example this would still work

 readAllFiles( br -> System.out.println( br.lines().count()));
🌐
Oracle
java.com › en › download › manual.jsp
Download Java
This download is for end users who need Java for running applications on desktops or laptops. Java 8 integrates with your operating system to run separately installed Java applications.
🌐
Niraj Sonawane
nirajsonawane.github.io › 2018 › 05 › 29 › Java-8-List-all-Files-in-Directory-and-Subdirectories
Java 8 List all Files in Directory and Subdirectories | Niraj Sonawane
June 10, 2018 - List All Files in Directory and SubdirectoriesFiles.walk Return a Stream that is lazily populated with Path by walking the file tree rooted at a given starting file. Files.list Method Return a lazily
🌐
HappyCoders.eu
happycoders.eu › java › how-to-switch-multiple-java-versions-windows
How to Change Java Versions in Windows (up to Java 25)
June 11, 2025 - Every time you run one of those scripts, a new JAVA_HOME entry is prepended to your path. As an example, I ran java8.bat three times in a row, and this is the start of my PATH: "C:\Program Files\Java\jre1.8.0_201\bin;C:\Program Files\Java\jre1.8.0_201\bin;C:\Program Files\Java\jre1.8.0_201\bin".
🌐
HowToDoInJava
howtodoinjava.com › home › java 8 › listing all files in a directory in java
Listing All Files in a Directory in Java
October 1, 2022 - List<File> files = Files.list(Paths.get(dirLocation)) .filter(path -> path.toFile().isHidden()) .map(Path::toFile) .collect(Collectors.toList()); In the above examples, we learn to use the java 8 APIs loop through the files in a directory ...
🌐
Nextptr
nextptr.com › tutorial › ta1321112508 › file-io-with-java-8-streams
File IO with Java 8 Streams - nextptr
This article assumes some familiarity with the Stream API. In the next section, we will see some examples of 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.