Skip to content

Java Streams

In Java, a Stream represents a sequence of elements that supports sequential and parallel aggregate operations. Streams enable functional-style operations on collections of elements through a declarative API, allowing for efficient data processing with features like filtering, mapping, and reduction.

Key Characteristics:

  • Lazy Evaluation: Intermediate operations are only executed when a terminal operation is invoked
  • Non-Mutating: Operations return new streams rather than modifying the source
  • Pipeline Architecture: Operations can be chained to form complex processing workflows
  • Redability: Allow operations on data in clear and concise manner, making the code more readable and maintanable.

Stream Creation Methods

1. From Collections

List<String> items = Arrays.asList("Item1", "Item2", "Item3");
Stream<String> stream = items.stream();

2. From Arrays

String[] array = {"A", "B", "C"};
Stream<String> stream = Arrays.stream(array);

3. Using Stream.of()

Stream<Integer> numberStream = Stream.of(1, 2, 3);

4. From Files

try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}

Stream Operations

Intermediate Operations

Operation Description Example
filter() Selects elements matching a predicate .filter(s -> s.length() > 5)
map() Applies a function to transform elements .map(String::toUpperCase)
distinct() Removes duplicate elements .distinct()
sorted() Orders elements according to natural order or specified comparator .sorted(Comparator.reverseOrder())
limit() Restricts stream to first n elements .limit(10)
peek() Performs action without modifying stream (debugging helper) .peek(System.out::println)

Terminal Operations

Operation Description Example
forEach() Executes action for each element .forEach(System.out::println)
collect() Accumulates elements into collection or summary object .collect(Collectors.toList())
count() Returns total number of elements .filter(s -> !s.isEmpty()).count()
anyMatch() Returns true if any element matches predicate .anyMatch(s -> s.contains("error"))
allMatch() Returns true if all elements match predicate .allMatch(s -> s.length() > 0))
noneMatch() Returns true if no elements match predicate .noneMatch(s -> s == null))
reduce() Combines elements using accumulation function .reduce(0, (a,b) -> a + b))
findFirst() Returns Optional describing first element .findFirst()

Best Practices

  1. Prefer Method References:
    map(String::length) instead of map(s -> s.length())

  2. Avoid Side Effects:
    Use collect() before performing output operations

  3. Reuse Predicates:

Predicate<String> lengthFilter = s -> s.length() > 5;
stream.filter(lengthFilter)... 
  1. Parallelization:
    Use .parallelStream() judiciously for CPU-intensive operations

Performance Considerations

  • Stream pipelines execute only when terminal operation is called
  • Intermediate operations have O(1) time complexity in isolation
  • Complex pipelines may benefit from parallel processing
  • Avoid nested streams (consider flatMap() instead)