Java Streams API

জাভা ফাংশনাল প্রোগ্রামিং (Java Functional Programming) - Java Technologies

443

Java Streams API হল Java 8-এর একটি গুরুত্বপূর্ণ বৈশিষ্ট্য যা functional programming ধারণা ব্যবহার করে ডেটা সংগ্রহ (collections) ও প্রক্রিয়াকরণ সহজ, কার্যকর এবং declarative পদ্ধতিতে করতে সহায়তা করে। Streams API ব্যবহার করে আপনি Collections (যেমন List, Set, Map ইত্যাদি) অথবা অন্যান্য ডেটা সোর্সের উপরে কার্যকরী এবং প্যারালাল অপারেশনগুলো করতে পারেন। এটি lambda expressionsfunctional interfaces ব্যবহার করে কোডের জটিলতা কমায় এবং পারফরম্যান্স উন্নত করে।

1. Streams API এর ধারণা

Stream হল একটি sequence of elements যা functional-style অপারেশন দ্বারা প্রক্রিয়া করা যায়। Streams API Collection অথবা array থেকে ডেটা প্রক্রিয়া করতে পারে এবং এগুলিতে filtering, mapping, reducing ইত্যাদি কার্যকলাপ করতে সহায়তা করে। একে aggregate operations বলা হয়।

Stream-এর কয়েকটি বৈশিষ্ট্য:

  • No Storage: Stream ডেটা সংরক্ষণ করে না, এটি সরাসরি ডেটা প্রক্রিয়া করে।
  • Functional in Nature: Stream API কেবল functions হিসেবে ডেটা পরিচালনা করে।
  • Laziness-seeking: Stream অপারেশনগুলো lazy হিসেবে কাজ করে, অর্থাৎ তারা ডেটার উপরে কাজ শুরু করে না যতক্ষণ না শেষ অপারেশন প্রয়োগ না হয়।
  • Possibly Unbounded: Stream-এর আকার সীমাহীন হতে পারে, যেমন ইনপুট ডেটার আকার বড় হতে পারে।
  • Pipelining: Stream অপারেশনগুলো পিপলাইনে চেইন করা যায়, যা কোডে আরও পরিষ্কারতা আনে।

2. Stream Operations Types

Stream-এর মধ্যে দুটি ধরনের অপারেশন রয়েছে:

  1. Intermediate Operations: এই অপারেশনগুলো একটি নতুন স্ট্রিম তৈরি করে এবং lazy evaluation অনুসরণ করে।
  2. Terminal Operations: এই অপারেশনগুলো স্ট্রিমে কার্যকরভাবে কিছু শেষ করে এবং non-lazy evaluation করে। উদাহরণস্বরূপ, forEach(), collect(), reduce()

3. Java Streams API ব্যবহার করার উদাহরণ

a) Stream Creation (from Collection)

স্ট্রিম তৈরির জন্য stream() মেথড ব্যবহার করা হয়। এর মাধ্যমে আপনি একটি কন্টেইনার (যেমন List) থেকে স্ট্রিম তৈরি করতে পারেন।

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

public class StreamExample {
    public static void main(String[] args) {
        // List of integers
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // Creating a stream from the list
        Stream<Integer> numberStream = numbers.stream();

        // Printing the stream elements
        numberStream.forEach(System.out::println);
    }
}

ব্যাখ্যা:

  • numbers.stream() থেকে একটি স্ট্রিম তৈরি করা হয়েছে এবং forEach() ব্যবহার করে স্ট্রিমের প্রতিটি উপাদান প্রিন্ট করা হয়েছে।

b) Intermediate Operations (Filter, Map, etc.)

Stream API-তে intermediate operations যেমন filter(), map(), distinct() ইত্যাদি ব্যবহার করে আপনি স্ট্রিমের উপাদানগুলির উপর কাজ করতে পারেন।

filter() Operation Example:
import java.util.*;
import java.util.stream.*;

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

        // Filtering even numbers
        List<Integer> evenNumbers = numbers.stream()
                                            .filter(n -> n % 2 == 0)
                                            .collect(Collectors.toList());

        System.out.println("Even Numbers: " + evenNumbers);
    }
}

ব্যাখ্যা:

  • filter() অপারেশনটি স্ট্রিমের উপাদানগুলির মধ্যে এমন উপাদান নির্বাচন করে যেগুলির শর্ত পূরণ করে। এখানে even numbers ফিল্টার করা হয়েছে।
  • collect(Collectors.toList()) স্ট্রিমের ফলাফলকে একটি List-এ সংগ্রহ করেছে।
map() Operation Example:
import java.util.*;
import java.util.stream.*;

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

        // Converting all words to uppercase using map
        List<String> uppercaseWords = words.stream()
                                           .map(String::toUpperCase)
                                           .collect(Collectors.toList());

        System.out.println("Uppercase Words: " + uppercaseWords);
    }
}

ব্যাখ্যা:

  • map() অপারেশনটি স্ট্রিমের প্রতিটি উপাদানকে নির্দিষ্ট ফাংশন প্রয়োগ করে পরিবর্তন করে। এখানে, সব শব্দগুলিকে uppercase করা হয়েছে।

c) Terminal Operations (forEach, collect, reduce)

Terminal operations স্ট্রিমের শেষ প্রক্রিয়া, যেমন forEach(), collect(), reduce() ইত্যাদি।

forEach() Operation Example:
import java.util.*;
import java.util.stream.*;

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

        // Using forEach to print all elements
        numbers.stream()
               .forEach(n -> System.out.println(n));
    }
}

ব্যাখ্যা:

  • forEach() স্ট্রিমের প্রতিটি উপাদান দিয়ে কার্যক্রম (যেমন প্রিন্ট) সম্পন্ন করতে ব্যবহৃত হয়।
collect() Operation Example:
import java.util.*;
import java.util.stream.*;

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

        // Collecting the even numbers into a new list
        List<Integer> evenNumbers = numbers.stream()
                                           .filter(n -> n % 2 == 0)
                                           .collect(Collectors.toList());

        System.out.println("Even Numbers: " + evenNumbers);
    }
}

ব্যাখ্যা:

  • collect() অপারেশনটি স্ট্রিমের ফলাফলকে একটি নির্দিষ্ট আউটপুট ফরম্যাটে (যেমন List) সংগ্রহ করতে ব্যবহৃত হয়।
reduce() Operation Example:
import java.util.*;
import java.util.stream.*;

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

        // Using reduce to sum the numbers
        int sum = numbers.stream()
                          .reduce(0, (a, b) -> a + b);

        System.out.println("Sum of Numbers: " + sum);
    }
}

ব্যাখ্যা:

  • reduce() অপারেশনটি স্ট্রিমের উপাদানগুলিকে একত্রিত করে একটি ফলাফল তৈরি করে। এখানে সব সংখ্যা যোগ করা হয়েছে।

4. Stream API এর সুবিধা

  • Declarative: Streams ব্যবহার করে আপনি আরও declarative কোড লিখতে পারেন, অর্থাৎ আপনি কী করতে চান তা লিখেন, কিভাবে তা হবে তা নয়।
  • Parallelism: Streams API-তে সহজেই প্যারালাল প্রক্রিয়াকরণ করা সম্ভব। উদাহরণস্বরূপ, আপনি parallelStream() ব্যবহার করে একই অপারেশনগুলো একাধিক থ্রেডে চালাতে পারেন।
  • Cleaner Code: Lambda expressions ও functional programming এর মাধ্যমে কোড কম্প্যাক্ট এবং পরিষ্কার হয়।
  • Efficient Data Processing: lazy evaluation এবং short-circuiting এর মাধ্যমে Stream API আপনার কোডের কার্যকারিতা বৃদ্ধি করতে পারে।

5. Stream Performance

Java Streams API কেবল সহজ এবং পরিষ্কার কোড তৈরি করতে সহায়তা করে না, এটি parallel streams এর মাধ্যমে পারফরম্যান্সও উন্নত করতে পারে। আপনি যদি বড় ডেটাসেটের উপর কাজ করছেন, তাহলে parallelStream() ব্যবহার করে স্ট্রিম অপারেশনগুলোকে একাধিক থ্রেডে প্রসেস করতে পারেন:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();
System.out.println("Sum using parallel stream: " + sum);

Java Streams API Java 8-এ একটি শক্তিশালী বৈশিষ্ট্য যা ফাংশনাল প্রোগ্রামিং স্টাইল ব্যবহার করে ডেটা প্রক্রিয়া সহজ, কার্যকরী এবং পরিষ্কার করে তোলে। filter(), map(), reduce(), এবং collect() সহ অন্যান্য স্ট্রিম অপারেশন ব্যবহার করে আপনি জটিল ডেটা প্রক্রিয়া এবং সংগ্রহের কাজগুলো সহজে করতে পারবেন। Stream API কোডের পরিস্কারতা বাড়ায় এবং ফাংশনাল পদ্ধতিতে ডেটা প্রক্রিয়াকরণকে আরও বেশি কার্যকর করে।

Content added By

Java Streams Java 8 এ Functional Programming ধারণাকে সমর্থন করার জন্য চালু করা হয়েছিল এবং এটি Java প্রোগ্রামিং ভাষায় ডেটা প্রসেসিংয়ের জন্য একটি অত্যন্ত শক্তিশালী বৈশিষ্ট্য। Streams এক ধরনের Abstracted sequence of elements যা আপনাকে ডেটা সংগ্রহের উপরে কাজ করার জন্য একটি declarative পদ্ধতি প্রদান করে। এটি আপনাকে ডেটাকে প্রক্রিয়াকরণের জন্য অনেক সহজ এবং পরিষ্কার উপায় দেয়, বিশেষত যখন আপনি একটি বড় ডেটাসেটের উপর অপারেশন করতে চান।

Streams কি?

Java Streams হল sequence of elements যা functional-style অপারেশন করতে সক্ষম। Stream সরাসরি ডেটা স্টোরেজ (যেমন, Collection, Array, I/O channel) থেকে ডেটা নিয়ে প্রক্রিয়া শুরু করে এবং একাধিক intermediate এবং terminal অপারেশন দিয়ে ডেটাকে প্রসেস করে।

Streams এর মাধ্যমে, আপনি map, filter, reduce এবং অন্যান্য ফাংশনাল অপারেশনগুলো ডেটা সংগ্রহের উপরে কার্যকরভাবে প্রয়োগ করতে পারেন, এবং ডেটা সৃষ্টির পরিবর্তে শুধুমাত্র তার উপর অপারেশন করতে পারেন।

Stream এর বৈশিষ্ট্য:

  1. No Storage: Stream ডেটা স্টোরেজ নয়, বরং এটি ডেটা উৎস থেকে ডেটা প্রক্রিয়া করার জন্য ব্যবহৃত হয়।
  2. Functional in Nature: Stream অপারেশনগুলো ফাংশনাল, যেমন map(), filter(), reduce(), ইত্যাদি।
  3. Lazy Evaluation: Stream অপারেশনগুলো সাধারণত lazy হয়, অর্থাৎ, তারা তখনই কার্যকর হয় যখন একটি terminal operation (যেমন collect(), forEach(), reduce()) চালানো হয়।
  4. Can be consumed only once: Stream একবার ব্যবহৃত হলে পুনরায় ব্যবহার করা যায় না।

Stream-এর প্রধান অপারেশন:

  1. Intermediate Operations:
    • এগুলি lazy অপারেশন, অর্থাৎ, এগুলি ততক্ষণ কাজ করে না যতক্ষণ না একটি terminal operation trigger না হয়। এগুলোর মধ্যে filter, map, sorted, distinct, limit ইত্যাদি অন্তর্ভুক্ত।
    • Example: filter(), map(), distinct()
  2. Terminal Operations:
    • এগুলি ডেটা প্রসেস শেষ করে এবং শেষ ফলাফল রিটার্ন করে (যেমন Collection এ রূপান্তর, আউটপুট দেওয়া, অথবা একটি একক মান রিটার্ন করা)। এগুলোর মধ্যে forEach, collect, reduce ইত্যাদি অন্তর্ভুক্ত।
    • Example: collect(), forEach(), reduce()

Stream-এর প্রধান অপারেশন গুলো:

1. Creating a Stream

Streams তৈরি করার জন্য Java বিভিন্ন পদ্ধতি প্রদান করে। যেমন, Collection থেকে Stream তৈরি করা, অ্যারে থেকে Stream তৈরি করা, বা Stream.of() পদ্ধতি দিয়ে একক মানের স্ট্রিম তৈরি করা।

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

public class Main {
    public static void main(String[] args) {
        // Collection থেকে Stream তৈরি
        List<String> list = Arrays.asList("Apple", "Banana", "Orange", "Mango");
        Stream<String> stream = list.stream();
        
        // Array থেকে Stream তৈরি
        int[] numbers = {1, 2, 3, 4, 5};
        IntStream intStream = Arrays.stream(numbers);
        
        // Stream.of() ব্যবহার
        Stream<String> fruitStream = Stream.of("Apple", "Banana", "Orange");
    }
}

2. Intermediate Operations:

Intermediate operations ডেটা ফিল্টার বা ট্রান্সফর্ম করতে ব্যবহৃত হয়, এবং তারা lazily execute হয়।

  • filter(): এটি একটি শর্তের উপর ভিত্তি করে উপাদানগুলো ফিল্টার করে।
  • map(): এটি একটি Function ব্যবহার করে উপাদানগুলিকে পরিবর্তন বা ট্রান্সফর্ম করে।
  • sorted(): এটি স্ট্রিমের উপাদানগুলো সোর্ট করে।

Example of Intermediate Operations:

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

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Orange", "Mango");

        // Filter elements that start with 'A'
        list.stream()
            .filter(s -> s.startsWith("A"))
            .forEach(System.out::println);  // Output: Apple

        // Map each string to its length
        list.stream()
            .map(String::length)
            .forEach(System.out::println);  // Output: 5 6 6 5 (lengths of Apple, Banana, Orange, Mango)
    }
}

3. Terminal Operations:

Terminal operations স্ট্রিম প্রসেসিং শেষ করে এবং ফলাফল রিটার্ন করে। এই অপারেশনগুলি স্ট্রিমের উপর অপারেশন সম্পন্ন করে।

  • collect(): এটি একটি Collector ব্যবহার করে স্ট্রিমের উপাদানগুলো একটি Collection-এ রূপান্তরিত করে।
  • forEach(): এটি স্ট্রিমের উপাদানগুলো প্রতি একটি লুপের মাধ্যমে চালায়।
  • reduce(): এটি স্ট্রিমের উপাদানগুলোর উপর একটি একক মান গণনা করে।
  • count(): এটি স্ট্রিমের উপাদানের সংখ্যা রিটার্ন করে।

Example of Terminal Operations:

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

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Orange", "Mango");

        // collect to a List
        List<String> result = list.stream()
                                  .filter(s -> s.startsWith("A"))
                                  .collect(Collectors.toList());
        System.out.println(result);  // Output: [Apple]

        // forEach operation
        list.stream().forEach(System.out::println);  // Output: Apple, Banana, Orange, Mango

        // reduce operation (concatenating strings)
        String concatenated = list.stream()
                                  .reduce((s1, s2) -> s1 + " " + s2)
                                  .orElse("No fruits");
        System.out.println(concatenated);  // Output: Apple Banana Orange Mango
    }
}

4. Stream Pipelines:

Stream pipelines সাধারণত intermediate এবং terminal অপারেশনগুলির একটি সংমিশ্রণ। এই pipelines একটি ধারাবাহিক স্ট্রিম প্রসেসিং তৈরি করে।

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

public class Main {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Apple", "Banana", "Orange", "Mango");

        // Stream pipeline example
        long count = list.stream()
                         .filter(s -> s.startsWith("A"))
                         .map(String::toUpperCase)
                         .count();
                         
        System.out.println(count);  // Output: 1 (since only "Apple" starts with A)
    }
}

Stream-এর সুবিধা:

  1. Declarative: Java Streams আপনার ডেটা প্রসেসিং কোডকে declarative, অর্থাৎ সহজ এবং বোধগম্য করে তোলে।
  2. Parallelism: Streams কে parallel হিসাবে ব্যবহার করা যায়, যাতে একাধিক প্রসেসর কোরে ডেটা প্রক্রিয়া করা হয়। এটি বড় ডেটাসেট প্রক্রিয়া করার জন্য উপকারী।
  3. Less Boilerplate: Collection বা Array-তে একে একে লুপিং করার পরিবর্তে, Stream-এ মেথড চেইনিং ব্যবহার করে কোড অনেক পরিষ্কার হয়।
  4. Lazy Evaluation: Intermediate operations lazy evaluation দিয়ে কাজ করে, ফলে অপ্রয়োজনীয় ডেটা প্রক্রিয়া না করেই কেবল প্রয়োজনীয় ডেটাতেই কাজ করা হয়।

সংক্ষেপে:

Java Streams হল একটি শক্তিশালী ফিচার যা functional programming ধারণাকে Java তে প্রবর্তন করেছে। এটি ডেটা সংগ্রহের উপরে কার্যকরভাবে বিভিন্ন ধরনের অপারেশন করতে সাহায্য করে যেমন map, filter, reduce, collect ইত্যাদি। Streams আপনার কোডকে আরও পরিষ্কার এবং কার্যকরী করে তোলে এবং parallel processing এর সুবিধাও প্রদান করে, যা বৃহৎ ডেটাসেটের সাথে কাজ করতে সাহায্য করে।

Content added By

Java 8 এ Streams API চালু করা হয়, যা Functional Programming ধারণাকে Java তে নিয়ে আসে। Streams হল একটি সিকোয়েন্স বা ধারাবাহিক ডেটা যা বিভিন্ন ধরনের অপারেশন (যেমন, ফিল্টার, ম্যাপ, সোর্ট) এর মাধ্যমে প্রসেস করা যায়। Streams API ব্যবহারের মাধ্যমে আপনি ডেটা পরিচালনা করতে পারেন খুবই কার্যকরভাবে, এবং এটি কোডকে আরও পরিষ্কার এবং সংক্ষিপ্ত করে তোলে।

Streams API দুই ধরনের অপারেশন প্রদান করে:

  1. Intermediate Operations (মাঝবর্তী অপারেশন)
  2. Terminal Operations (টার্মিনাল অপারেশন)

এই অপারেশনগুলোর মাধ্যমে আপনি স্ট্রিমের উপর বিভিন্ন প্রকারের কার্যাবলী এক্সিকিউট করতে পারেন।


1. Intermediate Operations (মাঝবর্তী অপারেশন)

Intermediate Operations হল এমন অপারেশন যেগুলি স্ট্রিমে প্রক্রিয়াকরণের একটি পর্যায় তৈরি করে, তবে এটি স্ট্রিমকে পরিবর্তন করে না, বরং একটি নতুন স্ট্রিম তৈরি করে। এই অপারেশনগুলি lazy (অলস) অপারেশন হিসেবে কাজ করে, অর্থাৎ তারা তখনই কার্যকরী হয় যখন একটি Terminal Operation প্রয়োগ করা হয়।

সাধারণ Intermediate Operations:

  • filter(): এটি একটি স্ট্রিম থেকে নির্দিষ্ট শর্ত মেনে এলিমেন্ট নির্বাচন করতে ব্যবহৃত হয়।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    List<Integer> evenNumbers = numbers.stream()
                                        .filter(n -> n % 2 == 0)
                                        .collect(Collectors.toList());
    System.out.println(evenNumbers);  // Output: [2, 4, 6]
    
  • map(): এটি স্ট্রিমের প্রতিটি এলিমেন্টকে একটি ফাংশন প্রয়োগ করে পরিবর্তন করে। এর মাধ্যমে আপনি স্ট্রিমের ডেটার আকার বা রূপ পরিবর্তন করতে পারেন।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
    List<Integer> squares = numbers.stream()
                                    .map(n -> n * n)
                                    .collect(Collectors.toList());
    System.out.println(squares);  // Output: [1, 4, 9, 16]
    
  • distinct(): এটি স্ট্রিম থেকে পুনরাবৃত্তি (duplicates) মুছে দেয়।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
    List<Integer> distinctNumbers = numbers.stream()
                                           .distinct()
                                           .collect(Collectors.toList());
    System.out.println(distinctNumbers);  // Output: [1, 2, 3, 4, 5]
    
  • sorted(): এটি স্ট্রিমের এলিমেন্টগুলোকে স্বাভাবিক ক্রম বা কাস্টম কম্প্যারেটর ব্যবহার করে সাজায়।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(5, 3, 4, 1, 2);
    List<Integer> sortedNumbers = numbers.stream()
                                         .sorted()
                                         .collect(Collectors.toList());
    System.out.println(sortedNumbers);  // Output: [1, 2, 3, 4, 5]
    
  • flatMap(): এটি স্ট্রিমের প্রতিটি এলিমেন্টের মধ্যে থাকা সাব-স্ট্রিমগুলোকে একত্রিত করে একটি নতুন স্ট্রিম তৈরি করে।

    উদাহরণ:

    List<List<Integer>> listOfLists = Arrays.asList(
        Arrays.asList(1, 2, 3),
        Arrays.asList(4, 5, 6),
        Arrays.asList(7, 8)
    );
    List<Integer> flatList = listOfLists.stream()
                                        .flatMap(List::stream)
                                        .collect(Collectors.toList());
    System.out.println(flatList);  // Output: [1, 2, 3, 4, 5, 6, 7, 8]
    

2. Terminal Operations (টার্মিনাল অপারেশন)

Terminal Operations হল অপারেশন যা স্ট্রিমের শেষ কার্যক্রমে ঘটে এবং এটি স্ট্রিমকে এক্সিকিউট করে বা একটি ফলাফল প্রদান করে। একবার একটি টার্মিনাল অপারেশন প্রয়োগ করা হলে, স্ট্রিমের মধ্যে আর কোনো অপারেশন করা যায় না (স্ট্রিম "consumed" হয়ে যায়)।

সাধারণ Terminal Operations:

  • forEach(): স্ট্রিমের প্রতিটি এলিমেন্টের জন্য একটি অ্যাকশন সম্পাদন করতে ব্যবহৃত হয়।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    numbers.stream().forEach(n -> System.out.println(n));  // Output: 1 2 3 4 5
    
  • collect(): এটি একটি স্ট্রিমের এলিমেন্টকে একটি কন্টেইনার (যেমন লিস্ট, সেট) বা একত্রিতভাবে সংগ্রহ করতে ব্যবহৃত হয়। এটি সবচেয়ে বেশি ব্যবহৃত টার্মিনাল অপারেশন।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
    List<Integer> squares = numbers.stream()
                                    .map(n -> n * n)
                                    .collect(Collectors.toList());
    System.out.println(squares);  // Output: [1, 4, 9, 16]
    
  • reduce(): এটি স্ট্রিমের সমস্ত এলিমেন্টগুলিকে একটি একক মানে রিডিউস (সামগ্রিকভাবে) করতে ব্যবহৃত হয়। এটি binary operator ব্যবহার করে একটি একক ফলাফল তৈরি করে।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
    int sum = numbers.stream()
                     .reduce(0, (a, b) -> a + b);
    System.out.println(sum);  // Output: 10
    
  • count(): এটি স্ট্রিমের উপাদানের সংখ্যা প্রদান করে।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    long count = numbers.stream()
                         .count();
    System.out.println(count);  // Output: 5
    
  • anyMatch(), allMatch(), noneMatch(): এগুলি স্ট্রিমের মধ্যে কোনো একটি শর্তের সাথে এলিমেন্ট মিলছে কিনা তা চেক করে।

    • anyMatch(): শর্ত পূরণ করলে true ফেরত দেয়।
    • allMatch(): সবগুলো শর্ত পূরণ করলে true ফেরত দেয়।
    • noneMatch(): কোনো শর্ত পূরণ না হলে true ফেরত দেয়।

    উদাহরণ:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    
    boolean anyEven = numbers.stream()
                             .anyMatch(n -> n % 2 == 0);
    System.out.println(anyEven);  // Output: true
    
    boolean allEven = numbers.stream()
                             .allMatch(n -> n % 2 == 0);
    System.out.println(allEven);  // Output: false
    

3. Combining Operations (অপারেশন একত্রিত করা)

Streams API তে আপনি Intermediate এবং Terminal Operations একসাথে ব্যবহার করে শক্তিশালী এবং কার্যকরী স্ট্রিম অপারেশন তৈরি করতে পারেন।

List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
List<String> result = words.stream()
                           .filter(w -> w.length() > 5)
                           .map(String::toUpperCase)
                           .sorted()
                           .collect(Collectors.toList());
System.out.println(result);  // Output: [BANANA, CHERRY]

এখানে, স্ট্রিমের মধ্যে filter, map, sorted, এবং collect অপারেশনগুলো একত্রিত করা হয়েছে, যা Functional Programming কনসেপ্টে খুবই সাধারণ।


4. Parallel Streams (প্যারালাল স্ট্রিমস)

Streams API তে Parallel Streams ব্যবহারের মাধ্যমে আপনি ডেটার মধ্যে multithreading প্রয়োগ করতে পারেন, যা বড় ডেটা সেট প্রক্রিয়াকরণে সহায়তা করে।

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
                 .reduce(0, (a, b) -> a + b);
System.out.println(sum);  // Output: 15

Parallel Streams কনকারেন্ট এক্সিকিউশন সিস্টেম ব্যবহারের মাধ্যমে কার্যকরীভাবে ডেটা প্রসেস করতে সক্ষম হয়, বিশেষ করে বড় ডেটা সেটের জন্য এটি দ্রুত হয়।


সারাংশ:

Java Streams API ফাংশনাল প্রোগ্রামিং কনসেপ্টকে Java তে নিয়ে এসেছে এবং ডেটা প্রক্রিয়াকরণে IntermediateTerminal Operations এর মাধ্যমে খুবই কার্যকরী এবং সংক্ষিপ্ত কোড লেখার সুবিধা দিয়েছে। filter, map, reduce, collect, sorted সহ অন্যান্য স্ট্রিম অপারেশনগুলো আপনার কোডকে আরও পরিষ্কার, মডুলার এবং রিডেবল করে তোলে। Parallel Streams ব্যবহার করে আপনি প্যারালাল প্রসেসিং এর সুবিধা পেতে পারেন, যা বড় ডেটা সেট প্রক্রিয়াকরণের জন্য অত্যন্ত কার্যকর।

Content added By

Java Functional Programming এর একটি অত্যন্ত গুরুত্বপূর্ণ এবং শক্তিশালী অংশ হল Streams API, যা Java 8 থেকে পরিচিতি লাভ করেছে। Streams API ব্যবহার করে আপনি ডেটার সাথে কার্যকরীভাবে কাজ করতে পারেন, যেমন ডেটা প্রসেসিং, ফিল্টারিং, ম্যাপিং, এবং অন্যান্য বিভিন্ন ট্রান্সফরমেশন, যা সহজ, পাঠযোগ্য এবং কম্প্যাক্ট কোড তৈরিতে সহায়তা করে।

এখানে, Streams এর Advanced Features নিয়ে আলোচনা করা হবে যা আপনাকে ডেটা প্রসেসিংয়ের জন্য আরও শক্তিশালী এবং নমনীয় পদ্ধতি প্রদান করবে।


1. Streams API Overview

Java-এ Streams API একটি functional approach প্রদান করে ডেটা প্রসেসিং করার জন্য। এর মধ্যে ফিচারগুলো filtering, mapping, reducing, collecting এবং sorting সহ বিভিন্ন কার্যকলাপ অন্তর্ভুক্ত। Streams ইন্টারফেসের মাধ্যমে আপনি data sources থেকে ডেটা প্রক্রিয়া করতে পারেন, যেমন collections, arrays, বা I/O channels

Basic Stream Example:

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

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        numbers.stream()
               .filter(n -> n % 2 == 0)  // Filter even numbers
               .forEach(System.out::println);  // Print each filtered number
    }
}

এই উদাহরণে, filter মেথডটি ব্যবহার করা হয়েছে যা even numbers ফিল্টার করে এবং forEach দিয়ে তাদের প্রিন্ট করা হয়েছে।


2. Advanced Features of Streams

2.1. FlatMap: Transforming Nested Collections

flatMap ব্যবহার করা হয় যখন আপনার একটি Nested Collection বা Stream থাকে এবং আপনি চান যে এর মধ্যে থাকা সমস্ত উপাদানগুলোকে একটি ফ্ল্যাট স্ট্রিমে রূপান্তরিত করতে। এটি Stream of Streams কে একত্রিত করে একটি single Stream তৈরি করে।

Example:

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

public class FlatMapExample {
    public static void main(String[] args) {
        List<List<Integer>> numbers = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5),
            Arrays.asList(6, 7, 8)
        );
        
        numbers.stream()
               .flatMap(List::stream)  // Flatten nested List to Stream
               .forEach(System.out::println);
    }
}

এখানে, flatMap মেথডটি List<List<Integer>> কে Stream<Integer>-এ রূপান্তরিত করেছে।

2.2. Collectors: Collecting Results

Collectors হল Streams API-এর একটি অত্যন্ত শক্তিশালী উপাদান, যা স্ট্রিমের উপাদানগুলোকে একটি কন্টেইনারে (যেমন List, Set, Map) সংগ্রহ করতে সহায়তা করে। Spring বা Java-তে ডেটা সংগ্রহ ও প্রসেসিংয়ের জন্য এটি একটি কার্যকরী পদ্ধতি।

  • toList(), toSet(), joining(), groupingBy(), partitioningBy() ইত্যাদি কন্টেইনার প্রসেসিংয়ের জন্য ব্যবহার করা যায়।

Example: Collecting with Collectors.toList():

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

public class CollectorExample {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("apple", "banana", "cherry", "apple", "date");
        List<String> uniqueWords = words.stream()
                                        .distinct()  // Remove duplicates
                                        .collect(Collectors.toList());  // Collect to List
        System.out.println(uniqueWords);
    }
}

এখানে, distinct() ফাংশন ডুপ্লিকেট শব্দগুলো বাদ দিচ্ছে এবং collect() এর মাধ্যমে তা একটি List-এ রূপান্তরিত হচ্ছে।

2.3. GroupingBy: Grouping Elements

groupingBy ব্যবহার করে আপনি একটি স্ট্রিমের উপাদানগুলিকে একটি নির্দিষ্ট ক্রাইটেরিয়াতে গ্রুপ করতে পারেন, যেমন একাধিক কীগুলির উপর ভিত্তি করে ডেটাকে গ্রুপ করা।

Example: Grouping with Collectors.groupingBy():

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

public class GroupingByExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("apple", "banana", "cherry", "date", "apricot");
        
        Map<Character, List<String>> groupedByFirstLetter = fruits.stream()
                                                                  .collect(Collectors.groupingBy(fruit -> fruit.charAt(0)));  // Group by first letter
        
        groupedByFirstLetter.forEach((key, value) -> System.out.println(key + ": " + value));
    }
}

এখানে, groupingBy ফাংশনটি ফলমূলের প্রথম অক্ষরের উপর ভিত্তি করে গ্রুপিং করছে এবং প্রতিটি গ্রুপ প্রিন্ট করছে।

2.4. PartitioningBy: Partitioning Elements into Two Groups

partitioningBy একটি বিশেষ ধরনের groupingBy যেখানে দুটি গ্রুপে ডেটা ভাগ করা হয় (যেমন, সঠিক বা ভুল)। এটি শুধুমাত্র একটি predicate গ্রহণ করে।

Example: Partitioning with Collectors.partitioningBy():

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

public class PartitioningByExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        
        Map<Boolean, List<Integer>> partitioned = numbers.stream()
                                                         .collect(Collectors.partitioningBy(n -> n % 2 == 0));  // Partition by even/odd
        
        partitioned.forEach((key, value) -> System.out.println(key + ": " + value));
    }
}

এখানে, partitioningBy ফাংশনটি সংখ্যাগুলিকে even এবং odd গ্রুপে ভাগ করছে।


3. Reducing: Accumulating Results

reduce() হল একটি শক্তিশালী ফাংশন যা একটি স্ট্রিমের উপাদানগুলোকে একক মানে রূপান্তরিত করে। এটি অ্যাকিউমুলেটর হিসাবে কাজ করে এবং স্ট্রিমের সকল উপাদানকে একটি ফলাফলে কম্পাইল করতে ব্যবহৃত হয়।

Example: Reducing with reduce()

import java.util.*;

public class ReducingExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        int sum = numbers.stream()
                         .reduce(0, (a, b) -> a + b);  // Sum the numbers
        
        System.out.println("Sum: " + sum);  // Output: Sum: 15
    }
}

এখানে, reduce() ফাংশনটি একটি accumulator হিসাবে কাজ করে এবং সমস্ত সংখ্যাকে যোগফল হিসেবে কম্পাইল করে।


4. Lazy Evaluation

Streams এর একটি lazy evaluation সুবিধা রয়েছে, যেখানে স্ট্রিম অপারেশন শুধুমাত্র তখনই কার্যকর হয় যখন ফলাফল প্রয়োজন হয়। এটি stream pipeline অপারেশনগুলিকে যথাযথভাবে প্রক্রিয়া করতে সহায়তা করে, যেমন filter, map, flatMap ইত্যাদি।

Example: Lazy Evaluation

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

public class LazyEvaluationExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Lazy evaluation: only executed when 'forEach' is called
        numbers.stream()
               .filter(n -> n % 2 == 0)  // Filter even numbers
               .map(n -> n * 2)  // Multiply each even number by 2
               .forEach(System.out::println);  // Print results
    }
}

এখানে, filter এবং map অপারেশনগুলো lazy evaluation ভিত্তিতে কাজ করছে, এবং এটি শুধুমাত্র forEach-এ পৌঁছানোর পর এক্সিকিউট হয়।

Java Streams API functional programming এর একটি শক্তিশালী অংশ, যা ডেটা প্রসেসিং এবং ট্রান্সফরমেশনকে অনেক সহজ এবং কার্যকরী করে তোলে।

Advanced Streams Features যেমন flatMap, Collectors, groupingBy, partitioningBy, reduce, এবং lazy evaluation-এর মাধ্যমে আপনি ডেটা পরিচালনা করতে পারবেন আরও শক্তিশালী এবং নমনীয়ভাবে। এটি Java 8 এবং পরবর্তী সংস্করণে ডেটা ম্যানিপুলেশনকে অনেক সহজ এবং পারফরম্যান্স ইফিশিয়েন্ট করে তোলে।

Content added By
Promotion

Are you sure to start over?

Loading...