ByteArrayOutputStream वर्ग एक आउटपुट स्ट्रीम लागू करता है जो डेटा को बाइट सरणी में लिखता है। जैसे ही डेटा लिखा जाता है, बफ़र अपने आप बढ़ जाता है।

ByteArrayOutputStream वर्ग मेमोरी में एक बफर बनाता है, और स्ट्रीम को भेजे गए सभी डेटा को बफर में संग्रहीत किया जाता है

ByteArrayOutputStream कंस्ट्रक्टर

ByteArrayOutputStream वर्ग में निम्नलिखित निर्माता हैं:

निर्माता
ByteArrayOutputStream () यह कन्स्ट्रक्टर एक इन-मेमोरी बफर बनाता है जो 32 बाइट लंबा होता है।
ByteArrayOutputStream (इंट ए) यह कन्स्ट्रक्टर एक विशिष्ट आकार के साथ एक इन-मेमोरी बफर बनाता है।

और यह वर्ग अंदर जैसा दिखता है:

// 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 वर्ग के तरीके

आइए उन विधियों के बारे में बात करें जिनका हम अपनी कक्षा में उपयोग कर सकते हैं।

आइए हमारी धारा में कुछ डालने का प्रयास करें। ऐसा करने के लिए, हम राइट () विधि का उपयोग करेंगे - यह लिखने के लिए एक बाइट या बाइट्स के एक सेट को स्वीकार कर सकता है।

तरीका
शून्य लेखन (इंट बी) एक बाइट लिखता है।
शून्य लेखन (बाइट बी [], इंट ऑफ, इंट लेन) एक विशिष्ट आकार के बाइट्स की एक सरणी लिखता है।
शून्य राइटबाइट्स (बाइट बी []) बाइट्स की एक सरणी लिखता है।
शून्य लिखने के लिए (आउटपुटस्ट्रीम आउट) वर्तमान आउटपुट स्ट्रीम से पारित आउटपुट स्ट्रीम में सभी डेटा लिखता है।

विधि कार्यान्वयन:

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);
}

नतीजा एक नई आउटपुट.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]);
    }
}

हमारे बफ़र में वह बाइट सरणी होती है जिसे हमने पास किया था।

रीसेट () विधि बाइट सरणी आउटपुट स्ट्रीम में मान्य बाइट्स की संख्या को शून्य पर रीसेट करती है (इसलिए आउटपुट में संचित सब कुछ रीसेट हो जाता है)।

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);
   }
}

जब हम रीसेट () विधि को कॉल करने के बाद अपना बफर प्रदर्शित करते हैं, तो हमें कुछ नहीं मिलता है।

बंद () विधि की विशिष्ट विशेषताएं

यह विधि विशेष ध्यान देने योग्य है। यह समझने के लिए कि यह क्या करता है, आइए अंदर झांकें:

/**
 * 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 क्लास का क्लोज़ () मेथड वास्तव में कुछ भी नहीं करता है।

ऐसा क्यों? ByteArrayOutputStream एक मेमोरी-आधारित स्ट्रीम है (अर्थात, इसे उपयोगकर्ता द्वारा कोड में प्रबंधित और पॉप्युलेट किया जाता है), इसलिए कॉलिंग क्लोज़ () का कोई प्रभाव नहीं पड़ता है।

अभ्यास

अब आइए अपने ByteArrayOutputStream और ByteArrayInputStream का उपयोग करके फ़ाइल सिस्टम को कार्यान्वित करने का प्रयास करें ।

आइए सिंगलटन डिज़ाइन पैटर्न का उपयोग करके एक FileSystem वर्ग लिखें और एक स्थिर 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");
       }
   }
}

इस वर्ग में, हमने निम्नलिखित सार्वजनिक विधियाँ बनाई हैं:

  • मानक सीआरयूडी विधियां (बनाएं, पढ़ें, अपडेट करें, हटाएं),
  • फ़ाइल मौजूद है या नहीं, यह जांचने का एक तरीका
  • फ़ाइल सिस्टम का एक उदाहरण प्राप्त करने की विधि।

किसी फ़ाइल से पढ़ने के लिए, हम एक InputStream वापस करते हैं । हुड के तहत ByteArrayInputStream कार्यान्वयन है। बफ़र फ़ाइल मैप में संग्रहीत एक बाइट सरणी है।

एक और दिलचस्प तरीका newOutputStream है । जब इस विधि को कहा जाता है, तो हम एक नया ByteArrayOutputStream ऑब्जेक्ट लौटाते हैं, जो दो तरीकों को ओवरराइड करता है: फ्लश और क्लोज । इन विधियों में से किसी एक को कॉल करने से लिखने का कारण बनना चाहिए।

और ठीक यही हम करते हैं: हमें वह बाइट सरणी मिलती है जिसे उपयोगकर्ता ने लिखा है, और एक कॉपी कोकीमतफ़ाइलों के नक्शे में एक उपयुक्त कुंजी के साथ।

हम अपने फ़ाइल सिस्टम (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. हम सत्यापित करते हैं कि फ़ाइल हटा दी गई है।

इस कोड को चलाने से हमें यह आउटपुट मिलता है:

फ़ाइल सफलतापूर्वक बनाई गई
फ़ाइल सामग्री:
फ़ाइल में लिखा गया डेटा
फ़ाइल सामग्री: CodeGym
फ़ाइल मौजूद है: असत्य

यह उदाहरण क्यों जरूरी था?

सीधे शब्दों में कहें, डेटा हमेशा बाइट्स का एक सेट होता है। यदि आपको डिस्क से/में बहुत अधिक डेटा पढ़ने/लिखने की आवश्यकता है, तो I/O समस्याओं के कारण आपका कोड धीरे-धीरे चलेगा। इस मामले में, रैम में वर्चुअल फ़ाइल सिस्टम को बनाए रखना समझ में आता है, इसके साथ उसी तरह काम करना जैसे आप एक पारंपरिक डिस्क के साथ करते हैं। और InputStream और OutputStream से सरल क्या हो सकता है ?

बेशक, यह निर्देश के लिए एक उदाहरण है, उत्पादन-तैयार कोड नहीं। इसका हिसाब नहीं है (निम्नलिखित सूची व्यापक नहीं है):

  • बहु सूत्रण
  • फ़ाइल आकार सीमा (चल रहे JVM के लिए उपलब्ध RAM की मात्रा)
  • पथ संरचना का सत्यापन
  • विधि तर्कों का सत्यापन

दिलचस्प अंदरूनी जानकारी:
CodeGym कार्य सत्यापन सर्वर कुछ इसी तरह के दृष्टिकोण का उपयोग करता है। हम एक आभासी एफएस को स्पिन करते हैं, यह निर्धारित करते हैं कि कार्य सत्यापन के लिए कौन से परीक्षण चलाने की आवश्यकता है, परीक्षण चलाएं और परिणाम पढ़ें।

निष्कर्ष और बड़ा सवाल

इस पाठ को पढ़ने के बाद बड़ा सवाल यह है कि "मैं बाइट [] का उपयोग क्यों नहीं कर सकता , क्योंकि यह अधिक सुविधाजनक है और प्रतिबंध नहीं लगाता है?"

ByteArrayInputStream का लाभ यह है कि यह दृढ़ता से इंगित करता है कि आप केवल-पढ़ने के लिए बाइट्स का उपयोग करने जा रहे हैं (क्योंकि स्ट्रीम अपनी सामग्री को बदलने के लिए इंटरफ़ेस प्रदान नहीं करती है)। उस ने कहा, यह ध्यान रखना महत्वपूर्ण है कि एक प्रोग्रामर अभी भी बाइट्स को सीधे एक्सेस कर सकता है ।

लेकिन अगर कभी-कभी आपके पास byte[] होता है, कभी-कभी आपके पास फ़ाइल होती है, कभी-कभी आपके पास नेटवर्क कनेक्शन होता है, और इसी तरह, आपको "बाइट्स की एक धारा" के लिए किसी प्रकार की अमूर्तता की आवश्यकता होगी, और मुझे परवाह नहीं है कि वे कहाँ हैं से आते हैं"। और यही InputStream है। जब स्रोत बाइट सरणी होता है, तो ByteArrayInputStream उपयोग करने के लिए एक अच्छा इनपुटस्ट्रीम होता है।

यह कई स्थितियों में मददगार है, लेकिन यहाँ दो विशिष्ट उदाहरण हैं:

  1. आप एक पुस्तकालय लिख रहे हैं जो बाइट्स प्राप्त करता है और उन्हें किसी तरह संसाधित करता है (उदाहरण के लिए, मान लीजिए कि यह छवि प्रसंस्करण उपयोगिताओं की लाइब्रेरी है)। आपकी लाइब्रेरी के उपयोगकर्ता किसी फ़ाइल से, इन-मेमोरी byte[] , या किसी अन्य स्रोत से बाइट प्रदान कर सकते हैं। तो आप एक इंटरफ़ेस प्रदान करते हैं जो इनपुटस्ट्रीम स्वीकार करता है , जिसका अर्थ है कि यदि उनके पास बाइट [] है, तो उन्हें इसे ByteArrayInputStream में लपेटने की आवश्यकता है ।

  2. आप कोड लिख रहे हैं जो नेटवर्क कनेक्शन को पढ़ता है। लेकिन इस कोड पर इकाई परीक्षण करने के लिए, आप एक कनेक्शन नहीं खोलना चाहते हैं - आप केवल कोड को कुछ बाइट फीड करना चाहते हैं। तो कोड एक इनपुटस्ट्रीम लेता है और आपका परीक्षण ByteArrayInputStream में पास होता है ।