java-streams-api

Master Java's Streams API for functional-style operations on collections, enabling declarative data processing with operations like filter, map, and reduce.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "java-streams-api" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-java-streams-api

Java Streams API

Master Java's Streams API for functional-style operations on collections, enabling declarative data processing with operations like filter, map, and reduce.

Introduction to Streams

Streams provide a functional approach to processing collections of objects. Unlike collections, streams don't store elements - they convey elements from a source through a pipeline of operations.

Creating streams:

import java.util.Arrays; import java.util.List; import java.util.stream.Stream;

public class StreamCreation { public static void main(String[] args) { // From collection List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream1 = list.stream();

    // From array
    String[] array = {"a", "b", "c"};
    Stream&#x3C;String> stream2 = Arrays.stream(array);

    // Using Stream.of()
    Stream&#x3C;String> stream3 = Stream.of("a", "b", "c");

    // Empty stream
    Stream&#x3C;String> stream4 = Stream.empty();

    // Infinite stream with limit
    Stream&#x3C;Integer> stream5 = Stream.iterate(0, n -> n + 1)
                                   .limit(10);
}

}

Intermediate Operations

Intermediate operations return a new stream and are lazy - they don't execute until a terminal operation is invoked.

filter() - Select elements:

import java.util.List; import java.util.stream.Collectors;

public class FilterExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);

    // Filter even numbers
    List&#x3C;Integer> evenNumbers = numbers.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList());
    // Result: [2, 4, 6, 8]

    // Multiple filters can be chained
    List&#x3C;Integer> result = numbers.stream()
        .filter(n -> n > 3)
        .filter(n -> n &#x3C; 7)
        .collect(Collectors.toList());
    // Result: [4, 5, 6]
}

}

map() - Transform elements:

public class MapExample { public static void main(String[] args) { List<String> words = List.of("hello", "world");

    // Convert to uppercase
    List&#x3C;String> uppercase = words.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toList());
    // Result: ["HELLO", "WORLD"]

    // Get string lengths
    List&#x3C;Integer> lengths = words.stream()
        .map(String::length)
        .collect(Collectors.toList());
    // Result: [5, 5]

    // Chain transformations
    List&#x3C;Integer> doubled = List.of(1, 2, 3).stream()
        .map(n -> n * 2)
        .collect(Collectors.toList());
    // Result: [2, 4, 6]
}

}

flatMap() - Flatten nested structures:

public class FlatMapExample { public static void main(String[] args) { List<List<Integer>> nested = List.of( List.of(1, 2), List.of(3, 4), List.of(5, 6) );

    // Flatten to single list
    List&#x3C;Integer> flattened = nested.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());
    // Result: [1, 2, 3, 4, 5, 6]

    // Split strings and flatten
    List&#x3C;String> sentences = List.of("hello world", "foo bar");
    List&#x3C;String> words = sentences.stream()
        .flatMap(s -> Arrays.stream(s.split(" ")))
        .collect(Collectors.toList());
    // Result: ["hello", "world", "foo", "bar"]
}

}

distinct() and sorted():

public class DistinctSortedExample { public static void main(String[] args) { List<Integer> numbers = List.of(5, 2, 8, 2, 1, 5, 3);

    // Remove duplicates
    List&#x3C;Integer> distinct = numbers.stream()
        .distinct()
        .collect(Collectors.toList());
    // Result: [5, 2, 8, 1, 3]

    // Sort ascending
    List&#x3C;Integer> sorted = numbers.stream()
        .sorted()
        .collect(Collectors.toList());
    // Result: [1, 2, 2, 3, 5, 5, 8]

    // Sort descending
    List&#x3C;Integer> descending = numbers.stream()
        .sorted((a, b) -> b - a)
        .collect(Collectors.toList());

    // Distinct and sorted
    List&#x3C;Integer> distinctSorted = numbers.stream()
        .distinct()
        .sorted()
        .collect(Collectors.toList());
    // Result: [1, 2, 3, 5, 8]
}

}

peek() - Debug or perform side effects:

public class PeekExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5);

    // Debug stream pipeline
    List&#x3C;Integer> result = numbers.stream()
        .peek(n -> System.out.println("Original: " + n))
        .map(n -> n * 2)
        .peek(n -> System.out.println("Doubled: " + n))
        .filter(n -> n > 5)
        .peek(n -> System.out.println("Filtered: " + n))
        .collect(Collectors.toList());
}

}

Terminal Operations

Terminal operations produce a result or side effect and close the stream.

collect() - Gather results:

import java.util.stream.Collectors; import java.util.Map; import java.util.Set;

public class CollectExample { public static void main(String[] args) { List<String> words = List.of("apple", "banana", "cherry");

    // To List
    List&#x3C;String> list = words.stream()
        .collect(Collectors.toList());

    // To Set
    Set&#x3C;String> set = words.stream()
        .collect(Collectors.toSet());

    // To Map
    Map&#x3C;String, Integer> map = words.stream()
        .collect(Collectors.toMap(
            w -> w,              // Key
            String::length       // Value
        ));
    // Result: {apple=5, banana=6, cherry=6}

    // Joining strings
    String joined = words.stream()
        .collect(Collectors.joining(", "));
    // Result: "apple, banana, cherry"

    // Grouping by length
    Map&#x3C;Integer, List&#x3C;String>> grouped = words.stream()
        .collect(Collectors.groupingBy(String::length));
    // Result: {5=[apple], 6=[banana, cherry]}
}

}

reduce() - Combine elements:

public class ReduceExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5);

    // Sum with identity
    int sum = numbers.stream()
        .reduce(0, (a, b) -> a + b);
    // Result: 15

    // Product
    int product = numbers.stream()
        .reduce(1, (a, b) -> a * b);
    // Result: 120

    // Max value
    Optional&#x3C;Integer> max = numbers.stream()
        .reduce((a, b) -> a > b ? a : b);
    // Result: Optional[5]

    // Using method reference
    int sum2 = numbers.stream()
        .reduce(0, Integer::sum);

    // String concatenation
    String concatenated = List.of("a", "b", "c").stream()
        .reduce("", (a, b) -> a + b);
    // Result: "abc"
}

}

forEach() and forEachOrdered():

public class ForEachExample { public static void main(String[] args) { List<String> words = List.of("hello", "world");

    // Print each element
    words.stream()
        .forEach(System.out::println);

    // Parallel stream with ordered iteration
    words.parallelStream()
        .forEachOrdered(System.out::println);

    // With side effects (use cautiously)
    List&#x3C;String> results = new ArrayList&#x3C;>();
    words.stream()
        .map(String::toUpperCase)
        .forEach(results::add);
}

}

count(), anyMatch(), allMatch(), noneMatch():

public class MatchingExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5);

    // Count elements
    long count = numbers.stream()
        .filter(n -> n > 2)
        .count();
    // Result: 3

    // Check if any match
    boolean hasEven = numbers.stream()
        .anyMatch(n -> n % 2 == 0);
    // Result: true

    // Check if all match
    boolean allPositive = numbers.stream()
        .allMatch(n -> n > 0);
    // Result: true

    // Check if none match
    boolean noNegative = numbers.stream()
        .noneMatch(n -> n &#x3C; 0);
    // Result: true
}

}

findFirst() and findAny():

public class FindExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5);

    // Find first element
    Optional&#x3C;Integer> first = numbers.stream()
        .filter(n -> n > 2)
        .findFirst();
    // Result: Optional[3]

    // Find any (useful in parallel streams)
    Optional&#x3C;Integer> any = numbers.parallelStream()
        .filter(n -> n > 2)
        .findAny();
    // Result: Optional[3] or Optional[4] or Optional[5]

    // Handle empty result
    Integer value = numbers.stream()
        .filter(n -> n > 10)
        .findFirst()
        .orElse(-1);
    // Result: -1
}

}

Advanced Collectors

Partitioning and grouping:

public class AdvancedCollectors { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);

    // Partition by predicate
    Map&#x3C;Boolean, List&#x3C;Integer>> partitioned = numbers.stream()
        .collect(Collectors.partitioningBy(n -> n % 2 == 0));
    // Result: {false=[1,3,5], true=[2,4,6]}

    // Group by with counting
    Map&#x3C;Integer, Long> lengthCounts = List.of("a", "bb", "ccc").stream()
        .collect(Collectors.groupingBy(
            String::length,
            Collectors.counting()
        ));
    // Result: {1=1, 2=1, 3=1}

    // Downstream collectors
    Map&#x3C;Integer, List&#x3C;String>> grouped =
        List.of("apple", "apricot", "banana").stream()
        .collect(Collectors.groupingBy(
            String::length,
            Collectors.mapping(
                String::toUpperCase,
                Collectors.toList()
            )
        ));
    // Result: {5=[APPLE], 6=[BANANA], 7=[APRICOT]}
}

}

Statistics collectors:

import java.util.IntSummaryStatistics; import java.util.stream.Collectors;

public class StatisticsExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5);

    // Summary statistics
    IntSummaryStatistics stats = numbers.stream()
        .collect(Collectors.summarizingInt(Integer::intValue));

    System.out.println("Count: " + stats.getCount());     // 5
    System.out.println("Sum: " + stats.getSum());         // 15
    System.out.println("Min: " + stats.getMin());         // 1
    System.out.println("Max: " + stats.getMax());         // 5
    System.out.println("Average: " + stats.getAverage()); // 3.0

    // Averaging
    double average = numbers.stream()
        .collect(Collectors.averagingInt(Integer::intValue));
    // Result: 3.0
}

}

Parallel Streams

Parallel streams automatically partition data and process in parallel.

Using parallel streams:

public class ParallelStreamExample { public static void main(String[] args) { List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);

    // Convert to parallel stream
    int sum = numbers.parallelStream()
        .filter(n -> n % 2 == 0)
        .mapToInt(Integer::intValue)
        .sum();

    // From sequential to parallel
    long count = numbers.stream()
        .parallel()
        .filter(n -> n > 3)
        .count();

    // Check if parallel
    boolean isParallel = numbers.parallelStream().isParallel();
    // Result: true

    // Back to sequential
    List&#x3C;Integer> result = numbers.parallelStream()
        .sequential()
        .collect(Collectors.toList());
}

}

Performance considerations:

import java.util.stream.IntStream;

public class ParallelPerformance { public static void main(String[] args) { // Small dataset - sequential is faster List<Integer> small = IntStream.range(0, 100) .boxed() .collect(Collectors.toList());

    // Large dataset - parallel may be faster
    List&#x3C;Integer> large = IntStream.range(0, 1_000_000)
        .boxed()
        .collect(Collectors.toList());

    // Sequential
    long start = System.nanoTime();
    long sum1 = large.stream()
        .mapToLong(Integer::longValue)
        .sum();
    long sequential = System.nanoTime() - start;

    // Parallel
    start = System.nanoTime();
    long sum2 = large.parallelStream()
        .mapToLong(Integer::longValue)
        .sum();
    long parallel = System.nanoTime() - start;

    System.out.println("Sequential: " + sequential);
    System.out.println("Parallel: " + parallel);
}

}

Primitive Streams

Specialized streams for primitive types avoid boxing overhead.

IntStream, LongStream, DoubleStream:

import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.DoubleStream;

public class PrimitiveStreams { public static void main(String[] args) { // IntStream range IntStream.range(1, 5) .forEach(System.out::println); // 1, 2, 3, 4

    // IntStream rangeClosed (inclusive)
    IntStream.rangeClosed(1, 5)
        .forEach(System.out::println); // 1, 2, 3, 4, 5

    // Sum of IntStream
    int sum = IntStream.of(1, 2, 3, 4, 5).sum();
    // Result: 15

    // Average
    double avg = IntStream.of(1, 2, 3, 4, 5)
        .average()
        .orElse(0.0);
    // Result: 3.0

    // mapToInt to avoid boxing
    int total = List.of(1, 2, 3, 4, 5).stream()
        .mapToInt(Integer::intValue)
        .sum();

    // Generate random numbers
    DoubleStream.generate(Math::random)
        .limit(5)
        .forEach(System.out::println);
}

}

Real-World Examples

Processing business objects:

class Employee { private String name; private String department; private double salary;

public Employee(String name, String department, double salary) {
    this.name = name;
    this.department = department;
    this.salary = salary;
}

// Getters
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }

}

public class EmployeeProcessing { public static void main(String[] args) { List<Employee> employees = List.of( new Employee("Alice", "Engineering", 80000), new Employee("Bob", "Engineering", 90000), new Employee("Charlie", "Sales", 70000), new Employee("Diana", "Sales", 75000) );

    // Average salary by department
    Map&#x3C;String, Double> avgSalaryByDept = employees.stream()
        .collect(Collectors.groupingBy(
            Employee::getDepartment,
            Collectors.averagingDouble(Employee::getSalary)
        ));
    // Result: {Engineering=85000.0, Sales=72500.0}

    // Highest paid employee
    Optional&#x3C;Employee> highestPaid = employees.stream()
        .max((e1, e2) -> Double.compare(e1.getSalary(),
                                        e2.getSalary()));

    // Total salary by department
    Map&#x3C;String, Double> totalByDept = employees.stream()
        .collect(Collectors.groupingBy(
            Employee::getDepartment,
            Collectors.summingDouble(Employee::getSalary)
        ));

    // Employees earning over 75k
    List&#x3C;String> highEarners = employees.stream()
        .filter(e -> e.getSalary() > 75000)
        .map(Employee::getName)
        .collect(Collectors.toList());
}

}

File processing example:

import java.nio.file.Files; import java.nio.file.Paths; import java.io.IOException;

public class FileProcessing { public static void main(String[] args) throws IOException { // Read file lines as stream Files.lines(Paths.get("data.txt")) .filter(line -> !line.isEmpty()) .map(String::trim) .forEach(System.out::println);

    // Count words in file
    long wordCount = Files.lines(Paths.get("data.txt"))
        .flatMap(line -> Arrays.stream(line.split("\\s+")))
        .count();

    // Find unique words
    Set&#x3C;String> uniqueWords = Files.lines(Paths.get("data.txt"))
        .flatMap(line -> Arrays.stream(line.split("\\s+")))
        .map(String::toLowerCase)
        .collect(Collectors.toSet());
}

}

When to Use This Skill

Use java-streams-api when you need to:

  • Process collections with functional-style operations

  • Filter, map, or transform data declaratively

  • Aggregate or reduce collections to single values

  • Group or partition data by criteria

  • Chain multiple data transformations

  • Process large datasets in parallel

  • Write more readable collection processing code

  • Avoid explicit loops and mutable state

  • Perform lazy evaluation of operations

  • Work with infinite sequences efficiently

Best Practices

  • Use method references when possible for readability

  • Avoid side effects in stream operations

  • Close streams from I/O sources (Files.lines, etc.)

  • Prefer collect() over forEach() for accumulation

  • Use primitive streams to avoid boxing overhead

  • Keep stream pipelines readable with proper formatting

  • Use parallel streams only for large datasets

  • Don't reuse streams - they're one-time use

  • Prefer Optional over null checks in results

  • Use Collectors factory methods for common operations

Common Pitfalls

  • Reusing streams after terminal operation (throws exception)

  • Modifying source collection during stream processing

  • Using parallel streams for small datasets (overhead cost)

  • Side effects in stateless operations (unpredictable results)

  • Not handling Optional results properly

  • Excessive chaining making code unreadable

  • Forgetting to close streams from I/O sources

  • Using forEach() when collect() is more appropriate

  • Not considering thread safety in parallel streams

  • Performance issues from unnecessary boxing/unboxing

Resources

  • Java Streams Documentation

  • Java Stream API Guide

  • Collectors Documentation

  • Parallel Streams Guide

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

android-jetpack-compose

No summary provided by upstream source.

Repository SourceNeeds Review
General

fastapi-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

storybook-story-writing

No summary provided by upstream source.

Repository SourceNeeds Review