DEV Community

Abhishek Singh
Abhishek Singh

Posted on

Java Stream API

Overview :
The Java Stream API facilitates processing sequences of elements, offering operations like filtering, mapping, and reducing. Streams can be used to perform operations in a declarative way, resembling SQL-like operations on data

Key Concepts :
Stream: A sequence of elements supporting sequential and parallel aggregate operations

Intermediate Operations: Operations that return another stream and are lazy (e.g., filter, map)

Terminal Operations: Operations that produce a result or a side-effect and are not lazy (e.g., collect, forEach)

Example Scenario :
Suppose we have a list of Person objects and we want to perform various operations on this list using the Stream API

public class Person { private String name; private int age; private String city; public Person(String name, int age, String city) { this.name = name; this.age = age; this.city = city; } public String getName() { return name; } public int getAge() { return age; } public String getCity() { return city; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + ", city='" + city + "'}"; } } 
Enter fullscreen mode Exit fullscreen mode

Use Cases :

  1. Filtering
  2. Mapping
  3. Collecting
  4. Reducing
  5. FlatMapping
  6. Sorting
  7. Finding and Matching
  8. Statistics

Filtering : Filtering allows you to select elements that match a given condition

import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Filter people older than 25 List<Person> filteredPeople = people.stream().filter(person -> person.getAge() > 25) .collect(Collectors.toList()); filteredPeople.forEach(System.out::println); } } 
Enter fullscreen mode Exit fullscreen mode

Mapping : Mapping transforms each element to another form using a function

public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Get list of names List<String> names = people.stream() .map(Person::getName) .collect(Collectors.toList()); names.forEach(System.out::println); } } 
Enter fullscreen mode Exit fullscreen mode

Collecting : Collecting gathers the elements of a stream into a collection or other data structures

public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Collect names into a set Set<String> uniqueCities = people.stream() .map(Person::getCity).collect(Collectors.toSet()); uniqueCities.forEach(System.out::println); } } 
Enter fullscreen mode Exit fullscreen mode

Reducing : Reducing performs a reduction on the elements of the stream using an associative accumulation function and returns an Optional

public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Sum of ages int totalAge = people.stream() .map(Person::getAge).reduce(0, Integer::sum); System.out.println("Total Age: " + totalAge); } } 
Enter fullscreen mode Exit fullscreen mode

FlatMapping : FlatMapping flattens nested structures into a single stream.

public class Main { public static void main(String[] args) { List<List<String>> namesNested = Arrays.asList( Arrays.asList("John", "Doe"), Arrays.asList("Jane", "Smith"), Arrays.asList("Peter", "Parker") ); List<String> namesFlat = namesNested.stream() .flatMap(List::stream).collect(Collectors.toList()); namesFlat.forEach(System.out::println); } } 
Enter fullscreen mode Exit fullscreen mode

Sorting : Sorting allows you to sort the elements of a stream

public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Sort by age List<Person> sortedPeople = people.stream() .sorted(Comparator.comparing(Person::getAge)) .collect(Collectors.toList()); sortedPeople.forEach(System.out::println); } } 
Enter fullscreen mode Exit fullscreen mode

Finding and Matching :
Finding and matching operations check the elements of a stream to see if they match a given predicate

public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Find any person living in New York Optional<Person> personInNY = people.stream() .filter(person -> "NewYork".equals(person.getCity())).findAny(); personInNY.ifPresent(System.out::println); // Check if all people are older than 18 boolean allAdults = people.stream() .allMatch(person -> person.getAge() > 18); System.out.println("All adults: " + allAdults); } } 
Enter fullscreen mode Exit fullscreen mode

Statistics : The Stream API can also be used to perform various statistical operations like counting, averaging, etc.

public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Count number of people long count = people.stream().count(); System.out.println("Number of people: " + count); // Calculate average age Double averageAge = people.stream() .collect(Collectors.averagingInt(Person::getAge)); System.out.println("Average Age: " + averageAge); } } 
Enter fullscreen mode Exit fullscreen mode

Practical Example :
Here's a comprehensive example that uses several of the features mentioned above:

import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("Alice", 30, "New York"), new Person("Bob", 20, "Los Angeles"), new Person("Charlie", 25, "New York"), new Person("David", 40, "Chicago") ); // Filter, map, sort, and collect List<String> names = people.stream() .filter(person -> person.getAge() > 20) .map(Person::getName) .sorted() .collect(Collectors.toList()); names.forEach(System.out::println); // Find the oldest person Optional<Person> oldestPerson = people.stream() .max(Comparator.comparing(Person::getAge)); oldestPerson.ifPresent(person -> System.out.println("Oldest Person: " + person)); // Group by city Map<String, List<Person>> peopleByCity = people.stream() .collect(Collectors.groupingBy(Person::getCity)); peopleByCity.forEach((city, peopleInCity) -> { System.out.println("People in " + city + ": " + peopleInCity); }); // Calculate total and average age IntSummaryStatistics ageStatistics = people.stream() .collect(Collectors.summarizingInt(Person::getAge)); System.out.println("Total Age: " + ageStatistics.getSum()); System.out.println("Average Age: " + ageStatistics.getAverage()); } } 
Enter fullscreen mode Exit fullscreen mode

Summary :
The Java Stream API is a powerful tool for working with collections and data. It allows for:

Filtering: Select elements based on a condition

Mapping: Transform elements

Collecting: Gather elements into collections or other data structures

Reducing: Combine elements into a single result.

FlatMapping: Flatten nested structures.

Sorting: Order elements.

Finding and Matching: Check elements against a condition.

Statistics: Perform statistical operations.

Understanding these features will help you write cleaner, more concise, and more readable code.

Happy Coding...

Top comments (0)