VOOZH about

URL: https://dev.to/priyankbhardwaj1199/streams-with-optional-file-io-in-java-4h2

⇱ Streams with Optional & File I/O in Java - DEV Community


Java Streams become even more powerful when combined with Optional and file handling APIs. In this article, we’ll explore how to safely handle missing values using Optional, process files efficiently with Files.lines(), and build a practical example that counts unique words in a text file.

If you’ve followed the earlier parts of this series, you already know how Streams help process collections in a functional and readable way. Now it’s time to apply those concepts to real-world scenarios.

Why Combine Streams, Optional, and File I/O?

In modern Java applications, you often:

  • Read large files
  • Process textual data
  • Search for values that may or may not exist
  • Avoid NullPointerException
  • Write concise and clean code

Streams, Optional, and Java NIO APIs work together beautifully to solve these problems.


Using Optional with Streams

Optional<T> is a container object introduced in Java 8 that may or may not contain a value.

Streams often return Optional when the result may be absent.

Common Stream Methods Returning Optional

Method Return Type
findFirst() Optional<T>
findAny() Optional<T>
max() Optional<T>
min() Optional<T>
reduce() Optional<T>

Example: findFirst()

import java.util.List;
import java.util.Optional;

public class OptionalExample {
 public static void main(String[] args) {

 List<String> names = List.of("Aman", "Priyank", "Rahul");

 Optional<String> result = names.stream()
 .filter(name -> name.startsWith("P"))
 .findFirst();

 result.ifPresent(System.out::println);
 }
}

Output:

Priyank

Handling Empty Results Safely

Without Optional, missing values could lead to NullPointerException.

Using orElse()

String value = result.orElse("Not Found");

System.out.println(value);

If no matching element exists, "Not Found" is returned.

Using orElseGet()

String value = result.orElseGet(() -> "Default Value");

Useful when default value creation is expensive.

Using orElseThrow()

String value = result.orElseThrow(
 () -> new RuntimeException("Value not found")
);

Throws an exception if value is absent.


Example: Find Maximum Number

import java.util.List;
import java.util.Optional;

public class MaxExample {
 public static void main(String[] args) {

 List<Integer> numbers = List.of(10, 20, 50, 30);

 Optional<Integer> max = numbers.stream()
 .max(Integer::compareTo);

 max.ifPresent(System.out::println);
 }
}

Output

50

Here, numbers.stream().max() returns an Optional. ifPresent() is another inbuilt function on Optional<T> type value which perform an action (in this case printing the value to console) if some value is returned by the stream.


Reading Files Using Files.lines()

Java NIO introduced the Files.lines() method for efficient file reading.

Instead of loading the entire file into memory, it processes the file lazily line by line using Streams.

Syntax

Files.lines(Path path)

Returns Stream<String>. Each line of the file becomes a stream element.

Basic Example

Suppose sample.txt contains:

Java
Spring Boot
Streams API

import java.nio.file.Files;
import java.nio.file.Paths;

public class FileReadExample {
 public static void main(String[] args) {

 try {
 Files.lines(Paths.get("sample.txt"))
 .forEach(System.out::println);

 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

Output

Java
Spring Boot
Streams API

Why Files.lines() is Powerful

  • Memory efficient
  • Lazy processing
  • Works well with large files
  • Can directly use Stream operations

Example: Filter Lines

Files.lines(Paths.get("sample.txt"))
 .filter(line -> line.contains("Java"))
 .forEach(System.out::println);

Example: Count Lines

long count = Files.lines(Paths.get("sample.txt"))
 .count();

System.out.println(count);

Real-World Example: Count Unique Words in a Text File

Now let’s combine everything we learned.

We will:

  • Read a text file
  • Split lines into words
  • Convert words to lowercase
  • Remove duplicates
  • Count unique words

Sample file (article.txt)

Java Streams are powerful
Streams make Java code concise
Java is widely used

Program

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class UniqueWordCounter {

 public static void main(String[] args) {

 try (Stream<String> lines = Files.lines(Paths.get("article.txt"))){ // Read File as Stream 

 long uniqueWords = lines
 .flatMap(line -> Stream.of(line.split("\\s+"))) // Convert Lines into Words
 .map(String::toLowerCase) // convert the words to lowercase
 .distinct() // remove duplicates
 .count(); // count unique words

 System.out.println("Unique Words: " + uniqueWords);

 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

Output

Unique Words: 10

Another Example: Find First Long Word (word with more than 7 characters) Using Optional

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.stream.Stream;

public class LongWordFinder {

 public static void main(String[] args) {

 try (Stream<String> lines = Files.lines(Paths.get("article.txt"))) {

 Optional<String> word = lines
 .flatMap(line -> Stream.of(line.split("\\s+")))
 .filter(w -> w.length() > 7) // filter the words with length greater than 7 characters
 .findFirst(); // pick the first word among the filtered long words

 System.out.println(
 word.orElse("No long word found")
 );

 } catch (Exception e) {
 e.printStackTrace();
 }
 }
}

Best Practices

Always Use try-with-resources. Files.lines() opens a file resource. Always close it properly:

try (Stream<String> lines = Files.lines(path)) {
 // process
}

Prefer Stream Operations Over Manual Loops

Instead of:

for(String line : lines)

Use:

lines.filter(...)
 .map(...)

Cleaner and more functional.

Avoid Calling get() on Optional Directly

Bad:

optional.get();

Safer alternatives:

orElse()
orElseThrow()
ifPresent()


Conclusion

Streams are not limited to collections. When combined with Optional and Java NIO file APIs, they become a powerful toolkit for handling real-world data processing tasks efficiently and elegantly.

In this article, we learned how to:

  • Use Optional safely with Streams
  • Read files using Files.lines()
  • Process text data functionally
  • Count unique words in a file

These techniques are commonly used in backend systems, analytics tools, log processing applications, and Java projects.