কম্পিউটার টিউটোরিয়াল

জাভাতে Collection.stream().forEach() এবং Collection.forEach() এর পার্থক্য


ভূমিকা

জাভাতে কালেকশনের উপাদানগুলোর ওপর পুনরাবৃত্তি (iteration) করার জন্য Collection.stream().forEach() এবং Collection.forEach() — এই দুটি পদ্ধতিই ব্যবহৃত হয়। যদিও দুটির কাজ একই মনে হয়, কিন্তু তাদের অভ্যন্তরীণ কার্যপ্রণালী, পারফরম্যান্স এবং আচরণে উল্লেখযোগ্য পার্থক্য রয়েছে। এই আর্টিকেলে আমরা এই দুটি পদ্ধতির মধ্যে মূল পার্থক্যগুলো আলোচনা করব।

মূল পার্থক্য সারসংক্ষেপ

ক্রম প্যারামিটার Collection.forEach() Collection.stream().forEach()
১ বেসিক কাজ কালেকশনের নিজস্ব ইটেরেটর (iterator) ব্যবহার করে। প্রথমে কালেকশনকে স্ট্রিমে রূপান্তর করে, তারপর স্ট্রিমের ওপর পুনরাবৃত্তি করে।
২ ক্রম (Order) ইটেরেবলের নির্দিষ্ট ইটেরেশন ক্রম অনুসরণ করে। প্রক্রিয়াকরণের ক্রম অনির্দিষ্ট (undefined)।
৩ লকিং (Locking) সিঙ্ক্রোনাইজড কালেকশন থাকলে পুরো কলের ধাপে লক ধরে রাখে। কালেকশন লক করে না।
৪ এক্সেপশন হ্যান্ডলিং স্ট্রাকচারাল মডিফিকেশন হলে সাথে সাথে এক্সেপশন থ্রো করে। এক্সেপশন পরে থ্রো হয়।

বিস্তারিত ব্যাখ্যা

Collection.forEach()

Collection.forEach() মেথডটি কালেকশনের বিল্ট-ইন ইটেরেটর ব্যবহার করে প্রতিটি উপাদান প্রসেস করে। অধিকাংশ কালেকশন ক্লাস (যেমন ArrayList, HashSet ইত্যাদি) পুনরাবৃত্তির সময় স্ট্রাকচারাল মডিফিকেশন (এলিমেন্ট যোগ বা ডিলিট) অনুমতি দেয় না। যদি কেউ চেষ্টা করে, তবে সাথে সাথে ConcurrentModificationException থ্রো হয়। এছাড়া, যদি কালেকশন সিঙ্ক্রোনাইজড হয় (যেমন Collections.synchronizedList()), তবে এই মেথডটি পুরো ইটেরেশন চলাকালীন লক ধরে রাখে, যা কনকারেন্সি কমাতে পারে।

Collection.stream().forEach()

Collection.stream().forEach() মেথডটি প্রথমে কালেকশনকে একটি স্ট্রিমে রূপান্তর করে (stream() কল করে), তারপর স্ট্রিমের forEach() টার্মিনাল অপারেশন ব্যবহার করে প্রসেস করে। স্ট্রিম প্যারালLEL প্রসেসিং সাপোর্ট করে, তাই ক্রম নিশ্চিত নয় (অনলেস forEachOrdered() ব্যবহার করা হয়)। এটি লক ধরে না, তাই সিঙ্ক্রোনাইজড কালেকশনে এটি আরও কার্যকর হতে পারে। কিন্তু স্ট্রাকচারাল মডিফিকেশন হলে এক্সেপশন কিছুক্ষণ পরে থ্রো হতে পারে।

কোড উদাহরণ

উদাহরণ ১: Collection.stream().forEach()

import java.util.ArrayList;
import java.util.List;

public class StreamForEachExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Ram");
        list.add("TutorialPoints");
        
        // স্ট্রিমের মাধ্যমে পুনরাবৃত্তি
        list.stream().forEach(System.out::print);
    }
}

আউটপুট: RamTutorialPoints

উদাহরণ ২: Collection.forEach()

import java.util.ArrayList;
import java.util.List;

public class ForEachExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Ram");
        list.add("TutorialPoints");
        
        // সরাসরি forEach ব্যবহার
        list.forEach(System.out::print);
    }
}

আউটপুট: RamTutorialPoints

কোনটি ব্যবহার করবেন?

  • Collection.forEach() ব্যবহার করুন যখন: সাধারণ ইটেরেশন দরকার, ক্রম বজায় রাখতে হয়, এবং সিঙ্ক্রোনাইজড কালেকশন না হয়।
  • Collection.stream().forEach() ব্যবহার করুন যখন: ফাংশনাল স্টাইল প্রোগ্রামিং পছন্দ, ফিল্টার/ম্যাপ/রিডিউস করা লাগে, বা প্যারালেল প্রসেসিং আপ্টিমাইজেশন দরকার।

সারসংক্ষেপ

যদিও দুটি পদ্ধতিই একই ফলাফল দিতে পারে সাধারণ কেসে, কিন্তু পারফরম্যান্স, থ্রেড সেফটি এবং ফাংশনাল প্রোগ্রামিং সুবিধার দৃষ্টিতে stream().forEach() আরও শক্তিশালী। তবে সাধারণ ইটেরেশনের জন্য forEach() সরল এবং দ্রুত। প্রয়োজন অনুযায়ী সঠিকটি বেছে নিন।

  1. জাভাতে Exception এবং Error-এর পার্থক্য

  2. জাভাতে Serialization এবং Externalization-এর পার্থক্য

  3. জাভাতে Iterator এবং Enumeration-এর পার্থক্য

  4. জাভাতে ArrayList এবং HashSet-এর পার্থক্য