Lambdas, streams, and files
Exceptions, collections and modern Java13 min readLesson 6 of 6
6.1 Lambdas
A lambda is a short implementation of a functional interface. (a, b) -> a + b gives the parameters and the result. The compiler uses the target type to find the parameter types and the method it implements.
Runnable task = () -> System.out.println("Done"); implements run. A lambda with one expression can omit braces; a block uses braces and an explicit return when a value is needed.
A method reference is shorter when an existing method does the work: String::toUpperCase in place of text -> text.toUpperCase(). Use it when the lambda would only pass its arguments through.
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
Runnable task = () -> System.out.println("Task done");
task.run();
List<String> names = new ArrayList<>(List.of("Ravi", "Asha"));
names.sort((a, b) -> a.compareTo(b));
System.out.println(names);
}
}Output
Task done [Asha, Ravi]
6.1.1 Stream pipelines
A stream processes a collection as a pipeline. stream() starts it, filter keeps matching elements, map transforms each one, and a terminal operation such as count, toList, or collect produces a result.
Intermediate operations are lazy. Nothing runs until a terminal operation asks for a result. That allows the implementation to combine steps and skip work.
A stream does not change the source collection. It reads from it and produces a new result. Reusing a stream after a terminal operation throws IllegalStateException; create a new stream from the source.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> marks = List.of(45, 72, 88, 61, 90);
long passed = marks.stream()
.filter(mark -> mark >= 60)
.count();
List<Integer> top = marks.stream()
.filter(mark -> mark >= 60)
.map(mark -> mark + 5)
.limit(2)
.toList();
System.out.println(passed);
System.out.println(top);
}
}Output
4 [77, 93]
6.1.2 Collecting results
toList() collects the elements into an unmodifiable list. collect is the general form and also accepts collectors such as Collectors.joining(", "), which builds a string from text elements.
mapToInt converts objects to primitive ints and provides sum, average, and max. These avoid boxing each value and return an IntStream with numeric operations.
Keep a pipeline short enough to read in one pass. If the logic needs names and branches, a loop may be clearer. Streams help most when each step transforms the collection in a clear way.
import java.util.List;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> names = List.of("Asha", "Ravi", "Meera");
String line = names.stream()
.filter(name -> name.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.joining(", "));
System.out.println(line);
System.out.println(names.stream().mapToInt(String::length).sum());
}
}Output
ASHA, RAVI, MEERA 13
6.1.3 Files with java.nio
Files.writeString(path, text) writes text to a file, creating or replacing it. Files.readString(path) reads the whole file into a String. Both are in java.nio.file and both can throw IOException.
Path.of("notes.txt") creates a path object for a relative file in the working directory. The methods take a Path object, so a file name string needs Path.of(...) first.
For large files, read line by line or use Files.lines(path) as a stream. For a short configuration file or a small note, reading the whole text is simpler.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) throws IOException {
Path file = Path.of("notes.txt");
Files.writeString(file, "line one\nline two");
String content = Files.readString(file);
System.out.println(content.lines().count());
System.out.println(content.toUpperCase());
}
}Output
2 LINE ONE LINE TWO
6.1.4 Resources and exceptions
File operations throw checked exceptions. A method can declare throws IOException and let the caller handle it, or catch it and report a useful message. The declaration is often enough in a small example; a larger program needs a decision about retry, fallback, or exit.
Files.lines(path) returns a stream that holds an open file. Use try-with-resources so the file closes when the stream finishes. Files.readAllLines returns a list and closes the file for you.
A path can point outside the working directory. Validate user-supplied paths before opening them, especially in a program that runs with access to a real file system.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
Path file = Path.of("tasks.txt");
Files.write(file, List.of("read", "practice", "review"));
List<String> lines = Files.readAllLines(file);
for (String line : lines) {
System.out.println(line.toUpperCase());
}
}
}Output
READ PRACTICE REVIEW
6.1.5 Practice
- Sort a mutable list of names with a lambda comparator.
- Filter marks above 60, add 5 to each, and collect the first two into a list.
- Join names longer than four characters with
", "and print the result. - Write two lines to a file and read them back with
Files.readString. - Write a list of tasks to a file, read all lines, and print each in uppercase.
For step 4, print the line count with content.lines().count(). The count should match the number of lines you wrote, with no final empty line.
