Object Oriented Programming With JAVA
Chapter-12
Stream API in JAVA
Java Stream API
The Stream API in Java is a powerful tool for processing data collections using
a functional and declarative approach. With this API, you can efficiently perform
operations such as filtering, mapping, sorting, and reducing by leveraging Lambdas
and Method References. This feature is introduced in Java 8.it works with Collections,
Arrays, and I/O and it supports Parallel Processing for better performance.
Key Features of Streams
1. Declarative Data Processing: Streams allow you to process data in a declarative
manner, similar to SQL queries, making your code cleaner and more concise.
2. Functional-Style Operations: Take advantage of functional programming with
operations like filter(), map(), and reduce() to manipulate data effectively and
efficiently.
3. Lazy Execution: Optimize performance with lazy execution, where operations are
only executed when necessary, minimizing resource usage and maximizing efficiency.
4. Parallel Processing: Enhance execution speed with parallelStream(), allowing for
parallel processing and faster data handling.
Creating a Stream
You can create a Stream in multiple ways:
1. From a List-
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class MyProg {
public static void main(String[] args) {
// Create a List
List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry",
"Avocado", "Blueberry");
// Create a Stream from the List
Stream<String> fruitStream = fruits.stream();
// Use the stream to filter and print fruits that start with 'A'
fruitStream.filter(fruit ->
fruit.startsWith("A")).forEach(System.out::println);
}
}
Output:
Object Oriented Programming With JAVA
2. From an Array-
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamExample {
public static void main(String[] args) {
String[] fruits = {"Java", "Stream", "Creating"};
Stream<String> fruitStream = Arrays.stream(fruits);
fruitStream.forEach(System.out::println);
}
}
Output:
3. Using Stream.of()-
import java.util.stream.Stream;
public class MyProg {
public static void main(String[] args) {
Stream<String> stream = Stream.of("Java", "Stream", "Creating");
stream.forEach(System.out::println);
}
}
Output:
Stream Operations
There are two types of operations in the Stream API:
1. Intermediate Operations – Transform the stream (e.g., map(), filter(), sorted())
2. Terminal Operations – End the stream and return a result (e.g., collect(), forEach(),
reduce())
1. filter() – The filter() method is an essential tool for any data analyst or programmer
looking to efficiently streamline and refine data. By utilizing this powerful function, you can
easily isolate and extract valuable information from large datasets based on specific criteria.
Example:
import java.util.Arrays;
Object Oriented Programming With JAVA
import java.util.List;
import java.util.stream.Collectors;
public class MyProg {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(5, 12, 7, 20, 3, 18, 25);
// Filter even numbers greater than 10
List<Integer> filtered = numbers.stream()
.filter(n -> n > 10 && n % 2 == 0)
.collect(Collectors.toList());
// Print the filtered list
System.out.println("Filtered numbers: " + filtered);
}
}
Output:
2. map() – The map() function is a method provided by the Java Stream API
that is used to transform the elements of a stream, allowing you to apply a specific
function to each element within that stream.
Example:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MyProg {
public static void main(String[] args) {
List<String> names = Arrays.asList ("Ram", "Anand", "vivek", "Alok");
// Convert all names to uppercase
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
System.out.println(upperCaseNames);
}
}
Output:
3. sorted() – The sorted() method in Java is used to sort the elements of a
stream. It returns a new stream where the elements are sorted in natural order or
according to the provided comparator. This method is part of the java.util.stream
package and is widely used for stream processing.
Example:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
Object Oriented Programming With JAVA
public class MyProg {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList (6, 3, 1, 9, 4, 8);
// Sorting in ascending order
List<Integer> sortedNumbers =
numbers.stream().sorted().collect(Collectors.toList());
System.out.println(sortedNumbers); // Output: [1,3,4,6,8,9]
}
}
Output:
4. reduce() – The reduce() method in Java is a powerful tool used primarily
with streams to perform aggregate operations. This method is part of the Java
Stream API and facilitates processing elements within streams, allowing you to
accumulate them into a single result.
Example:
import java.util.Arrays;
import java.util.List;
public class MyProg {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList (1, 2, 3, 4, 5);
// Sum of natural numbers using reduce
int sum = numbers.stream().reduce(0, Integer::sum);
System.out.println(sum); // Output: 15
}
}
Output:
5. collect() – The collect() method is used to aggregate the elements of a stream into
a single final result. This method leverages collectors, which offer different ways to
accumulate the elements, making it a versatile choice for data processing tasks.
Example:
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class MyProg {
public static void main(String[] args) {
List<String> names = Arrays.asList ("Ram", "Anand", "vivek", "Anand");
// Collect unique names into a Set
Set<String> uniqueNames = names.stream().collect(Collectors.toSet());
System.out.println(uniqueNames); // Output: [Ram,Anand,vivek]
Object Oriented Programming With JAVA
}
}
Output:
6. count() – In Java, the count() method belongs to the Stream API. It returns a long
value indicating the number of elements present in a given stream. The method is a terminal
operation, meaning it processes the stream to completion and returns a single resultant
value.
Example:
import java.util.Arrays;
import java.util.List;
public class MyProg {
public static void main(String[] args) {
List<String> names = Arrays.asList ("Ram", "Anand", "vivek", "Anand");
// Count names with length greater than 3
long count = names.stream() .filter(name -> name.length() > 3) .count();
System.out.println(count); // Output: 3
}
}
Output:
Default Methods-
Default methods in Java allow interfaces to have method implementations without
breaking existing code. Introduced in Java 8, they are primarily used in the Stream API to
enhance functionality without affecting backward compatibility.it allows adding new
methods to interfaces without breaking existing implementations.it helps in functional
programming and stream operations.it improves code reuse by providing common
implementations.
Default Method Purpose
forEach() Iterates through stream elements
spliterator() Splits elements for parallel processing
stream() Converts a collection to a stream
parallelStream() Enables parallel processing
removeIf() Removes elements matching a condition
replaceAll() Updates elements in a list
Example:
import java.util.Arrays;
Object Oriented Programming With JAVA
import java.util.List;
import java.util.ArrayList;
import java.util.Spliterator;
import java.util.stream.Stream;public class MyProg {
public static void main(String[] args) {
List<String> names = Arrays.asList("Ram", "Anand", "vivek", "Amir");
// Using forEach (Default method in Stream API)
names.stream().forEach(System.out::println);
// Using spliterator()
Spliterator<String> spliterator = names.spliterator();
spliterator.forEachRemaining(System.out::println);
//Using stream() and Convert List to Stream and process
List<Integer> numbers = Arrays.asList (10, 20, 30, 40);
Stream<Integer> numberStream = numbers.stream();
numberStream.forEach(System.out::println);
// Using parallel stream for faster execution
numbers.parallelStream().forEach(System.out::println);
List<String> fruits = new ArrayList<>(Arrays.asList("Apple", "Banana",
"Orange"));
// Remove names that contain the letter 'a'
fruits.removeIf(name -> name.contains("a"));
System.out.println(fruits);
// Convert all names to uppercase
fruits.replaceAll(String::toUpperCase);
System.out.println(fruits);
}
}
Output:
Stream API
The Stream API offers a variety of static methods within the Stream class designed to
create and efficiently manage streams. With these methods, developers can seamlessly
generate streams from multiple sources, including collections, arrays, and functions.
Object Oriented Programming With JAVA
Static Method Description
Stream.of() Creates a stream from values or arrays
Stream.iterate() Generates an infinite sequential stream
Stream.generate() Generates an infinite stream using a supplier
Stream.concat() Merges two streams
Stream.empty() Returns an empty stream
Stream.builder() Creates a stream using a builder
Java Streams offer a powerful way to handle data processing tasks in a concise and efficient
manner. Here are the benefits of using static methods in Streams:
1. Convenient Stream Creation-Static methods allow for seamless stream creation,
eliminating the need to convert collections first. This means you can generate streams easily
without hassle.
2. Efficient Stream Manipulation-With static methods, you can handle infinite data
processing with ease. They enable you to manipulate data streams efficiently, helping you
manage large datasets smoothly.
3. Enhanced Performance-Stream operations, when using static methods, are
designed to optimize memory usage. This results in better application performance,
especially when dealing with large volumes of data.
4. Flexible Usage-Static methods in streams are highly compatible with lambda
expressions. This flexibility allows you to write more readable and maintainable code,
enhancing developer productivity.
Example:
import java.util.Random;
import java.util.stream.Stream;
public class MyProg {
public static void main(String[] args) {
// Using Stream.of - a stream from values
Stream<String> names = Stream.of("Ram", "Anand", "vivek", "Amir");
names.forEach(System.out::println);
// Stream from an array
String[] data = {"Ram", "Anand", "vivek", "Amir"};
Stream<String> dataStream = Stream.of(data);
dataStream.forEach(System.out::println);
// Using Stream.iterate() - prints the table of 2
Stream.iterate(2, n -> n + 2).limit(10) .forEach(System.out::println);
// Using Stream.generate()
Stream.generate(() -> new
Random().nextInt(100)).limit(5).forEach(System.out::println);
// Using Stream.concat()
Stream<String> s1 = Stream.of("Apple", "Banana");
Stream<String> s2 = Stream.of("Cherry", "Date");
Stream<String> mergedStream = Stream.concat(s1, s2);
mergedStream.forEach(System.out::println);
// Using Stream.empty()
Stream<String> emptyStream = Stream.empty();
System.out.println("Stream count: " + emptyStream.count()); // Output: 0
Object Oriented Programming With JAVA
// Using Stream.builder()
Stream<String> stream =
Stream.<String>builder().add("One").add("Two").add("Three").build();
stream.forEach(System.out::println);
}
}
Output:
forEach() Method- The forEach() method in the Java Stream API is a powerful tool
for iterating over each element in a stream. This method allows you to perform various
operations such as printing, modifying, or saving data efficiently.
1. Using forEach() with Streams
import java.util.List;
import java.util.Arrays;
public class MyProg {
public static void main(String[] args) {
List<String> names = Arrays.asList ("Ram", "Anand", "vivek", "Amir");
List<Integer> numbers =Arrays.asList (10, 15, 20, 25, 30);
// Using forEach() to print names with stream
names.stream().forEach(System.out::println);
// Print only even numbers with filter ()
Object Oriented Programming With JAVA
numbers.stream().filter(n -> n % 2 ==
0).forEach(System.out::println);
// Convert names to uppercase before printing with map()
names.stream().map(String::toUpperCase).forEach(System.out::println);
}
}
Output:
Base64 Encode & Decode in Java
Base64 encoding is used to encode binary data (like images, files, or sensitive data)
into a readable text format. It is commonly used in data transfer, storing passwords, and
embedding binary data in JSON/XML.Java provides Base64 class in java.util package for
encoding and decoding.
1. Encode and Decode a String to Base64
import java. util. Base64;
public class MyProg {
public static void main(String[] args) {
String text = "Hello, Java Base64!";
// Encoding
String encodedText =
Base64.getEncoder().encodeToString(text.getBytes());
System.out.println("Encoded: " + encodedText);
// Decoding
byte[] decodedBytes = Base64.getDecoder().decode(encodedText);
String decodedText = new String(decodedBytes);
System.out.println("Decoded: " + decodedText);
}
}
Output:
2. Decode Base64 to File
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
public class MyProg {
public static void main(String[] args) throws Exception {
Object Oriented Programming With JAVA
String encodedString = "HQgRmlsZQU2FtcGxlIFRle"; // Example Base64 string
// Decode Base64 and write to file
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
Files.write(Paths.get("decoded.txt"), decodedBytes);
System.out.println("File Decoded Successfully!");
// Convert bytes to String
String decodedText = new String(decodedBytes);
System.out.println(decodedText);
}
}
Output:
3. URL Safe Base64 Encoding-Base64 encoding can contain special characters (+, /, =),
which are not safe for URLs. Use Base64.getUrlEncoder() to get a URL-safe Base64 string.
import java.util.Base64;
public class MyProg {
public static void main(String[] args) {
String text = "https://example.com/?query=base64";
// URL Safe Encoding
String encodedText =
Base64.getUrlEncoder().encodeToString(text.getBytes());
System.out.println("URL Safe Encoded: " + encodedText);
// Decoding
byte[] decodedBytes = Base64.getUrlDecoder().decode(encodedText);
System.out.println("Decoded: " + new String(decodedBytes));
}
}
Output:
4.MIME Encoding -Base64 supports MIME encoding used for large data, which adds line
breaks for large data like email attachments.
import java. util. Base64;
public class MyProg {
public static void main(String[] args) {
String s = "This is a long string that needs Base64 MIME
encoding...";
// MIME Encoding (Breaks into multiple lines)
String em = Base64.getMimeEncoder().encodeToString(s.getBytes());
System.out.println("MIME Encoded:\n" + em);
// Decoding
String decodedText = new String(Base64.getMimeDecoder().decode(em));
Object Oriented Programming With JAVA
System.out.println("\nDecoded: " + decodedText);
}
}
Output:
Try with Resources
The Try-with-Resources feature in Java, introduced in Java 7, is essential for
automating the closure of resources such as files, streams, and sockets after they have been
used. With Java's try-with-resources feature, there's no need to manually call the close()
method. The Java programming language automatically handles resource management for
you, ensuring efficient and error-free code execution.
Syntax:
try (ResourceType resource = new ResourceType()) {
// use resource
} catch (Exception e) {
// handle exception
}
Example:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class MyProg {
public static void main(String[] args) {
String fileName = "file.txt";
try (Stream<String> lines = Files.lines(Paths.get(fileName))) {
lines.forEach(System.out::println);// returns a Stream of lines from a file.
} catch (IOException e) {
e.printStackTrace();//The stream is automatically closed when the
try block ends even if an exception occurs.
}
}
}
Output:
Object Oriented Programming With JAVA