Backend Development 26 min read

Comprehensive Guide to Java 8 Stream API with Practical Code Examples

This article provides an in-depth tutorial on Java 8 Stream API, covering its concepts, creation methods, intermediate and terminal operations, mapping, reduction, collection, grouping, sorting, and includes numerous practical code examples to help developers master functional programming with streams.

Top Architect
Top Architect
Top Architect
Comprehensive Guide to Java 8 Stream API with Practical Code Examples

Java 8 introduced the Stream API and Lambda expressions, allowing developers to write more concise and expressive code for processing collections. The article begins by explaining that a Stream represents a sequence of elements on which various operations can be performed, such as filtering, sorting, and aggregating.

The Stream overview defines a stream as a view of a data source that supports declarative processing. It distinguishes between intermediate operations (which return a new stream) and terminal operations (which produce a result or side‑effect).

Creating streams can be done in three main ways:

1. From a collection: List list = Arrays.asList("a", "b", "c"); Stream stream = list.stream(); Stream parallelStream = list.parallelStream();

2. From an array: int[] array = {1, 3, 5, 6, 8}; IntStream stream = Arrays.stream(array);

3. Using static factory methods: Stream s1 = Stream.of(1, 2, 3, 4, 5, 6); Stream s2 = Stream.iterate(0, x -> x + 3).limit(4); Stream s3 = Stream.generate(Math::random).limit(3);

Intermediate operations such as filter , map , and flatMap are demonstrated with an employee class ( Person ) and simple numeric lists. Example of filtering employees with salary > 8000:

List highEarners = personList.stream() .filter(p -> p.getSalary() > 8000) .map(Person::getName) .collect(Collectors.toList());

Mapping examples include converting strings to uppercase and adding 3 to each integer:

List upper = Arrays.stream(strArr) .map(String::toUpperCase) .collect(Collectors.toList()); List plusThree = intList.stream() .map(x -> x + 3) .collect(Collectors.toList());

FlatMap is used to split strings and flatten the result into a single list:

List flat = list.stream() .flatMap(s -> Arrays.stream(s.split(","))) .collect(Collectors.toList());

Terminal operations such as forEach , findFirst , anyMatch , max , and min are shown. Example of finding the first element greater than 6:

Optional first = list.stream() .filter(x -> x > 6) .findFirst();

Reduction examples calculate sum, product, and maximum of an integer list:

Optional sum = list.stream().reduce(Integer::sum); Optional product = list.stream().reduce((x, y) -> x * y); Optional max = list.stream().max(Integer::compareTo);

Collecting results uses Collectors to gather stream elements into lists, sets, maps, and to perform statistical calculations. Examples:

To list and set: List evens = list.stream() .filter(x -> x % 2 == 0) .collect(Collectors.toList()); Set evenSet = list.stream() .filter(x -> x % 2 == 0) .collect(Collectors.toSet());

To map by name: Map nameMap = personList.stream() .filter(p -> p.getSalary() > 8000) .collect(Collectors.toMap(Person::getName, p -> p));

Statistical collectors: Long count = personList.stream().collect(Collectors.counting()); Double avg = personList.stream().collect(Collectors.averagingDouble(Person::getSalary)); Integer total = personList.stream().collect(Collectors.summingInt(Person::getSalary)); DoubleSummaryStatistics stats = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

Grouping and partitioning examples demonstrate how to split employees by salary threshold, gender, and region:

Map > bySalary = personList.stream() .collect(Collectors.partitioningBy(p -> p.getSalary() > 8000)); Map > byGender = personList.stream() .collect(Collectors.groupingBy(Person::getSex)); Map >> byGenderAndArea = personList.stream() .collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));

Joining strings is illustrated with: String names = personList.stream() .map(Person::getName) .collect(Collectors.joining(","));

Sorting shows natural order and custom comparators, including sorting by salary descending and then by age descending:

List bySalary = personList.stream() .sorted(Comparator.comparing(Person::getSalary).reversed()) .map(Person::getName) .collect(Collectors.toList()); List custom = personList.stream() .sorted((p1, p2) -> { if (p1.getSalary() == p2.getSalary()) { return p2.getAge() - p1.getAge(); } return p2.getSalary() - p1.getSalary(); }) .map(Person::getName) .collect(Collectors.toList());

Extraction and combination demonstrates concatenating two streams, removing duplicates, limiting, and skipping elements:

List merged = Stream.concat(stream1, stream2) .distinct() .collect(Collectors.toList()); List limited = Stream.iterate(1, x -> x + 2) .limit(10) .collect(Collectors.toList()); List skipped = Stream.iterate(1, x -> x + 2) .skip(1) .limit(5) .collect(Collectors.toList());

The article also defines the Person class used throughout the examples, showing fields such as name, salary, age, sex, and area, along with a constructor and getters/setters.

Overall, the guide equips Java developers with a complete reference to the Stream API, illustrating how to write expressive, functional‑style code for data processing, aggregation, and transformation in backend applications.

JavaFunctional ProgrammingCollectionscode examplesJava8Stream API
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.