I'm always excited to take on new projects and collaborate with innovative minds.

Email

contact@niteshsynergy.com

Website

https://www.niteshsynergy.com/

Java 8 Features- Stream API

In Java 8, Streams provide a powerful way to process sequences of elements (such as collections or arrays) in a functional style. They allow you to process data in a concise and declarative manner, which improves code readability and maintainability. Streams can be created from collections, arrays, or individual values.

In Java 8, Streams provide a powerful way to process sequences of elements (such as collections or arrays) in a functional style. They allow you to process data in a concise and declarative manner, which improves code readability and maintainability. Streams can be created from collections, arrays, or individual values.

 

1. Creating Streams from Collections

A Collection (e.g., List, Set) is one of the most common sources of data from which you can create a stream.

  • Using stream() method: Collections in Java 8 implement the Stream interface, and you can create a stream using the stream() method.

import java.util.List;
import java.util.Arrays;

public class StreamFromCollection {
   public static void main(String[] args) {
       List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
       
       // Create a stream from a collection
       names.stream()
            .filter(name -> name.startsWith("A"))
            .forEach(System.out::println);  // Output: Alice
   }
}
 

Explanation: Here, we use the stream() method to create a stream from the List. We then use operations like filter() to process the elements in the stream. The forEach() terminal operation prints the result.

 

 

Creating Streams from Arrays

Arrays are another common data source for streams in Java. You can create a stream from an array using the Arrays.stream() method or the stream() method (if working with arrays in general).

  • Using Arrays.stream():

import java.util.Arrays;

public class StreamFromArray {
   public static void main(String[] args) {
       String[] fruits = {"Apple", "Banana", "Mango", "Pineapple"};
       
       // Create a stream from an array
       Arrays.stream(fruits)
             .filter(fruit -> fruit.length() > 5)
             .forEach(System.out::println);  // Output: Banana, Mango, Pineapple
   }
}
Explanation: The Arrays.stream() method creates a stream from the array fruits. The stream operations such as filter() and forEach() are used to process the elements.

 

 

Creating Streams from Individual Values

In addition to collections and arrays, you can create streams from individual values using the Stream.of() method. This method is useful when you want to create a stream from a small number of elements.

  • Using Stream.of():

 

import java.util.stream.Stream;

public class StreamFromValues {
   public static void main(String[] args) {
       // Create a stream from individual values
       Stream<String> stream = Stream.of("John", "Jane", "Mike", "Anna");
       
       // Use operations on the stream
       stream.filter(name -> name.length() > 3)
             .forEach(System.out::println);  // Output: John, Jane, Mike
   }
}
 

Explanation: Here, we use Stream.of() to create a stream from individual values. We apply the filter() operation to select names with more than 3 characters, and forEach() is used to print the filtered results.

 

Creating Streams from Primitive Arrays

For arrays of primitive types (e.g., int[], double[], etc.), Java provides specialized stream types: IntStream, LongStream, and DoubleStream. These allow efficient handling of primitive data types without the overhead of autoboxing.

  • Using Arrays.stream() for primitive arrays:

    Example:

import java.util.Arrays;

public class StreamFromPrimitiveArray {
   public static void main(String[] args) {
       int[] numbers = {1, 2, 3, 4, 5};
       
       // Create an IntStream from an int array
       Arrays.stream(numbers)
             .filter(num -> num % 2 == 0)
             .forEach(System.out::println);  // Output: 2, 4
   }
}
Explanation: We create an IntStream from the int[] array using Arrays.stream(). The filter() method is applied to select even numbers, and forEach() prints the results.

 

 

Summary of Ways to Create Streams:

SourceMethod to Create StreamExample
Collectionstream() methodList<String> list = ...; list.stream()
ArrayArrays.stream() methodint[] arr = ...; Arrays.stream(arr)
Individual ValuesStream.of() methodStream<String> stream = Stream.of("a", "b", "c")
Primitive ArraysArrays.stream() for primitive types (int[], double[])int[] numbers = {1, 2, 3}; Arrays.stream(numbers)

Why Use Streams?

  1. Declarative and Functional Style: Streams allow you to process data in a more functional way, making the code more readable and concise. Instead of writing explicit loops and conditional statements, you can use operations like filter(), map(), reduce(), and more.
  2. Parallel Processing: Streams can easily be processed in parallel, improving performance on multi-core systems.
  3. Chainable Operations: Streams support method chaining, enabling you to apply multiple operations in a fluent, readable manner.

 

Real-World Use Case (Gaming):

In a gaming project, suppose you have a list of players, and you want to filter out all players whose scores are below a certain threshold and then sort the remaining players by their scores.

 

 

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

class Player {
   String name;
   int score;

   Player(String name, int score) {
       this.name = name;
       this.score = score;
   }

   public String toString() {
       return name + ": " + score;
   }
}

public class StreamExample {
   public static void main(String[] args) {
       List<Player> players = Arrays.asList(
           new Player("Alice", 1500),
           new Player("Bob", 800),
           new Player("Charlie", 1200),
           new Player("David", 2000)
       );

       // Use streams to filter and sort
       List<Player> filteredPlayers = players.stream()
               .filter(player -> player.score > 1000)
               .sorted((p1, p2) -> Integer.compare(p2.score, p1.score))  // Sort by score descending
               .collect(Collectors.toList());

       filteredPlayers.forEach(System.out::println);  // Output: David: 2000, Alice: 1500, Charlie: 1200
   }
}
 

 

In Java 8, the Stream API provides a set of operations to process sequences of elements in a functional style. The Stream API can be categorized into two main types based on how data flows and is processed: Streams and Primitive Streams.

Here’s a breakdown of the types of Stream API in Java:

 

1. Stream (Reference Type Stream)

  • Stream<T>: The general stream type for working with objects (non-primitive types).

    • Purpose: Used to process data that is wrapped in reference types (e.g., String, Integer, Custom objects).
    • Operations: Can perform operations like filtering, mapping, sorting, reducing, etc., on objects.

    Example:

import java.util.List;
import java.util.Arrays;

public class ReferenceStreamExample {
   public static void main(String[] args) {
       List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

       names.stream()
            .filter(name -> name.length() > 3)
            .forEach(System.out::println);  // Output: Alice, Charlie, David
   }
}
 

 

Features:

  • Supports operations like map(), filter(), forEach(), reduce().
  • Allows creating a pipeline of multiple operations on collections or other data sources.
  • Does not support primitive operations directly.

 

Primitive Streams

  • IntStream, LongStream, and DoubleStream: These are specialized streams for handling primitive types (int, long, and double), providing better performance by avoiding autoboxing (the conversion between primitives and objects).

    • IntStream: Used for int values.
    • LongStream: Used for long values.
    • DoubleStream: Used for double values.

    Why Primitive Streams?

    • Efficiency: They avoid the overhead of boxing and unboxing primitive values into wrapper classes like Integer, Long, and Double.
    • Specialized Operations: These streams support operations like sum(), average(), and min(), which are optimized for primitive data types.

    Example:

import java.util.stream.IntStream;

public class PrimitiveStreamExample {
   public static void main(String[] args) {
       IntStream.range(1, 6)
                .filter(n -> n % 2 == 0)
                .forEach(System.out::println);  // Output: 2, 4
   }
}
 

 

Features:

  • IntStream: Provides methods like sum(), average(), max(), and min().
  • LongStream and DoubleStream: Similar methods as IntStream but for long and double data types, respectively.
  • They offer mapToObj(), flatMapToInt(), etc., to transform between primitive and object streams.

 

 

Stream Pipelines and Operations

Regardless of whether you're using reference streams or primitive streams, the stream operations can be categorized into:

  • Intermediate Operations: These operations transform a stream into another stream (e.g., map(), filter(), distinct()). They are lazy, meaning they are not executed until a terminal operation is invoked.
  • Terminal Operations: These operations produce a result or a side effect (e.g., collect(), forEach(), reduce()). A terminal operation marks the end of the stream pipeline.
  • Short-circuiting Operations: These operations allow the processing to stop early based on a condition, such as anyMatch(), allMatch(), findFirst(), etc.

 

Key Differences Between Stream Types:

Stream TypeFor Primitive TypesFor Objects/Reference Types
StreamNoYes
IntStreamYesNo
LongStreamYesNo
DoubleStreamYesNo

 

 

                                                                Types Of Stream API In Java 8

 

image-180.png

Intermediate operations are operations that transform a stream into another stream. These operations are lazy, meaning they do not perform any processing until a terminal operation is invoked. When you perform intermediate operations, you create a stream pipeline that is executed when a terminal operation is triggered. Since they are lazy, intermediate operations can be chained together and only evaluated when necessary, which can help improve performance by avoiding unnecessary computations.

Here are the key Intermediate Operations available in Java 8 Streams:

 

package com.niteshsynergy.java8.stream;

import com.niteshsynergy.Emp;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class Demo01IntermediateOperations {
public static void main(String[] args) {
//filter() – Filters elements based on a condition.

List<String> list1 = Arrays.asList("apple", "banana", "carrot","Mango","Anans","Appu","Awla");
list1.stream()
.filter(s->s.startsWith("a"))
.forEach(System.out::println);
//map() – Transforms each element of the stream.
list1.stream()
.map(s->s.replaceAll("a","b"))
.forEach(System.out::println);

//distinct() – Removes duplicates.
List<Integer> list2 = Arrays.asList(1,2,3,4,5,6,7,8,9);
list2.stream()
.distinct()
.forEach(System.out::println);
//sorted() – Sorts the stream.
List<Integer> list3 = Arrays.asList(1,2,3,4,5,6,7,8,9);
list3.stream()
.sorted()
.forEach(System.out::println);

//limit() – Limits the number of elements in the stream.
List<Integer> list4 = Arrays.asList(1,2,3,4,5,6,7,8,9);
list4.stream()
.limit(3)
.forEach(System.out::println);

//skip() – Skips the first n elements.
List<Integer> list5 = Arrays.asList(1,2,3,4,5,6,7,8,9);
list5.stream()
.skip(3)
.forEach(System.out::println);

//Below is an example using an Emp class to demonstrate various intermediate operations in the Stream API.

// List of employees
List<Emp> employees = Arrays.asList(
new Emp(1, "Alice", 75000),
new Emp(2, "Bob", 55000),
new Emp(3, "Charlie", 70000),
new Emp(4, "David", 85000),
new Emp(5, "Eva", 65000),
new Emp(6, "Frank", 70000)
);

// 1. filter - Get employees with salary greater than 60000
System.out.println("Filtered Employees (Salary > 60000):");

employees.stream()
.filter(e->e.getId()>60000)
.forEach(System.out::println);
// 2. map - Get employee names in uppercase
System.out.println("\nEmployee Names in Uppercase:");

employees.stream()
.map(e-> e.getName().toUpperCase())
.forEach(System.out::println);
// 3. sorted - Sort employees by salary
System.out.println("\nEmployees Sorted by Salary:");

employees.stream()
.sorted(Comparator.comparingDouble(Emp::getSalary))
.forEach(System.out::println);
// 4. distinct - Get distinct salary values
System.out.println("\nDistinct Salaries:");

employees.stream()
.map(emp->emp.getSalary())
.distinct()
.forEach(System.out::println);

// 5. flatMap - Flatten a list of employees with additional employee objects for illustration
System.out.println("\nAll Employees (After flatMap):");

List<Emp> additionalEmployees = Arrays.asList(
new Emp(7, "George", 70000),
new Emp(8, "Helen", 72000)
);
List<Emp> allEmployees = Stream.concat(employees.stream(),
additionalEmployees.stream())
.collect(Collectors.toList());

// 6. limit - Get first 3 employees by salary
System.out.println("\nTop 3 Employees by Salary:");

employees.stream()
.sorted(Comparator.comparingDouble(Emp::getSalary).reversed())
.limit(3)
.forEach(System.out::println);

// 7. skip - Skip the first 2 employees and get the rest
System.out.println("\nEmployees after skipping the first 2:");

employees.stream()
.skip(2)
.forEach(System.out::println);

//To create a Map where the key is the empId and the value is the same Emp object, we can utilize a HashMap or TreeMap in Java
// Create a few Emp objects
System.out.println("\nTo create a Map where the key is the empId and the value is the same Emp object, we can utilize a HashMap or TreeMap in Java");
Emp emp1 = new Emp(1, "Alice", 75000);
Emp emp2 = new Emp(2, "Bob", 55000);
Emp emp3 = new Emp(3, "Charlie", 70000);
Emp emp4 = new Emp(4, "David", 85000);
// Creating a Map with empId as key and Emp object as value
Map<Integer, Emp> empMap = new HashMap<>();
empMap.put(emp1.getId(), emp1);
empMap.put(emp2.getId(), emp2);
empMap.put(emp3.getId(), emp3);
empMap.put(emp4.getId(), emp4);

// Display all entries in the Map
System.out.println("Emp Map:");
empMap.forEach((key, value) -> System.out.println("empId: " + key + " => " + value));

// Example: Retrieve an Emp by empId (key)
Emp retrievedEmp = empMap.get(3);
System.out.println("\nRetrieved Employee with empId 3: " + retrievedEmp);

// Example: Check if an Emp exists in the map
boolean containsEmp = empMap.containsKey(2);
System.out.println("\nDoes the map contain empId 2? " + containsEmp);

// Example: Remove an Emp by empId
empMap.remove(4);
System.out.println("\nMap after removing empId 4:");
empMap.forEach((key, value) -> System.out.println("empId: " + key + " => " + value));

// Create a few Emp objects
Emp emp11 = new Emp(1, "Alice", 75000);
Emp emp21 = new Emp(2, "Bob", 55000);
Emp emp31 = new Emp(3, "Charlie", 70000);
Emp emp41 = new Emp(4, "David", 85000);
// Create a list of Emp objects
List<Emp> empList1 = Arrays.asList(emp11, emp21, emp31, emp41);

// Use filter, map, and Collectors.toMap
Map<Integer, Emp> empMap1 = empList1.stream()
.filter(emp111 -> emp1.getSalary() > 60000) // Filter employees with salary > 60000
.map(emp111 -> new Emp(emp1.getId(), emp1.getName(), emp1.getSalary())) // Map operation (can modify objects if needed)
.collect(Collectors.toMap(Emp::getId, emp111 -> emp1)); // Collect into a Map with empId as the key

// Display the resulting map
System.out.println("Filtered and Mapped Emp Map:");
empMap1.forEach((key1, value1) -> System.out.println("empId: " + key1 + " => " + value1));

// Example: Retrieve an Emp by empId (key)
Emp retrievedEmp1 = empMap1.get(3);
System.out.println("\nRetrieved Employee with empId 3: " + retrievedEmp1);


// Print different way
empList1.stream()
.filter(pp-> pp.getSalary()>40000)
.map(ee-> new Emp(ee.getId(), ee.getName(), ee.getSalary()))
.collect(Collectors.toMap(Emp::getId, ee1 -> ee1))
.forEach((key, value) -> System.out.println("empId: " + key + " => " + value));

}
}


 
image-181.png

Terminal Operations are the operations that trigger the processing of the stream and produce a result, a side-effect, or a final computation. Once a terminal operation is invoked, the stream is consumed and can no longer be used. These operations mark the end of the stream pipeline and cause the intermediate operations to be evaluated.

Here’s a breakdown of some key Terminal Operations in Java 8 Streams:

package com.niteshsynergy.java8.stream;

import com.niteshsynergy.Emp;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.RandomAccess;
import java.util.function.Function;
import java.util.stream.Collectors;

public class Demo03TerminalOperations {

public static void main(String[] args) {
//collect() – Collects elements into a collection like a List, Set, or Map.
List<String> list = Arrays.asList("apple", "banana", "cherry");
List<String> result = list.stream()
.collect(Collectors.toList());
System.out.println(result); // Output: [apple, banana, cherry]
list.stream()
.collect(Collectors.toMap(Function.identity(), l->list))
.forEach((key, value) -> System.out.println("Identity: " + key + " => " + value));

//forEach() – Performs an action for each element in the stream.

list.stream()
.forEach(System.out::println);

//reduce() – Reduces the stream to a single value (e.g., sum, product)
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(" integers.stream()\n" +
" .reduce(0, Integer::sum);"+ integers.stream()
.reduce(0, Integer::sum));
System.out.println(" integers.stream()\n" +
" .reduce(10, Integer::sum)"+ integers.stream()
.reduce(10, Integer::sum));
System.out.println(" integers.stream()\n" +
" .reduce(10, Integer::max)"+ integers.stream()
.reduce(10, Integer::max));

System.out.println("Count of integers: " + integers.stream().count());
// real time example
// Create a few Emp objects
Emp emp11 = new Emp(1, "Alice", 75000);
Emp emp21 = new Emp(2, "Bob", 55000);
Emp emp31 = new Emp(3, "Charlie", 70000);
Emp emp41 = new Emp(4, "David", 85000);

List<Emp> empList1 = Arrays.asList(emp11, emp21, emp31, emp41);
empList1.stream()
.filter(emp -> emp.getSalary() > 70000);


//anyMatch() / allMatch() / noneMatch() – Tests if any, all, or none of the elements match a given predicate.
List<Integer> listInt = Arrays.asList(1, 2, 3, 4, 5);

boolean anyMatch = listInt.stream().anyMatch(x -> x>4);
boolean allMatch = listInt.stream().allMatch(x -> x>0);
boolean noneMatch = listInt.stream().noneMatch(x -> x<0);
System.out.println(anyMatch); // Output: true
System.out.println(allMatch); // Output: true
System.out.println(noneMatch); // Output: true

}
}
 
image-182.png

 

Short-circuiting operations in Java 8 Streams are special types of terminal operations that terminate the stream processing early. These operations evaluate the stream lazily, which means that they stop processing elements as soon as the condition is met. This can result in performance optimization, especially when working with large datasets, as it avoids unnecessary processing.

Short-circuiting operations are used when you don’t need to process the entire stream, but only a part of it. They "short-circuit" the evaluation once a result is determined.

package com.niteshsynergy.java8.stream;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class Demo04ShortCircuitingOperations {
//anyMatch() – Returns true if any element in the stream matches the given predicate (stops processing as soon as the match is found).
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
boolean anyMatch = list.stream().anyMatch(x -> x > 4);
System.out.println(anyMatch); // Output: true
boolean allMatch = list.stream().allMatch(x -> x > 0);
System.out.println(allMatch); // Output: tr
boolean noneMatch = list.stream().noneMatch(x -> x < 0);
System.out.println(noneMatch); // Output: true
Optional<Integer> first = list.stream().findFirst();
first.ifPresent(System.out::println); // Output: 1
Optional<Integer> any = list.stream().findAny();
any.ifPresent(System.out::println); // Output: 1 (or any element)

}
}
 

Gaming Example: Player Ranking and Filtering

import java.util.*;
import java.util.stream.*;

public class GamingExample {
   public static void main(String[] args) {
       // Creating a list of players with scores and rankings
       List<Player> players = Arrays.asList(
           new Player("Alice", 1000, "Bronze", Arrays.asList("Quest1", "Quest2")),
           new Player("Bob", 1500, "Gold", Arrays.asList("Quest2", "Quest3")),
           new Player("Charlie", 2500, "Platinum", Arrays.asList("Quest1", "Quest4")),
           new Player("David", 800, "Silver", Arrays.asList("Quest3", "Quest5")),
           new Player("Eve", 1300, "Gold", Arrays.asList("Quest4", "Quest6"))
       );

       // Stream pipeline to process players:
       List<String> highRankingPlayers = players.stream()
           // 1. Filter out players who have less than 1000 points
           .filter(player -> player.getScore() >= 1000)
           
           // 2. Map the filtered players to their names
           .map(Player::getName)
           
           // 3. Apply a transformation to convert all player names to uppercase
           .map(String::toUpperCase)
           
           // 4. Filter players whose names start with 'A'
           .filter(name -> name.startsWith("A"))
           
           // 5. Convert the list of names to a stream of quests each player is associated with
           .flatMap(name -> players.stream()
                                   .filter(player -> player.getName().equals(name))
                                   .flatMap(player -> player.getQuests().stream()))
           
           // 6. Remove duplicate quests
           .distinct()
           
           // 7. Sort quests alphabetically
           .sorted()
           
           // 8. Limit the output to the first 3 quests
           .limit(3)
           
           // 9. Collect the result into a List
           .collect(Collectors.toList());

       // Displaying the results
       System.out.println("High-ranking players' quests: " + highRankingPlayers);
   }
}

// Player class with name, score, rank, and quests
class Player {
   private String name;
   private int score;
   private String rank;
   private List<String> quests;

   public Player(String name, int score, String rank, List<String> quests) {
       this.name = name;
       this.score = score;
       this.rank = rank;
       this.quests = quests;
   }

   public String getName() {
       return name;
   }

   public int getScore() {
       return score;
   }

   public String getRank() {
       return rank;
   }

   public List<String> getQuests() {
       return quests;
   }
}
 

Explanation of the Stream Pipeline:

  1. filter(player -> player.getScore() >= 1000): Filters out players with scores less than 1000, ensuring we only process high-scoring players.
  2. map(Player::getName): Maps the players to their names, extracting the relevant data from each Player object.
  3. map(String::toUpperCase): Converts the player names to uppercase for uniform formatting.
  4. filter(name -> name.startsWith("A")): Filters the list of player names to only include those starting with the letter "A".
  5. flatMap(name -> players.stream().filter(player -> player.getName().equals(name)).flatMap(player -> player.getQuests().stream())): For each player, we extract their quests, and use flatMap to flatten the nested lists into a single stream of quest names.
  6. distinct(): Removes any duplicate quest names from the resulting stream.
  7. sorted(): Sorts the quest names in alphabetical order.
  8. limit(3): Limits the result to the first 3 quests, demonstrating a cap on the stream's size.
  9. collect(Collectors.toList()): Collects the result into a list, which is the final output.

Output:

High-ranking players' quests: [Quest1, Quest2, Quest3]
 

 

Java 8 Stream Operations with 20 Method Chain

import java.util.*;
import java.util.stream.*;

public class StreamMethodChainExample {
   public static void main(String[] args) {
       // Create a list of players with names, scores, ranks, and quests
       List<Player> players = Arrays.asList(
           new Player("Alice", 1000, "Bronze", Arrays.asList("Quest1", "Quest2")),
           new Player("Bob", 1500, "Gold", Arrays.asList("Quest2", "Quest3")),
           new Player("Charlie", 2500, "Platinum", Arrays.asList("Quest1", "Quest4")),
           new Player("David", 800, "Silver", Arrays.asList("Quest3", "Quest5")),
           new Player("Eve", 1300, "Gold", Arrays.asList("Quest4", "Quest6"))
       );

       // Chain of 20 stream methods
       List<String> result = players.stream()
           // 1. Filter players with a score greater than or equal to 1000
           .filter(player -> player.getScore() >= 1000)
           // 2. Map to player's name
           .map(Player::getName)
           // 3. Convert names to uppercase
           .map(String::toUpperCase)
           // 4. Filter names that start with 'A'
           .filter(name -> name.startsWith("A"))
           // 5. Map to player's quests by finding players by name
           .flatMap(name -> players.stream()
                                   .filter(player -> player.getName().equals(name))
                                   .flatMap(player -> player.getQuests().stream()))
           // 6. Remove duplicates from the quest names
           .distinct()
           // 7. Sort quest names alphabetically
           .sorted()
           // 8. Limit to the first 3 quest names
           .limit(3)
           // 9. Map quests to their length
           .map(String::length)
           // 10. Filter quests that are longer than 4 characters
           .filter(len -> len > 4)
           // 11. Reduce to the total length of quest names
           .reduce(0, Integer::sum)
           // 12. Add a constant value (total length of all quests) to it
           .map(totalLength -> totalLength + 10)
           // 13. Convert the total length to a string with a message
           .map(length -> "Total quest name length with 10 added: " + length)
           // 14. Filter if the string length is greater than 50
           .filter(msg -> msg.length() > 50)
           // 15. If not empty, concatenate " - Completed" at the end
           .map(msg -> msg + " - Completed")
           // 16. Sort the final string list alphabetically
           .sorted()
           // 17. Limit the results to top 1 string
           .limit(1)
           // 18. Convert to a list (collect)
           .collect(Collectors.toList())
           // 19. Print the result
           .forEach(System.out::println);
   }
}

// Player class with name, score, rank, and quests
class Player {
   private String name;
   private int score;
   private String rank;
   private List<String> quests;

   public Player(String name, int score, String rank, List<String> quests) {
       this.name = name;
       this.score = score;
       this.rank = rank;
       this.quests = quests;
   }

   public String getName() {
       return name;
   }

   public int getScore() {
       return score;
   }

   public String getRank() {
       return rank;
   }

   public List<String> getQuests() {
       return quests;
   }
}

Explanation of the Stream Method Chain:

  1. filter(player -> player.getScore() >= 1000): Filters out players with a score less than 1000.
  2. map(Player::getName): Extracts the names of the filtered players.
  3. map(String::toUpperCase): Converts all player names to uppercase.
  4. filter(name -> name.startsWith("A")): Filters names that start with "A".
  5. flatMap(): Extracts quests of players whose names match the filtered ones.
  6. distinct(): Removes any duplicate quest names.
  7. sorted(): Sorts the quest names alphabetically.
  8. limit(3): Limits the result to the first 3 quests.
  9. map(String::length): Converts each quest name to its length.
  10. filter(len -> len > 4): Filters quest names with length greater than 4.
  11. reduce(0, Integer::sum): Reduces the length of quest names to a total sum.
  12. map(totalLength -> totalLength + 10): Adds 10 to the total length.
  13. map(length -> "Total quest name length with 10 added: " + length): Converts the total length into a string with a message.
  14. filter(msg -> msg.length() > 50): Filters if the string message length is greater than 50.
  15. map(msg -> msg + " - Completed"): Appends "- Completed" to the message.
  16. sorted(): Sorts the final string messages alphabetically.
  17. limit(1): Limits the final result to only the top 1 string.
  18. collect(Collectors.toList()): Collects the result into a list.
  19. forEach(System.out::println): Prints the final result.

Output:
Total quest name length with 10 added: 17 - Completed


You can ask any query just drop mail to us on say@niteshsynergy.com

 

 

25 min read
Nov 19, 2024
By Nitesh Synergy
Share