ByteArrayOutputStream ক্লাস একটি আউটপুট স্ট্রিম প্রয়োগ করে যা একটি বাইট অ্যারেতে ডেটা লেখে। এতে ডেটা লেখার সাথে সাথে বাফারটি স্বয়ংক্রিয়ভাবে বৃদ্ধি পায়।

ByteArrayOutputStream ক্লাস মেমরিতে একটি বাফার তৈরি করে এবং স্ট্রীমে পাঠানো সমস্ত ডেটা বাফারে সংরক্ষণ করা হয়।

ByteArrayOutputStream কনস্ট্রাক্টর

ByteArrayOutputStream ক্লাসে নিম্নলিখিত কনস্ট্রাক্টর রয়েছে :

কনস্ট্রাক্টর
ByteArrayOutputStream() এই কনস্ট্রাক্টর একটি ইন-মেমরি বাফার তৈরি করে যা 32 বাইট দীর্ঘ।
ByteArrayOutputStream(int a) এই কনস্ট্রাক্টর একটি নির্দিষ্ট আকারের সাথে একটি ইন-মেমরি বাফার তৈরি করে।

এবং এটি ক্লাসের ভিতরের মত দেখাচ্ছে:


// The buffer itself, where the data is stored.
protected byte buf[];

// Current number of bytes written to the buffer.
protected int count;

public ByteArrayOutputStream() {
    this(32);
}

public ByteArrayOutputStream(int size) {
    if (size < 0) {
        throw new IllegalArgumentException("Negative initial size: "
                                           + size);
    }
    buf = new byte[size];
}
    

ByteArrayOutputStream ক্লাসের পদ্ধতি

আসুন আমরা আমাদের ক্লাসে যে পদ্ধতিগুলি ব্যবহার করতে পারি সে সম্পর্কে কথা বলি।

আসুন আমাদের স্রোতে কিছু রাখার চেষ্টা করি। এটি করার জন্য, আমরা write() পদ্ধতি ব্যবহার করব — এটি লেখার জন্য একটি বাইট বা বাইটের একটি সেট গ্রহণ করতে পারে।

পদ্ধতি
অকার্যকর লিখুন (int b) এক বাইট লেখে।
অকার্যকর লিখুন (বাইট বি [], int বন্ধ, int লেন) একটি নির্দিষ্ট আকারের বাইটের একটি অ্যারে লেখে।
অকার্যকর রাইটবাইটস(বাইট বি[]) বাইটের একটি অ্যারে লেখে।
অকার্যকর লিখুন (আউটপুট স্ট্রিম আউট) বর্তমান আউটপুট স্ট্রীম থেকে পাস করা আউটপুট স্ট্রীমে সমস্ত ডেটা লেখে।

পদ্ধতি বাস্তবায়ন:


public static void main(String[] args) throws IOException {
   ByteArrayOutputStream outputByte = new ByteArrayOutputStream();
   // Write one byte
   while(outputByte.size()!= 7) {
      outputByte.write("codegym".getBytes());
   }

   // Write array of bytes
   String value = "\nWelcome to Java\n";
   byte[] arrBytes = value.getBytes();
   outputByte.write(arrBytes);

   // Write part of an array
   String codeGym = "CodeGym";
   byte[] b = codeGym.getBytes();
   outputByte.write(b, 4, 3);

   // Write to a file
   FileOutputStream fileOutputStream = new FileOutputStream("output.txt");
   outputByte.write(80);
   outputByte.writeTo(fileOutputStream);
}
    

ফলাফল হল একটি নতুন output.txt ফাইল যা দেখতে এইরকম:

toByteArray () পদ্ধতি এই আউটপুট স্ট্রীমের বর্তমান বিষয়বস্তুকে বাইটের অ্যারে হিসাবে প্রদান করে। এবং আপনি পাঠ্য হিসাবে buf বাইট অ্যারে পেতে toString() পদ্ধতি ব্যবহার করতে পারেন :


public static void main(String[] args) throws IOException {
    ByteArrayOutputStream outputByte = new ByteArrayOutputStream();

    String value = "CodeGym";
    outputByte.write(value.getBytes());

    byte[] result = outputByte.toByteArray();
    System.out.println("Result: ");

    for(int i = 0 ; i < result.length; i++) {
        // Display the characters
        System.out.print((char)result[i]);
    }
}
    

আমাদের বাফারে বাইট অ্যারে রয়েছে যা আমরা এটিতে পাস করেছি।

reset () পদ্ধতিটি বাইট অ্যারে আউটপুট স্ট্রিমে বৈধ বাইটের সংখ্যা শূন্যে রিসেট করে (তাই আউটপুটে জমে থাকা সবকিছু রিসেট করা হয়)।


public static void main(String[] args) throws IOException {
   ByteArrayOutputStream outputByte = new ByteArrayOutputStream(120);

   String value = "CodeGym";
   outputByte.write(value.getBytes());
   byte[] result = outputByte.toByteArray();
   System.out.println("Output before reset: ");

   for (byte b : result) {
      // Display the characters
      System.out.print((char) b);
   }

   outputByte.reset();

   byte[] resultAfterReset = outputByte.toByteArray();
   System.out.println("\nOutput after reset: ");

   for (byte b : resultAfterReset) {
      // Display the characters
      System.out.print((char) b);
   }
}
    

reset() মেথড কল করার পর যখন আমরা আমাদের বাফার প্রদর্শন করি , তখন আমরা কিছুই পাই না।

ক্লোজ() পদ্ধতির নির্দিষ্ট বৈশিষ্ট্য

এই পদ্ধতি বিশেষ মনোযোগ প্রাপ্য। এটি কী করে তা বোঝার জন্য, আসুন ভিতরে উঁকি দেওয়া যাক:


/**
 * Closing a {@code ByteArrayOutputStream} has no effect. The methods in
 * this class can be called after the stream has been closed without
 * generating an {@code IOException}.
 */
public void close() throws IOException {
}
    

মনে রাখবেন যে ByteArrayOutputStream ক্লাসের close() পদ্ধতি আসলে কিছুই করে না।

তা কেন? একটি ByteArrayOutputStream একটি মেমরি-ভিত্তিক স্ট্রীম (অর্থাৎ, এটি কোডে ব্যবহারকারী দ্বারা পরিচালিত এবং জনবহুল), তাই close() কল করার কোনো প্রভাব নেই।

অনুশীলন করা

এখন আমাদের ByteArrayOutputStream এবং ByteArrayInputStream ব্যবহার করে একটি ফাইল সিস্টেম বাস্তবায়ন করার চেষ্টা করা যাক ।

সিঙ্গলটন ডিজাইন প্যাটার্ন ব্যবহার করে একটি ফাইলসিস্টেম ক্লাস লিখি এবং একটি স্ট্যাটিক HashMap<String, byte[]> ব্যবহার করি , যেখানে:

  • স্ট্রিং হল একটি ফাইলের পথ
  • বাইট [] সংরক্ষিত ফাইলের ডেটা

import java.io.*;
import java.util.HashMap;
import java.util.Map;

class FileSystem {
   private static final FileSystem fileSystem = new FileSystem();
   private static final Map<String, byte[]> files = new HashMap<>();

   private FileSystem() {
   }

   public static FileSystem getFileSystem() {
       return fileSystem;
   }

   public void create(String path) {
       validateNotExists(path);
       files.put(path, new byte[0]);
   }

   public void delete(String path) {
       validateExists(path);
       files.remove(path);
   }

   public boolean isExists(String path) {
       return files.containsKey(path);
   }

   public InputStream newInputStream(String path) {
       validateExists(path);
       return new ByteArrayInputStream(files.get(path));
   }

   public OutputStream newOutputStream(String path) {
       validateExists(path);
       return new ByteArrayOutputStream() {
           @Override
           public void flush() throws IOException {
               final byte[] bytes = toByteArray();
               files.put(path, bytes);
               super.flush();
           }

           @Override
           public void close() throws IOException {
               final byte[] bytes = toByteArray();
               files.put(path, bytes);
               super.close();
           }
       };
   }

   private void validateExists(String path) {
       if (!files.containsKey(path)) {
           throw new RuntimeException("File not found");
       }
   }

   private void validateNotExists(String path) {
       if (files.containsKey(path)) {
           throw new RuntimeException("File exists");
       }
   }
}
    

এই ক্লাসে, আমরা নিম্নলিখিত পাবলিক পদ্ধতি তৈরি করেছি:

  • স্ট্যান্ডার্ড CRUD পদ্ধতি (তৈরি, পড়া, আপডেট, মুছে ফেলা),
  • একটি ফাইল বিদ্যমান কিনা তা পরীক্ষা করার একটি পদ্ধতি,
  • ফাইল সিস্টেমের একটি উদাহরণ পেতে একটি পদ্ধতি।

একটি ফাইল থেকে পড়তে, আমরা একটি ইনপুটস্ট্রিম ফেরত দেই । হুড অধীনে ByteArrayInputStream বাস্তবায়ন হয়. বাফার হল ফাইল ম্যাপে সংরক্ষিত একটি বাইট অ্যারে।

আরেকটি আকর্ষণীয় পদ্ধতি হল newOutputStream । যখন এই পদ্ধতিটি বলা হয়, তখন আমরা একটি নতুন ByteArrayOutputStream অবজেক্ট ফেরত দিই যা দুটি পদ্ধতিকে ওভাররাইড করে: flush এবং close । এই পদ্ধতিগুলির যেকোন একটিকে কল করলে লেখাটি সংঘটিত হওয়া উচিত।

এবং আমরা ঠিক এটিই করি: আমরা বাইট অ্যারে পাই যা ব্যবহারকারী লিখেছে এবং একটি অনুলিপি হিসাবে সংরক্ষণ করেমানএকটি উপযুক্ত কী দিয়ে ফাইল ম্যাপে

আমরা আমাদের ফাইল সিস্টেম (FS) পরীক্ষা করতে নিম্নলিখিত কোড ব্যবহার করি:


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import static java.nio.charset.StandardCharsets.UTF_8;

public class MyFileSystemTest {
   public static void main(String[] args) throws IOException {
       FileSystem fileSystem = FileSystem.getFileSystem();
       final String path = "/user/bin/data.txt";

       // Create a file
       fileSystem.create(path);
       System.out.println("File created successfully");

       // Make sure it's empty
       try (InputStream inputStream = fileSystem.newInputStream(path)) {
           System.out.print("File contents:\t");
           System.out.println(read(inputStream));
       }

       // Write data to it
       try (final OutputStream outputStream = fileSystem.newOutputStream(path)) {
           outputStream.write("CodeGym".getBytes(UTF_8));
           System.out.println("Data written to file");
       }

       // Read data
       try (InputStream inputStream = fileSystem.newInputStream(path)) {
           System.out.print("File contents:\t");
           System.out.println(read(inputStream));
       }

       // Delete the file
       fileSystem.delete(path);

       // Verify that the file does not exist in the FS
       System.out.print("File exists:\t");
       System.out.println(fileSystem.isExists(path));

   }

   private static String read(InputStream inputStream) throws IOException {
       return new String(inputStream.readAllBytes(), UTF_8);
   }
}
    

পরীক্ষার সময়, আমরা নিম্নলিখিত ক্রিয়াগুলি যাচাই করি:

  1. আমরা একটি নতুন ফাইল তৈরি করি।
  2. আমরা পরীক্ষা করি যে তৈরি করা ফাইলটি খালি আছে।
  3. আমরা ফাইলে কিছু ডেটা লিখি।
  4. আমরা ডেটা আবার পড়ি এবং যাচাই করি যে এটি আমরা যা লিখেছি তার সাথে মেলে।
  5. আমরা ফাইল মুছে ফেলি।
  6. আমরা যাচাই করি যে ফাইলটি মুছে ফেলা হয়েছে।

এই কোড চালানো আমাদের এই আউটপুট দেয়:

ফাইল সফলভাবে তৈরি হয়েছে
ফাইলের বিষয়বস্তু:
ফাইলে লেখা ডেটা
ফাইলের বিষয়বস্তু: কোডজিম
ফাইল বিদ্যমান: মিথ্যা

কেন এই উদাহরণ প্রয়োজন ছিল?

সহজ করে বললে, ডেটা সবসময় বাইটের একটি সেট। আপনার যদি ডিস্ক থেকে/এ প্রচুর ডেটা পড়তে/লিখতে হয়, তাহলে I/O সমস্যার কারণে আপনার কোড ধীরে ধীরে চলবে। এই ক্ষেত্রে, এটি RAM-তে একটি ভার্চুয়াল ফাইল সিস্টেম বজায় রাখা বোধগম্য, এটির সাথে একইভাবে কাজ করে যেমন আপনি একটি প্রথাগত ডিস্কের সাথে করেন। এবং ইনপুটস্ট্রিম এবং আউটপুটস্ট্রিমের চেয়ে সহজ আর কী হতে পারে ?

অবশ্যই, এটি নির্দেশের জন্য একটি উদাহরণ, উৎপাদন-প্রস্তুত কোড নয়। এটির জন্য হিসাব নেই (নিম্নলিখিত তালিকাটি ব্যাপক নয়):

  • মাল্টিথ্রেডিং
  • ফাইলের আকার সীমা (চলমান JVM এর জন্য উপলব্ধ RAM এর পরিমাণ)
  • পাথ গঠন যাচাই
  • পদ্ধতি আর্গুমেন্ট যাচাই

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

উপসংহার এবং বড় প্রশ্ন

এই পাঠটি পড়ার পরে বড় প্রশ্নটি হল "কেন আমি শুধু বাইট [] ব্যবহার করতে পারি না , যেহেতু এটি আরও সুবিধাজনক এবং বিধিনিষেধ আরোপ করে না?"

ByteArrayInputStream এর সুবিধা হল যে এটি শক্তিশালী ইঙ্গিত দেয় যে আপনি শুধুমাত্র পঠনযোগ্য বাইট ব্যবহার করতে যাচ্ছেন (কারণ স্ট্রিমটি এর বিষয়বস্তু পরিবর্তন করার জন্য একটি ইন্টারফেস প্রদান করে না)। এটি বলেছে, এটি লক্ষ্য করা গুরুত্বপূর্ণ যে একজন প্রোগ্রামার এখনও সরাসরি বাইট অ্যাক্সেস করতে পারে ।

কিন্তু যদি কখনও কখনও আপনার কাছে একটি বাইট থাকে[] , কখনও কখনও আপনার কাছে একটি ফাইল থাকে, কখনও কখনও আপনার কাছে একটি নেটওয়ার্ক সংযোগ থাকে এবং তাই, আপনার "বাইটের একটি স্ট্রীম" এর জন্য কিছু ধরণের বিমূর্ততা প্রয়োজন, এবং সেগুলি কোথায় তা আমি চিন্তা করি না থেকে আসছে". এবং যে কি ইনপুটস্ট্রিম হয়. যখন উত্সটি একটি বাইট অ্যারে হয়, তখন ব্যবহার করার জন্য ByteArrayInputStream একটি ভাল ইনপুটস্ট্রিম ।

এটি অনেক পরিস্থিতিতে সহায়ক, তবে এখানে দুটি নির্দিষ্ট উদাহরণ রয়েছে:

  1. আপনি একটি লাইব্রেরি লিখছেন যা বাইট গ্রহণ করে এবং সেগুলিকে একরকম প্রক্রিয়া করে (উদাহরণস্বরূপ, ধরুন এটি চিত্র প্রক্রিয়াকরণ ইউটিলিটিগুলির একটি লাইব্রেরি)। আপনার লাইব্রেরির ব্যবহারকারীরা একটি ফাইল থেকে, একটি ইন-মেমরি বাইট[] বা অন্য কোনো উৎস থেকে বাইট প্রদান করতে পারে। সুতরাং আপনি একটি ইন্টারফেস প্রদান করেন যা একটি InputStream গ্রহণ করে , যার অর্থ হল যদি তাদের একটি বাইট [] থাকে, তাহলে তাদের এটি একটি ByteArrayInputStream- এ মোড়ানো দরকার ।

  2. আপনি কোড লিখছেন যা একটি নেটওয়ার্ক সংযোগ পড়ে। কিন্তু এই কোডে ইউনিট পরীক্ষা করার জন্য, আপনি কোনো সংযোগ খুলতে চান না — আপনি শুধু কোডটিতে কয়েকটি বাইট দিতে চান। সুতরাং কোডটি একটি ইনপুটস্ট্রিম নেয় এবং আপনার পরীক্ষাটি একটি ByteArrayInputStream এ পাস করে ।