Wednesday, April 19, 2017

ল্যামডা এক্সপ্রেশনঃ পর্ব দুই (Lambda Expression: Part 2)

আগে পর্বে জাভাতে ল্যামডা এক্সপ্রেশন কী এবং কীভাবে ব্যবহার করা হয় তার একটি প্রস্তাবনা দেওয়া হয়েছে। এবার আরও ভেতরে প্রবেশ করা যাক-

খুব সহজে করে সংজ্ঞায়িত করা যায় এভাবে - ল্যামডা এক্সপ্রেশন হলো অ্যানোনিমাস ফাংশন যা অন্য মেথডে আর্গুমেন্ট হিসেবে পাস করা যায় বা ভ্যারিয়েবলে অ্যাসাইন করা যায়। 

আমরা সংজ্ঞা থেকে এর তিনটি বৈশিষ্ট্যকে আলাদা করতে পারি -

১. অ্যানোনিমাস- এদের নাম নেই। 
২. ফাংশন- এটিতে মেথডের মতো প্যারামিটার লিস্ট থাকে, রিটার্ন টাইপ থাকে এবং এক্সেপশন লিস্ট থাকতে পারে। তবে যেহেতু এগুলো কোনো ক্লাসের সাথে সংযুক্ত নয় তাই এগুলোকে ফাংশন বলা হয়। 
৩. পাসড অ্যারাউন্ড- এগুলোকে কোনো মেথডের আর্গুমেন্ট হিসেবে পাস করা যায় বা ভ্যারিয়েবলে অ্যাসাইন করা যায়। 

তবে ল্যামডা এক্সপ্রেশনের আরকটি বৈশিষ্ট্য হলো, সংক্ষিপ্ত। এতে অনেক কম লাইন কোড লিখে একই কাজ করা যায়। এতে বয়লারপ্লেট কোড কম থাকে। 

এবার ল্যামডা এক্সপ্রেশনের সিনট্যাক্স দেখা যাক। ল্যামডা এক্সপ্রেশনের তিনটি অংশ থাকে- 

১. প্যারামিটার লিস্ট- এতে শুরুতে কতগুলো প্যারামিটার থাকে। প্যরামিটারগুলোতে টাইপ থাকতে পারে। তবে অনেক্ষেত্রেই টাইপ ইনফরমেশন দেওয়ার প্রয়োজন পরে না। কম্পাইলার বুঝে নিতে পারে। একাধিক প্যরিমিটার থাকলে এগলোকে কমা দিয়ে আলাদা করতে হয় এবং প্যারামিটারগুলো একটি প্যারেন্থেসিসের মধ্যে থাকে। তবে একটি প্যারামিটার থাকলে প্যারেন্থেসিস দেওয়ার প্রয়োজন হয় না। 
২. অ্যারো সাইন - অ্যারো ( -> ) সাইন প্যারামিটার লিস্টকে বডি থেকে আলাদা করে। এটি দুটি ক্যারেক্টার (-) ড্যাশ ও (>) রাইট অ্যাঙ্গেল ব্রাকেট সংযুক্ত করে লেখা হয়। 
৩. বডি - যেখানে মূল কোড অংশটুকু থাকে।

কতগুলো উদাহরণ দেখা যাক - 

1
2
3
(Contact contact1, Contact contact2) -> {
          return contact1.getName().compareTo(contact2.getName());
};

তবে একে সংক্ষিপ্ত করে এক লাইনেও লেখা যায়- 

1
(Contact contact1, Contact contact2) -> contact1.getName().compareTo(contact2.getName());

একে ভ্যারিয়েবলে অ্যাসাইন করা যায়- 

1
Comparator contactComparator = (Contact contact1, Contact contact2) -> contact1.getName().compareTo(contact2.getName());

আরও কয়েকটি উদাহরণ- 


1
2
3
(String s) -> {
        return s.toUpperCase();
}

তবে একে সংক্ষি্প্ত করে লিখলে -


1
(String s) -> s.toUpperCase()

সুতরাং ল্যামডা এক্ষপ্রেশন দু রকম হতে পারে -

(parameters) → expression

অথবা 

(parameters) → {statements;}

বডিতে একাধিক স্ট্যাটমেন্ট থাকলে কার্লি ব্রেসের প্রয়োজন হয়।

Saturday, April 15, 2017

ল্যামডা এক্সপ্রেশনঃ পর্ব এক (Lambda Expression: Part 1)

অনেকগুলো জনপ্রিয় প্রোগ্রামিং ল্যাংগুয়েজেই ল্যামডা এক্সপ্রেশন রয়েছে। যেমন- জাভাস্ক্রিপ্ট, পাইথন, গ্রুভি, স্ক্যালা, সি শার্প, সি++ ইত্যাদি। জাভাতে এই বিশেষ ফিচারটি অনেকদিন অনুপস্থিত থাকায় জাভা প্রোগ্রামারদের মধ্যে মনোকষ্টের কমতি ছিল না। ২০০৬ সালে জাভা পাঁচ রিলিজ হওয়ার পর এই ফিচারটি নিয়ে আলোচনা শুরু হলেও এটি খুব একটা আলোর মুখ দেখে নি। অনেকেই বলতে শুরু করে "Java is the new COBOL”। তবে জাভাকে ওরাকল কিনে নেওয়ার পর এই অবস্থার পরিবর্তন হতে শুরু করে এবং অবশেষে প্রোগ্রামারদের প্রবল মনোবেদনা ঘুচিয়ে ২০১৪ সালে রিলিজ হয় জাভা আট যাতে এই বিশেষ এবং খুবই গুরুত্বপূর্ণ ফিচারটি সংযুক্ত করা হয়।

চলুন তাহলে এই বিশেষ প্রোগ্রামিং ফিচার, ল্যামডা এক্সপ্রেশন নিয়ে আলোচনা করা যাক।


ল্যামডা এক্সপ্রেশনঃ

ল্যামডা টার্মটি এসেছে ল্যম্বডা ক্যালকুলাস (Lambda Calculus) থেকে। এটি গণিতিক যুক্তি ও কম্পিউটার সাইন্সে ব্যবহৃত এক ধরণের ফর্মাল সিস্টেম যাতে বিভিন্ন ধরণের কম্পিউটিশন বিভিন্ন ভ্যারিয়েবলের মাধ্যমে প্রকাশ করা হয়। তবে এই আমাদের এই আলোচনায় তাত্বিক দিকটি পরিহার করে বরং সাধারণভাবে এর আচরণ ও ব্যবহার নিয়ে কথা বলা যাক। 

সহজকরে বলা যেতে পারে যে, ল্যমডা এক্সপ্রেশন হলো একটি ছোট কোড যা কোনো কাজ করে থাকে। উদহারণ-

(int x) → {return x +1;}

উপরের কোডটি একটি ল্যামডা এক্সপ্রেশনের উদাহরণ। এটি দেখে মনে হচ্ছে একটি মেথড কিন্তু এর কোনো নাম নেই। এতে একটি মেথডে যা কিছু থাকে তার পায় সবই রয়েছে, যেমন – প্যারামিটার, মেথড বডি। তবে এতে রিটার্ন টাইপ দেখা যাচ্ছে না। রিটার্ন টাইপ এক্ষেত্রে কম্পাইলার বুঝে নিতে পারে, কারণ এর রিটার্ন স্ট্যাটমেন্টে দেখা যাচ্ছে যে এটি একটি ইন্টিজার রিটার্ন করছে। তাহলে একে বলতে পারি নাম ছাড়া মেথড বা অ্যানোনিমাস মেথড।


অ্যানোনিমাস ইনার ক্লাস ও ল্যাম্ডা এক্সপ্রেশন

জাভা অ্যানোনিমাস ইনার ক্লাস তৈরি করতে দেয় এবং এর বিভিন্ন রকম ব্যবহার রয়েছে। এটিও একধরণের ক্লাস তবে এর কোনো নাম নেই। এটি সাধারণত অন্য একটি ক্লাসের মধ্যে তৈরি করা হয় বলে একে অ্যানোনিমাস ইনার ক্লাস বলা হয়। অ্যানোনিমাস ক্লাস দুই ভাবে তৈরি করা যায়- ইন্টারফেস থেকে এবং একটি ক্লাস থেকে।


1
2
3
public interface Filterable {
     boolean apply(Object obj);
}

উপরের ইন্টারফেইসটি থেকে একটি অ্যানোনিমাস ইনার ক্লাস তৈরি করা যেতে পারে।


1
2
3
4
5
6
7
new Filterable() {
    @Override
    public boolean apply(Object object) {
 // other logic 
        return false;
    }
};

এখানে ইন্টারফেস থেকে একটি ইনার ক্লাস তৈরি করা হয়েছে। এখানে লক্ষ করুন, ইন্টারফেসের সঙ্গে একটি নিউ অপারেটর ব্যবহার করা হয়েছে এবং কার্লি ব্রেসের মধ্যে ইন্টারফেইসটির মেথডটি ইম্প্লিেমেন্ট করা হয়েছে। অর্থাৎ অ্যানোনিমাস ইনার ক্লাস তৈরি করতে হলে নিউ অপারেটর ব্যবহার করতে হয়।

একইভাবে ক্লাস থেকেও অ্যানোনিমাস ইনার ক্লাস তৈরি করা যায়। যে ক্লাস থেকে অ্যানোনিমাস ক্লাস তৈরি করা হবে সেই ক্লাসটিকে অ্যানোনিমাস ইনার ক্লাস ইনহেরিট করে। উদাহরণ-


1
2
3
4
5
new Person() {
    public void doSomething() {
        System.out.println("doing something");
    }
};

এখানে Person ক্লাস ব্যবহার করে একটি অ্যানোনিমাস ক্লাস তৈরি করা হয়েছে। তবে এই পদ্ধতি নিয়ে এই অনুচ্ছেদে এর বেশি আলোচনা হবে না।

এবার চলুন এই অ্যানোনিমাস ইনার ক্লাসের সুবিধা নিয়ে আলোচনা করা যাক -

মনে করুন, আপনার কাছে একটি কন্টাক্ট লিস্ট আছে। আপনি এই কন্টাক্ট লিস্ট থেকে নানাভাবে কন্টাক্টগুলোকে ফিল্টার করতে চান।

আপনার কন্টাক্ট ক্লাসটি দেখতে এরকম-


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Contact {
    enum Sex {
        MALE, FEMALE
    }

    private String name;
    private String emailAddress;
    private String phoneNumber;
    private int age;
    private Sex sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Sex getSex() {
        return sex;
    }

    public void setSex(Sex sex) {
        this.sex = sex;
    }
}

এখন আপনি একটি মেথড লিখতে পারেন, যা আপনার লিস্ট থেকে যাদের বয়স ১৮ থেকে ২৫ এর মধ্যে তাদেরকে আলাদা করবে। মেথডটি এভাবে লেখা যেতে পারে -



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.util.ArrayList;
import java.util.List;

public class ContactService {
    public List<Contact> findContactAgedBetween18And25(List<Contact> contactList) {
        List<Contact> contacts = new ArrayList<>();
        
        for (Contact contact : contactList) {
            if (contact.getAge() >= 18 && contact.getAge() <= 25) {
                contacts.add(contact);
            }
        }
        return contacts;
    }
}

এক্ষেত্রে এই মেথড প্রথমে কন্টাক্ট লিস্ট থেকে একটি ফর লুপ এবং একটি ইফ স্ট্যাটমেন্ট ব্যবহার করে প্রত্যেকটি কন্টাক্ট পরীক্ষা করে দেখছে যে এদের বয়স ১৮ থেকে ২৫ এর মধ্যে কিনা। তারপর মেথডের মধ্যে একটি লোকাল ভ্যারিয়েলে সেগুলো রাখছে এবং লুপ শেষ হয়ে গেলে রিটার্ন করছে।

কিছুক্ষণ পর আপনার মনে হলো, আপনি এই বয়সের মধ্য শুধুমাত্র পুরুষগুলোকে আলাদা করতে চান। সেক্ষেত্রে আপনি একটি নতুন মেথড লিখতে পারেন।


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public List<Contact> findContactMaleAgedBetween18And25(List<Contact> contactList) {
    List<Contact> contacts = new ArrayList<>();

    for (Contact contact : contactList) {
        if (contact.getAge() >= 18
                && contact.getAge() <= 25
                && contact.getSex() == Contact.Sex.MALE) {
            contacts.add(contact);
        }
    }

    return contacts;
}

এভাবে যদি মহিলাদের আলাদা করতে চান তাহলে -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public List<Contact> findContactFemaleAgedBetween18And25(List<Contact> contactList) {
    List<Contact> contacts = new ArrayList<>();

    for (Contact contact : contactList) {
        if (contact.getAge() >= 18
                && contact.getAge() <= 25
                && contact.getSex() == Contact.Sex.FEMALE) {
            contacts.add(contact);
        }
    }

    return contacts;
}

এভাবে প্রত্যেকটি নতুন নতুন রিকোয়ারমেন্টের জন্য আপনাকে নতুন একটি করে মেথড লিখতে হচ্ছে। মেথডগুলোতে লক্ষ্য করুন, প্রত্যকটি মেথডের কোডগুলো প্রায় একই শুধুমাত্র ইফ স্ট্যাটমেন্টের ফিল্টার কীভাবে করা হবে সেই অংশটুকু ছাড়া।

এভাবে যদি আরও অনেকগুলো রিকোয়ারমেন্টের আসে তাহলে আরও অনেকগুলো মেথড লিখতে হবে। তবে একটু বুদ্ধি করে আমরা অ্যানোনিমাস ইনার ক্লাস ব্যবহার করে এই বার বার একইরকম কোড করার কাজটুকু কমিয়ে ফেলতে পারি। এজন্য প্রথমে আমাদের একটি ইন্টারফেস লিখতে হবে।


1
2
3
interface FilterCriteria {
    boolean match(Contact contact);
}

এখন আমরা একটি মেথড লিখবো যেখানে এই ইন্টারফেইসটি প্যারামিটার হিসেবে থাকবে।


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public List<Contact> findContacts(List<Contact> contactList, FilterCriteria criteria) {
    List<Contact> contacts = new ArrayList<>();

    for (Contact contact : contactList) {
        if (criteria.match(contact)) {
            contacts.add(contact);
        }
    }

    return contacts;
}

এই লক্ষ্য করুন , আমরা উপরের মেথডের ইফ স্ট্যাটমেন্টটি সামন্য পরিবর্তন করেছি। এটিতে কোনো নির্দিষ্ট লজিক নেই, বরং এটি আমাদের ইন্টারফেসটির একটি মেথডকে কল করছে যা কিনা বুলিয়ান রিটার্ন করে। এর অর্থ হলো- এই match() মেথডটি যেসব Contact এর ক্ষেত্রে true রিটার্ন করবে শুধুমাত্র সেগুলোকে ভেতরের লিস্টে যুক্ত করবে এবং রিটার্ন করবে। এখন এই মেথডটি ব্যবহার করে দেখা যাক-


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.util.ArrayList;
import java.util.List;

public class ContactApp {
    public static void main(String[] args) {
        List<Contact> contactList = new ArrayList<>(); // lets assume we have that list
        ContactService contactService = new ContactService();
        List<Contact> contactAged18To25 = contactService.findContacts(contactList, new FilterCriteria() {
            @Override
            public boolean match(Contact contact) {
                return contact.getAge() >= 18 && contact.getAge() <= 25;
            }
        });
    }
}

উপরের মেথডটিতে আর্গুমেন্ট হিসেবে একটি অ্যানোনিমাস ইনার ক্লাস দেওয়া হয়েছে যাতে আমরা match() মেথডের মূল লজিকটুকু লিখেছি। এভাবে আমাদের যদি এখন এই ১৮ থেকে ২৫ বছর বয়সের কন্টাক্টগুলো থেকে শুধুমাত্র মহিলাগুলোকে আলাদা করতে চাই তাহলে কন্টাক্ট সার্ভিসে(ContactService) নতুন কোনো মেথড যুক্ত করতে হবে না। শুধুমাত্র কল করার সময় আমাদের কোডের বিহেবিয়ারটুকু আর্গুমেন্ট হিসেবে পাস করলেই হয়ে যাচ্ছে।


1
2
3
4
5
6
7
8
List<Contact> contactAged18To25Female = contactService.findContacts(contactList, new FilterCriteria() {
    @Override
    public boolean match(Contact contact) {
        return contact.getAge() >= 18
                && contact.getAge() <= 25
                && contact.getSex() == Contact.Sex.FEMALE;
    }
});

এভাবে আমরা অ্যানোনিমাস ইনার ক্লাস ব্যবহার করে বিহেবিয়ার প্যারামিটারাইজড করতে পারি। এতে করে অনে কম কোড লিখে অনেক বেশি ফিচার তৈরি করা যাচ্ছে।

তবুও উপরের কোডটিতে খেয়াল করলে দেখা যাবে যে বেশ কিছু কোড বারবার লিখতে হচ্ছে।

চিত্র: প্রয়োজনীয় কোড লজিক

এই কোডটিতে শুধুমাত্র লাল রঙ্গের অংশটুকু আমাদের প্রয়োজনীয় কোড, বাকিটুকু অর্থাৎ নিউ অপারেটর থেকে শুরু করে, ওভারাইড অ্যানোটেশন, পাবলিক কিওয়ার্ড, রিটার্ন টাইপ, মেথডের নাম ইত্যাদি একই রয়ে গেছে পত্যেকবার কল করার সময়। এই কোডগুলোকে boilerplate কোড বলা হয়।

তবে জাভা ৮ ল্যাম্বডা এক্সপ্রশন আসার সুবাধে এই boilerplate কোডটুকুকেও বাদ দেওয়া যায়। উদাহরণ -


1
2
3
4
5
6
List<Contact> contactAged18To25Female = contactService.findContacts(contactList,
        (Contact contact) -> {
            return contact.getAge() >= 18
                    && contact.getAge() <= 25
                    && contact.getSex() == Contact.Sex.FEMALE;
        });

উপরের কোডটুকু লক্ষ্য করুন, এতে শুধুমাত্র প্রয়োজনীয় কোডটুকু রয়েছে। তাহলে আমাদের প্রথম ল্যাম্বডা এক্সপ্রেশন লেখা হয়ে গেলো।

এই প্যারামিটাইরাইজড কোডটুকু আলাদা করে লিখলে -


1
2
3
4
5
6
(Contact contact) -> {
    
    return contact.getAge() >= 18
            && contact.getAge() <= 25
            && contact.getSex() == Contact.Sex.FEMALE;
};

যা শুরুতে ল্যাম্বডা নিয়ে কথা বলার সময় যে উদাহরণ দেওয়া হয়েছিল তার সঙ্গে মিলে যায়।

তাহলে দেখা যাচ্ছে যে, অ্যানোনিমাস ক্লাসের পরিবর্তে আমরা ল্যামডা এক্সপ্রেশন ব্যবহার করতে পারি। তবে এখানে একটি শর্ত রয়েছে। যদি কোনো ইন্টারফেইসে একটি মাত্র মেথড থাকে, এবং সেই ইন্টারফেসটি যেখানে অ্যানোনিমাস ক্লাস হিসেবে ব্যবহার করা যায়, তার পরিবর্তে ল্যামডা এক্সপ্রেশন ব্যবহার করা যাবে। একটি মাত্র মেথডযুক্ত ইন্টারফেসেরের একটি নাম রয়েছে। একে ফাংশনাল ইন্টারফেস বলে। 

আরও একটি ব্যবাহার দেখা যাক-


1
2
3
4
5
6
7
File root = new File("./");
File[] files = root.listFiles(new FileFilter() {
    @Override
    public boolean accept(File pathname) {
        return pathname.isFile();
    }
});

অনেক সময় আমাদের ফাইল লিস্টিং করতে হয়, অর্থাৎ একটি ফোল্ডারে যে ফাইলগুলো  রয়েছে তা বিভিন্নভাবে ফিল্টার করে তার লিস্ট করা যায়। এক্ষেত্রে উপরের কোডটি রুট ফোল্ডারে যে ফাইলগুলো ডিরেক্টরী নয় শুধুমাত্র ফাইল(জাভাতে ইউনিক্স সিস্টেম অনুসরণ করে ফাইল এবং ডিরেক্টরী দুটুই একটি ক্লাস java.io.File দিয়ে অ্যাবস্ট্র্যাক্ট করা হয়েছে), সেগুলোকে লিস্ট করবে।

তবে উপরের কোডটুকু সংক্ষিপ্ত করে ল্যামডা এক্সপ্রেশন ব্যবহাv করে এভাবে লেখা যায় -


1
File[] files = file.listFiles(pathname -&gt; pathname.isFile());

কিংবা আরও সংক্ষেপে -


1
File[] files = file.listFiles(File::isFile); //method reference

এখানে মেথড রেফারেন্স ব্যবহার করা হয়েছে।

এই আর্টিক্যাল এ শুধুমাত্র ল্যামডা এক্সপ্রেশনের সাধারণ ব্যবহার দেখানো হলো। পরিবর্তীতে পর্বে এ নিয়ে আরও বিস্তারিত থাকবে।

Saturday, April 8, 2017

ইনমেমোরী ফাইল সিস্টেম (In memory file system in java)

ফাইল সিস্টেম হলো এক ধরণের ডেটা স্ট্রাকচার ও পদ্ধতি যার মাধ্যে অপারেটিং সিস্টেম ডিস্কে যে বিভিন্নরকম ফাইল রাখা হয় তার ট্র্যাক রাখে। 

অনেক সময়ই বিভিন্ন ইন্টারভিউতে ইন মেমোরী ফাইল সিস্টেম তৈরি করতে বলে। প্রশ্নটি এমন থাকে যে, একই ইন মেমোরী ফাইল সিস্টেম তৈরি রকতে হলে আপনি কীভাবে ডিজাইন করবেন এবং কী কী ডেটা স্ট্রাকচার ব্যবহার করবেন। সম্ভব হলে কিছু কোড লিখে দেখান। (Explain the data structures and algorithm that  you would use to design an in-memory file system. Illustrate with an example in code if possible. )

অনেকই ফাইল সিস্টেমের নাম শুনেই ভয় পেয়ে যায়, মনে করে এটি অনেক লো লেভেল (low level) অর্থাৎ অপারেটিং সিস্টেমের সঙ্গে সম্পর্কিত। কিন্তু সহজ-সরল ডিজাইন কিন্তু খুব কঠিন কিছু নয়। অবজেক্ট ওরিয়েন্টেড ডিজাইন ব্যবহার করেই এটি সমাধান করে ফেলা যায়। 

একটি ফাইল সিস্টেমে থাকে কতগুলো ফাইল এবং ডিরেক্টরী। এরা বেশ কিছু বৈশিষ্ট্য শেয়ার করে। যেমন - এদের নাম থাকে, এদের পাথ থাকে, এগুলোর কখন তৈরি করা হয়েছে, কখন মোডিফাই করা হয়েছে, কবে শেষবার অ্যাকসেস করা হয়েছে ইত্যাদি। এই বৈশিষ্ট্যগুলো নিয়ে আমরা একটি অ্যাবস্ট্রাক্ট সুপার ক্লাস তৈরি করতে পারি। 


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public abstract class Node implements Comparable<Node> {
    private Directory root;
    private String name;
    private LocalDateTime created;
    private LocalDateTime lastUpdated;
    private LocalDateTime lastAccessed;

    public Node(String name) {
        this.name = name;
        this.created = LocalDateTime.now();
        this.lastUpdated = LocalDateTime.now();
        this.lastAccessed = LocalDateTime.now();
    }

    public boolean isDirectory() {
        return this instanceof Directory;
    }

    public String getPath() {
        return root != null ? root.getPath() + "/" + name : name;
    }

    public Node getParent() {
        return root;
    }

    public abstract long getLength();

    public String getName() {
        return name;
    }

    @Override
    public int compareTo(Node o) {
        return this.getName().compareTo(o.getName());
    }

    public void setRoot(Directory root) {
        this.root = root;
    }

    public LocalDateTime getCreated() {
        return created;
    }

    public LocalDateTime getLastUpdated() {
        return lastUpdated;
    }

    public LocalDateTime getLastAccessed() {
        return lastAccessed;
    }

    @Override
    public String toString() {
        return "root=" + root +
                ", \nname='" + name + '\'' +
                ", \ncreated=" + created +
                ", \nlastUpdated=" + lastUpdated +
                ", \nlastAccessed=" + lastAccessed;
    }
}


এখানে কম্পেয়ারেবল ইন্টারফেইস ইমপ্লিমেন্ট করার কারণ হলো, যাতে আমরা সর্ট করতে পারি।

প্রত্যেকটি ফাইলের একটি করে রুট থাকে। রুট না থাকলে সে নিজেই রুট। এই নোডকে এক্সটেন্ড করে একটি ফাইল তৈরি করে ফেলা যাক -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class File extends Node {
    private String content; // for simplicity

    public File(String name, String content) {
        super(name);
        this.content = content;
    }

    public File(String name) {
        super(name);
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public long getLength() {
        if (content != null) {
            return content.getBytes().length;
        }
        return 0;
    }
}


প্রত্যেকটি ফাইল কিছু কন্টেন্ট রাখে। সহজভাবে বুঝানোর জন্য এতে আপরা স্ট্রিং রেখেছি।
এবার ডিরেক্টরী স্ট্রাকচার তৈরি করা যাক -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Directory extends Node {
    private Set<Node> nodes;
    public Directory(String path) {
        super(path);
        nodes = new TreeSet<>();
    }

    public void add(Node node) {
        node.setRoot(this);
        nodes.add(node);
    }

    public Set<Node> getNodes() {
        return nodes;
    }

    @Override
    public long getLength() {
        long length = 0;
        for (Node node : nodes) {
            length += node.getLength();
        }
        return length;
    }

    public int numberOfFiles() {
        int count = 0;
        for (Node node : nodes) {
            if (node instanceof Directory) {
                count++;/// Directory counts as a file
                Directory directory = (Directory) node;
                count += directory.numberOfFiles();
            } else if (node instanceof File) {
                count++;
            }
        }

        return count;
    }
}


প্রত্যেকটি ডিরেক্টরীতে অারও অনেকগুলো করে ফাইল বা ডিরেক্টরী থাকতে পারে। এক্ষেত্রে ডিরেক্টরীতে একটি ট্রিসেটে (TreeSet) রাখা হয়েছে যা সেই ফাইল বা ডিরেক্টরীগুলো ধরে রাখে।

ডিরেক্টরী স্ট্রাকচারকরে সুন্দর করে প্রিন্ট করার জন্য কতগুলো মেথড যুক্ত করা যেতে পারে । যেমন-


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void printTree() {
    int indent = 0;
    StringBuilder sb = new StringBuilder();
    printDirectoryTree(this, indent, sb);
    System.out.println(sb.toString());
}

private void printDirectoryTree(Node node, int indent, StringBuilder sb) {
    sb.append(getIndentString(indent));
    sb.append("+--");
    sb.append(node.getName());
    sb.append("/");
    sb.append("\n");
    if (node.isDirectory()) {
        Directory directory = (Directory) node;
        for (Node file : directory.getNodes()) {
            printDirectoryTree(file, indent + 1, sb);
        }
    }
}

private static String getIndentString(int indent) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < indent; i++) {
        sb.append("|  ");
    }
    return sb.toString();
}


এবার একটি মেইন মেথড থেকে এগুলোকে ব্যবহার করা যাক -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main {
    public static void main(String[] args) {
        Directory root = new Directory("root");
        File file = new File("profile.jpg");
        root.add(file);

        Directory movie = new Directory("movie");
        root.add(movie);

        Directory englishMovie = new Directory("english");
        englishMovie.add(new File("IronFist.mp4"));
        englishMovie.add(new File("The Shawshank Redemption.mp4"));
        englishMovie.add(new File("ZotaPia.mp4"));
        File despicableMe = new File("DespicableMe.mp4");
        englishMovie.add(despicableMe);

        movie.add(englishMovie);

        Directory banglaMovie = new Directory("Bangla");
        banglaMovie.add(new File("The Clay Bird.mp4"));
        banglaMovie.add(new File("Jibon Thekey Neya.mp4"));
        movie.add(banglaMovie);

        root.printTree();

        System.out.println("name: " + movie.getName());
        System.out.println("Created: " + movie.getCreated());
    }
}

এটিকে রান করলে নিচের আউটপুটটি পাওয়া যাবে-


+--root/
|  +--movie/
|  |  +--Bangla/
|  |  |  +--Jibon Thekey Neya.mp4/
|  |  |  +--The Clay Bird.mp4/
|  |  +--english/
|  |  |  +--DespicableMe.mp4/
|  |  |  +--IronFist.mp4/
|  |  |  +--The Shawshank Redemption.mp4/
|  |  |  +--ZotaPia.mp4/
|  +--profile.jpg/

name: movie
Created: 2017-04-08T02:26:38.341


সম্পূর্ণ কোডটি পাওয়া যাবে এখানে: https://gist.github.com/rokon12/d3c83562c785de6d1a483a5585205b92

Saturday, March 25, 2017

জাভা মেইন থ্রেড (Java Main Thread)

জাভা ভার্চুয়াল মেশিন যখন চলা শুরু করে, তখন এটি একটি থ্রেড তৈরি করে। একে মেইন থ্রেড বলা হয়। আমরা জাভাতে যে কোডগুলো করি সেগুলোকে মেইন থ্রেড রান করে। আমরা জানি যে প্রত্যেকটি জাভা প্রোগ্রামের একটি এন্ট্রি পয়েন্ট থাকে আর সেটি হলো মেইন মেথড (static void main(String[] args))। এই মেইন থ্রেড শুরুতেই এই মেইন মেথডটি খুঁজে। এই মেইন মেথড থেকেই প্রোগ্রাম চলা শুরু করে। উদাহরণ-


1
2
3
4
5
6
7
8
public class MainThreadDemo {

 public static void main(String[] args) {

  Thread thread = Thread.currentThread();
  System.out.println("Current Thread: " + thread.getName());
 }
}


এই প্রোগ্রামটি রান করলে আউটপুট আসবে- 

Current Thread: main

এথেকে বোঝা যাচ্ছে যে, জাভাতে আমরা যে কোডগুলো এক্সিকিউট করি সেগুলো কোনো না কোনো থ্রেড রান করে থাকে। 

Try-with-resource statement in java

প্রোগ্রামের কাজ শেষ হয়ে গেলে যে অবজেক্টগুলোকে ক্লোজ করে দেওয়া অত্যাবশ্যক সেগুলোকে রিসোর্স (Resource) বলা হয়। যেমন, FileOutputStream, BufferRedear। এই ক্লাসগুলো java.io.Cloasble ই্ন্টারফেসকে ইমপ্লিমেন্ট করে।


উপরের ইন্টারফেসটিতে দেখা যাচ্ছে যে close() মেথডটি IOException থ্রো করে যা একটি চেকড এক্সেপশন। চেকড এক্সেপশন সবসময় হ্যান্ডেল করতে হয়। এর অর্থ হচ্ছে, এই মেথডটি কল করতে গেলে অবশ্যই try-catch ব্লকের ভেতরে তা কল করতে হবে। উদাহরণ হিসেবে নিচের মেথডটি খেয়াল করা যাক,


উপরের মেথডটিতে close() মেথডটি finally ব্লকের মধ্যে কল করা হয়েছে। এর কারণ প্রোগ্রামটির কাজ শেষ হলেই আমরা রিসোর্স ক্লোজ করতে পারি। এই কোড ব্লক দেখেই মনে হচ্ছে কাজটুকু খুবই বিরক্তিকর। একটি ছোট কাজের জন্য অনেক কোড লিখতে হচ্ছে। এই বিরক্তি দূর করতে জাভা 7 এ নতুন একটি ফিচার সংযুক্ত করা হয়েছে যাকে বলে try-with-resource স্টেটমেন্ট। উপরের মেথডটিকে try-with-resource স্টেটমেন্ট ব্যবহার করে লিখলে দাঁড়ায়,



দুটি কোড মেথড যদিও একই কাজ করে, কিন্তু নিচের মেথডটিকে কম সংখ্যক লাইন কোড রয়েছে উপরের মেথডটি থেকে এবং এতে close() মেথডটি কল করা হয়নি।

এবার নিচের লাইনটি ভালো করে লক্ষ করা যাক,

try (FileInputStream fis = new FileInputStream("file.txt"))

এটি একটি try-with-resource স্টেটমেন্ট। FileInputStream এর ভ্যারিয়েবলটি try কিওয়ার্ডের ভেতরে ডিক্লেয়ার করা হয়েছ এবং ইনস্ট্যানসিয়েট করা হয়েছে। এখানে যখন এই try ব্লক শেষ হয়ে যাবে তখন FileInputStream রিসোর্সটি নিজে থেকেই ক্লোজ হয়ে যাবে। এটি সম্ভব হবে কারণ FileInputStream ক্লাসটি AutoCloseable ইন্টারফেসকে ইমপ্লিমেন্ট করে। যে সব ক্লাসগুলো এই ইন্টারফেসকে ইমপ্লিমেন্ট করে, তাদের প্রত্যেকের ক্ষেত্রে এই try-with-resource স্টেটমেন্ট ব্যবহার করা যাবে।

একাধিক রিসোর্সের ক্ষেত্রেও এই try-with-resource স্টেটমেন্ট ব্যবহার করা যায়। যেমন,


এক্ষেত্রে FileInputStream এবং BufferInputStream দুটি ক্লাসই try ব্লক শেষ হওয়ার সঙ্গে সঙ্গেই ক্লোজ হয়ে যাবে।

Friday, March 24, 2017

মাল্টিথ্রেডেড ফাইল কপিয়ার (Multi-threaded file copier)

আগের দুটি আর্টিক্যালে কীভাবে জাভা দিয়ে ফাইল কপি করা যায় দেখানো হয়েছে ও কীভাবে থ্রেড ব্যবহার করতে হয় তার প্রাথমিক ধারণা দেওয়া হয়েছে। 

এই দুটি ধারণা থেকে আমরা একটি মাল্টি থ্রেডেড ফাইল কপিয়ার তৈরি করতে পারি। চলুন তাহলে লিখে ফেলা যাক- 


এক্ষেত্রে আমাদের প্রথমে একটি কপিয়ার লাগবে যা একই নির্দিষ্ট পাথ থেকে ফাইল  অন্য একটি পাথে কপি করতে পারে।


এরপর একটি থ্রেড ক্লাস তৈরি করি যা কিনা এই কপি অপারেশন সম্পন্ন করবে। 


এই ক্লাসটির রান মেথডে Copier এর একটি ইনস্ট্যান্স তৈরি করা হয়েছে। তারপর এর copy মেথডটিকে কল করা হয়েছে। আমাদের উদ্দ্যেশ্য হচ্ছে, একটি ফাইলের জন্য একটি থ্রেড ব্যবহার করবো। অর্থাৎ আমাদের ডিরেক্টরিতে যদি ৫ টি ফাইল থাকে তাহলে আমরা ৫টি থ্রেড তৈরি করবো এবং এগুলো আলাদা আলাদাভাবে পাশাপাশি কপি করতে থাকবে। 

আমাদের এই প্রোগ্রামটি হবে কমান্ডলাইন প্রোগ্রাম। অর্থাৎ আমরা কমান্ড লাইন থেকে ইনপুট নেবো। এক্ষেত্রে আমাদের দুটি ইনপুট নিতে হবে, একটি সোর্স ডিরেক্টরি, অন্যটি ডেস্টিনেশন, অর্থাৎ যে পাথে ফাইলগুলো রাখবো। 


এই CopyApp ক্লাসটির মেইন মেথডে প্রথমে এর ইনস্ট্যান্স তৈরি করা হয়েছে। তারপর মেইন মেথডের আর্গুমেন্ট অ্যারে থেকে সোর্স ও ডেস্টিনেশন টি আলাদা করে copyFiles এই মেথডে আর্গুমেন্ট হিেবে পাস করা হয়েছে। এই মেথডটিতে প্রথমে রিকার্সিভলি সবগুলো ফাইল একটি লিস্টে রাখা হয়েছে। এরপর একটি লুপের মধ্যে প্রত্যেকটি ফাইলের জন্য একটি করে থ্রেড তৈরি করে স্টার্ট করা হয়েছে। 

এরপর থ্রেডগুলোর রেফারেন্স একটি লিস্টে রাখা হয়েছে এবং একটি লুপরের মাধ্যমে এর join মেথডটি কল করা হয়েছে। 

প্রত্যেকটি জাভা প্রোগ্রাম শুরু হয় একটি মেইন থ্রেড থেকে। অর্থাৎ আমরা যদি জাভা প্রোগ্রামে কোনো থ্রেড তৈরি নাও করি, তাহলেও একটি মেইন থ্রেড আমাদের কোডগুলো এক্সিকিউট করে। তাহলে মেইন থ্রেড থেকে আমরা অনেকগুলো থ্রেড তৈরি করেছি এবং মেইন থ্রেড থেকে এগুলোকে রান করতে দিয়েছি। এই থ্রেডগুলো শেষ না হওয়া পর্যন্ত যাতে করে মেইন থ্রেড অপেক্ষা করে এজন্যে এই join মেথডটি কল করা হয়েছে। সবগুলো থ্রেডের কাজ শেষ হয়ে গেলে মেইন থ্রেডে একটি প্রিন্ট করা হয়েছে যে কপি সম্পন্ন হয়েছে।

এবার কমান্ডলাইনে এটিকে রান করতে হলে -




বিঃদ্রঃ এই আর্টিক্যালটি শুধুমাত্র তাদের জন্য যারা উপরে উল্লেখিত আর্টিক্যাল দুটি পড়েছে। এখানে যে কোড দেখানো হয়েছে এগুলো জাভা ৮ ব্যবহার করে আরও সংক্ষিপ্ত ও আর ভালোভাবে লেখা যায়। পরবর্তীতে তা দেখানো হবে।

Thursday, March 23, 2017

জাভা থ্রেড প্রোগ্রামিংঃ পর্ব- এক (Java threading: part -1 )

বর্তমানে সব ধরণের কম্পিউটারই একই সঙ্গে একাধিক কাজ করে থাকে অর্থাৎ অনেকগুলো প্রোগ্রাম রান করে। প্রত্যেকটি আলাদা আলাদা প্রোগ্রামের জন্য অপারেটিং সিস্টেম একটি করে প্রসেস রান করে। প্রসেসগুলো একে অপরের থেকে স্বাধীন এবং ভিন্ন হয়। এগুলোর নিজস্ব এক্সিকিউশন এনভাইরনমেন্ট ও মেমোরি স্পেস থাকে। প্রসেসগুলো একইসঙ্গে পাশাপাশি চলতে থাকে। উদাহরণসরূপ- আমরা একইসঙ্গে ইউটিউবে গান শুনতে পারি ও ওয়ার্ড প্রসেসরে ডকুমেন্ট টাইপ করতে পারি। 

তবে কখনো কখনো কোনো একটি প্রোগ্রামের বিভিন্ন অংশ পাশাপাশি চলার প্রয়োজন হতে পারে। সহজভাবে বোঝার জন্য ধরা ওয়ার্ডপ্রসেসরের দুটি ফাংশনালিটি কথা চিন্তা করা যাক- ইউজার যখন কিবোর্ড টাইপ করে সেগুলো ইনপুট হিসেবে নেওয়া ও বানান দেখা। এটি একটি নির্দিষ্ট প্রোগ্রামের দুটি অংশ। এগুলো পাশাপাশি চলে। অর্থাৎ আমরা যখন টাইপ করি, সঙ্গে সঙ্গে এগুলোর বানান ঠিক আছে কিনা তা দেখার প্রক্রিয়াটিও চলতে থাকে। একই প্রোগ্রামের একাধিক কাজ পাশাপাশি করার জন্য থ্রেড প্রয়োজন হয়। থ্রেডকে অনেকসময় লাইটওয়েট প্রসেসও বলা হয়। অর্থাৎ এগুলো একটি প্রসেসের মধ্যে ছোট ছোট প্রসেস যাদের নিজস্ব এক্সিকিউশন এনভাইরনমেন্ট থাকে। এক্সিকিউশন এনভাইরনমেন্ট অর্থ হলো যেখানে নির্দিষ্ট কোড এক্সিকিউট হয়। একটি প্রোগ্রামে অনেকগুলো থ্রেড কাজ করলে তাকে মাল্টিথ্রেডেড প্রোগ্রাম বলা হয়। 

আধুনিক প্রায় সব প্রোগ্রাম বা সফটওয়্যারগুলো মাল্টিথ্রেডেড। মাল্টিথ্রেডেড প্রোগ্রামিংকে কনকারেন্ট প্রোগ্রামিংও বলা হয়। কারণ এতে কোনো প্রোগ্রামের বিভিন্ন অংশ কনকারেন্টলি(concurrently) বা একইসঙ্গে চলে। জাভা প্রোগ্রামিং ল্যাংগুয়েজ ব্যবহার করে মাল্টিথ্রেডেড প্রোগ্রাম লেখা খুব সহজ। 

জাভাতে থ্রেড প্রোগ্রামিংয়ের ক্ষেত্রে Thread ক্লাসটি ব্যবহার করা হয়। জাভাতে দুইভাবে এই ক্লাসটি ব্যবহার করা যায়। 

১. Thread ক্লাসটিকে এক্সটেন্ড করে।
২. Runnable ইন্টারিফেস ব্যবহার করে। 

প্রথমে আমরা Thread ক্লাসটিকে এক্সটেন্ড করে একটি উদাহরণ দেখি-




উপরের প্রোগ্রামটিতে Thread ক্লাসটিকে ইনহেরিট করে একটি MyThread নামে একটি ক্লাস লেখা হয়েছে। Thread ক্লাসটি run() নামে একটি মেথড রয়েছে। এই মেথডটিতে ওভাররাইড করে এতে কিছু কোড লেখা হয়েছে। এই কোড অংশটুকু আমরা কনকারেন্টলি রান করতে চাই। এখানে একটি লুপ লেখা হয়েছে এবং এতে একটি প্রিন্ট মেথড রয়েছে। 

এরপর থ্রেড ক্লাসের একটি স্ট্যাটিক মেথড sleep() ব্যবহার করে কিছুক্ষণের জন্য প্রোগ্রামটিকে থামিয়ে দেওয়া হয়েছে। এই মেথড আর্গুমেন্ট হিসেবে একটি সংখ্যা নেয়। যে সংখ্যাটি দেওয়া হবে, ঠিক ততো মিলি সেকেন্ড এই থ্রেডটি বন্ধ হয়ে থাকেবে এবং আবার শুরু করবে। একটি সংখ্যা প্রিন্ট করতে মোটেও কোনো সময় লাগে না, এটি শুধুমাত্রে বোঝার সুবিধার্থে ব্যবহার করা হয়েছে যে, যাতে মনে হয় থ্রেডটি বেশ কিছুক্ষণ কাজ করছে। এই মেথডটি একটি চেকড এক্সেপশন থ্রো করে, এজন্য একে ট্রাই ক্যাচ ব্লকের মধ্যে রাখা হয়েছে। 

এবার এই ক্লাসটিকে একটি মেইন মেথড থেকে ব্যবহার করা যাক-





এখানে MyThread ক্লাসটির দুটি ইনস্ট্যান্স তৈরি করা হয়েছে। থ্রেড রান করা জন্য এর start() মেথডটি কল করতে হয়। 

এই প্রোগ্রামটি রান করলে যে আউটপুট আসবে তা হলো - 

0
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9

এর অর্থ হলো দুটি লুপই পাশাপাশি রান করছে। 

এবার দ্বিতীয় উপায়টি অর্থাৎ Runnable ইন্টারফেইস ব্যবহার করে একটি উদাহরণ দেখা যাক- 






এখানে MyRunnable ক্লাসটি Runnable ইন্টারফেইসকে ইমপ্লিমেন্ট করেছে। এই ইন্টারফেইসটিতে একটি মাত্র মেথড run() রয়েছে। যে কোড অংশটুকু কনকারেন্টলি রান করার প্রয়োজন সেই কোড অংশটুকু এই মেথডে লিখতে হয়। এই ক্লাসটিকে ব্যবহার করতে হলে- 





এখানে প্রথমে একটি MyRunnable ক্লাসের ইনস্ট্যান্স তৈরি করা হয়েছে। এরপর Thread ক্লাসের কনস্ট্রাক্টরে এই ইনস্ট্যান্সটি আর্গুমেন্ট হিসেবে দিয়ে Thread ক্লাসের ইনস্ট্যান্স তৈরি করা হয়েছে। এরপর থ্রেডটিকে রান করার জন্য start() মেথডটি কল করা হয়েছে। 


এখানে একটি বিষয় মনে রাখতে হবে যে, যদিও যে কোড অংশটুকু কনকারেন্টলি রান করার প্রয়োজন তা রান মেথডের মধ্যে লিখতে হয়, কিন্তু থ্রেড রান করার জন্য এই মেথডটি ব্যবহার করা হয় না। এই মেথডটি থ্রেড নিজে ব্যবহার করে থেকে। থ্রেড রান করার জন্য start() মেথডটি কল করতে হয়। 


চলবে...