Basics of Functional Programming

1. What Is Functional Style?

Java's Stream API makes it easier to refactor many common loops into a more declarative style.


2. Common Refactorings

Imperative Functional
for loop IntStream.range()
for loop with custom step IntStream.iterate() + takeWhile()
foreach + if filter()
Loop + transformation map()
Read file line by line Files.lines()

3. Simple for Loop → IntStream.range()

Imperative

for (int i = 0; i < n; i++) {
    // action
}

Functional

IntStream.range(0, n)
         .forEach(i -> {
             // action
         });

range(0, n) generates:

0, 1, 2, ..., n - 1

For an inclusive upper limit, use rangeClosed():

IntStream.rangeClosed(1, 5)
         .forEachprintln;

Output:

1
2
3
4
5

4. Loop with a Custom Step

Imperative

for (int i = 0; i < n; i += step) {
    // action
}

Functional

IntStream.iterate(0, i -> i + step)
         .takeWhile(i -> i < n)
         .forEach(i -> {
             // action
         });

For example:

IntStream.iterate(0, i -> i + 2)
         .takeWhile(i -> i < 10)
         .forEachprintln;

Output:

0
2
4
6
8

5. foreach + iffilter()

Imperative

for (String s : list) {
    if (s.length() > 3) {
        // action
    }
}

Functional

list.stream()
    .filter(s -> s.length() > 3)
    .forEach(s -> {
        // action
    });

Think of filter() as:

"Keep only the elements that satisfy this condition."


6. Loop + Transformation → map()

Suppose we want to convert every string into its length.

Imperative

List<Integer> result = new ArrayList<>();

for (String s : list) {
    result.add(s.length());
}

Functional

List<Integer> result = list.stream()
                           .maplength
                           .collect(Collectors.toList());

Here:

.maplength

means:

String → Integer

For example:

["Java", "Python", "Go"]
       ↓ map(length)
[4, 6, 2]

7. Reading Files with Streams

Instead of manually reading every line:

Imperative

BufferedReader reader =
    new BufferedReader(new FileReader("file.txt"));

String line;

while ((line = reader.readLine()) != null) {
    // process line
}

Functional

Files.lines(Paths.get("file.txt"))
     .forEach(line -> {
         // process line
     });

Files.lines() creates a Stream<String> where each element represents a line.

Important: In real code, prefer try-with-resources so the file is properly closed:

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    lines.forEachprintln;
}

8. The Basic Stream Pipeline

Many functional-style operations follow this pattern:

Source
  ↓
filter()
  ↓
map()
  ↓
forEach() / collect()

Example:

List<String> result = list.stream()
                          .filter(s -> s.length() > 3)
                          .maptoUpperCase
                          .collect(Collectors.toList());

This means:

  1. Get the elements from list.

  2. Keep strings longer than 3 characters.

  3. Convert them to uppercase.

  4. Collect the result into a list.


Summary

Task Imperative Functional
Simple iteration for IntStream.range()
Inclusive range for IntStream.rangeClosed()
Custom step for (i += step) iterate() + takeWhile()
Filtering if filter()
Transformation result.add(...) map()
File processing while (readLine()) Files.lines()
Final action Loop body forEach()
Create collection Manually add collect()

In short

The most important conversions to remember are:

if       → filter()
convert  → map()
loop     → stream()
action   → forEach()
collect  → collect()

Functional style is especially useful for data processing pipelines, where you can express a sequence of transformations without manually managing loop counters and temporary variables.