Featured resource
2025 Tech Upskilling Playbook
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Check it out
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Core Tech
Labs

Guided: Java SE Streams Methods

In this Code Lab, you'll build efficient data processing pipelines for a book inventory system using the Java Streams API. You will implement filtering, mapping, and reduction operations to query data and generate analytical reports, culminating in an exploration of parallel streams for performance optimization.

Lab platform
Lab Info
Level
Intermediate
Last updated
Oct 24, 2025
Duration
30m

Contact sales

By clicking submit, you agree to our Privacy Policy and Terms of Use.
Table of Contents
  1. Challenge

    Step 1: Introduction and Setup

    Welcome to the Java Streams Data Pipeline Lab! In this lab, you'll learn how to replace traditional for loops with modern, declarative, and highly efficient stream-based code.

    The Java Streams API, introduced in Java 8 and enhanced in subsequent versions, allows you to process sequences of elements in a functional style. A stream pipeline consists of:

    1. A source: Such as a List or an array.
    2. Zero or more intermediate operations: These transform the stream (e.g., filter(), map(), sorted()). They are lazy, meaning they don't execute until a terminal operation is invoked.
    3. A terminal operation: This produces a result or a side-effect (e.g., collect(), forEach(), count()).

    There is a provided a sample dataset of books and two service classes, BookInventoryService and BookAnalyticsService, with empty methods. Your job is to fill in these methods using the Streams API.

    Move to the next step to get started!

    This lab experience was developed by the Pluralsight team using Forge, an internally developed AI tool utilizing Gemini technology. All sections were verified by human experts for accuracy prior to publication. For issue reporting, please contact us.

  2. Challenge

    Step 2: Basic Filtering

    Filtering is one of the most common stream operations. The filter() method takes a Predicate (a function that returns a boolean) and returns a new stream containing only the elements that match the predicate.

    To complete a stream pipeline, you need a terminal operation. collect(Collectors.toList()) is a common choice that gathers all stream elements into a List.

    In this step, you'll implement two basic filters in the BookInventoryService.

  3. Challenge

    Step 3: Advanced Filtering and Mapping

    Stream operations are designed to be chained together to form a processing pipeline. You can apply multiple filter() calls to create more complex queries.

    Another key operation is map(). It transforms each element in the stream into a new element. For example, you can map a stream of Book objects to a stream of their String titles. This is useful for extracting specific data fields before collecting the results.

  4. Challenge

    Step 4: Analytics with Reductions

    Reduction operations (or terminal operations) consume the stream to produce a single summary result. So far, you've used collect(), but there are many others.

    For numeric streams, you can use specialized operations like sum(), average(), min(), and max(). To use them, you first need to convert a Stream<Book> to a primitive stream like IntStream or DoubleStream using methods like mapToInt() or mapToDouble(). This avoids the overhead of using boxed types (Integer, Double) and is more performant.

    Now you'll switch to the BookAnalyticsService to implement some of these reductions.

  5. Challenge

    Step 5: Advanced Reductions and Grouping

    The collect() terminal operation is incredibly versatile, thanks to the Collectors utility class. One of its most powerful features is Collectors.groupingBy(), which lets you group elements of a stream into a Map based on a classification function.

    Sorting and limiting are also essential for reporting. The sorted() intermediate operation allows you to sort stream elements, and limit() truncates the stream to a specific size. These are perfect for creating 'Top N' reports.

  6. Challenge

    Step 6: Performance Optimization with Parallel Streams

    For very large datasets, stream processing can be a bottleneck. The Streams API provides a simple way to parallelize operations: parallelStream(). By calling parallelStream() instead of stream(), the Java runtime will attempt to divide the work among multiple threads, potentially leading to significant performance gains on multi-core processors.

    However, parallel streams are not a magic bullet. They have overhead and are not suitable for all tasks. They work best on large datasets where the processing for each element is independent and computationally significant. For simple operations on small collections, a sequential stream is often faster.

    In this final step, you'll convert a query to use a parallel stream. There is a JMH (Java Microbenchmark Harness) class in the starter code (StreamPerformanceBenchmark.java) that you can inspect to see how one might professionally benchmark the difference.

  7. Challenge

    Step 7: Compiling and Running

    With all your classes written, the final step is to compile and run them. When working with packages, you can't just run javac *.java. You need to use specific flags to tell the compiler about your project structure. ​

    • javac -d <output_dir>: The -d flag specifies the directory where the compiled .class files should be placed. The compiler will automatically create the package directory structure inside this output directory.
    • -sourcepath <source_dir>: This tells the compiler where to find the .java source files. ​ You will compile all your files into a bin directory. To run the compiled code, you'll use the java command. You need to tell the Java Virtual Machine (JVM) where to find your compiled classes using the classpath flag.

    java -cp <classpath> or java -classpath <classpath>: This flag sets the path where the JVM will look for classes. Since you compiled everything into the bin directory, that will be your classpath. com.library.Main: After the classpath, you provide the fully qualified name of the class that contains the main method you want to execute. ### Congratulations! You've successfully built a structured Java library application! These core concepts are essential for professional Java development.

About the author

Pluralsight Code Labs offer an opportunity to get hands-on learning in real-time. Be it a Challenge, Sandbox, or Guided Lab, these provide the real world experience needed to succeed in your development role.

Real skill practice before real-world application

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Learn by doing

Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.

Follow your guide

All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.

Turn time into mastery

On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.

Get started with Pluralsight