Java IO इतका वाईट का आहे?

IO (इनपुट आणि आउटपुट) API हे Java API आहे जे विकासकांना प्रवाहांसह कार्य करणे सोपे करते. समजा आम्हाला काही डेटा प्राप्त झाला आहे (उदाहरणार्थ, नाव, मधले नाव, आडनाव) आणि आम्हाला ते एका फाईलमध्ये लिहावे लागेल — java.io वापरण्याची वेळ आली आहे .

java.io लायब्ररीची रचना

परंतु Java IO चे तोटे आहेत, म्हणून त्या प्रत्येकाबद्दल बोलूया:

  1. इनपुट/आउटपुटसाठी प्रवेश अवरोधित करणे. समस्या अशी आहे की जेव्हा डेव्हलपर Java IO वापरून फाइलवर काहीतरी वाचण्याचा किंवा लिहिण्याचा प्रयत्न करतो , तेव्हा ते फाइल लॉक करते आणि कार्य पूर्ण होईपर्यंत त्यात प्रवेश अवरोधित करते.
  2. व्हर्च्युअल फाइल सिस्टमसाठी कोणतेही समर्थन नाही.
  3. दुव्यांसाठी समर्थन नाही.
  4. बरेच आणि चेक केलेले अपवाद बरेच.

फाइल्ससह कार्य करताना नेहमी अपवादांसह कार्य करणे आवश्यक आहे: उदाहरणार्थ, आधीपासून अस्तित्वात असलेली नवीन फाइल तयार करण्याचा प्रयत्न केल्यास IOException टाकले जाईल . या प्रकरणात, अनुप्रयोग चालू ठेवावा आणि वापरकर्त्याला सूचित केले पाहिजे की फाइल का तयार केली जाऊ शकली नाही.


try {
	File.createTempFile("prefix", "");
} catch (IOException e) {
	// Handle the IOException
}

/**
 * Creates an empty file in the default temporary-file directory 
 * any exceptions will be ignored. This is typically used in finally blocks. 
 * @param prefix 
 * @param suffix 
 * @throws IOException - If a file could not be created
 */
public static File createTempFile(String prefix, String suffix) 
throws IOException {
...
}

येथे आपण पाहतो की जेव्हा फाइल तयार करता येत नाही तेव्हा createTempFile पद्धत IOException टाकते . हा अपवाद योग्यरित्या हाताळला गेला पाहिजे. जर आपण या पद्धतीला ट्राय-कॅच ब्लॉकच्या बाहेर कॉल करण्याचा प्रयत्न केला, तर कंपायलर एक त्रुटी निर्माण करेल आणि त्याचे निराकरण करण्यासाठी दोन पर्याय सुचवेल: पद्धत ट्राय -कॅच ब्लॉकमध्ये गुंडाळा किंवा File.createTempFile ला IOException टाका ( त्यामुळे ते उच्च पातळीवर हाताळले जाऊ शकते).

Java NIO वर पोहोचणे आणि ते Java IO शी कसे तुलना करते

Java NIO , किंवा Java नॉन-ब्लॉकिंग I/O (किंवा कधीकधी Java New I/O) उच्च-कार्यक्षमता I/O ऑपरेशन्ससाठी डिझाइन केलेले आहे.

Java IO पद्धती आणि त्या बदलणाऱ्या पद्धतींची तुलना करूया .

प्रथम, Java IO सह काम करण्याबद्दल बोलूया :

इनपुटस्ट्रीम वर्ग


try(FileInputStream fin = new FileInputStream("C:/codegym/file.txt")){
    System.out.printf("File size: %d bytes \n", fin.available());
    int i=-1;
    while((i=fin.read())!=-1) {
        System.out.print((char)i);
    }   
} catch(IOException ex) {
    System.out.println(ex.getMessage());
}

FileInputStream वर्ग हा फाईलमधील डेटा वाचण्यासाठी आहे. हे InputStream क्लास इनहेरिट करते आणि त्यामुळे त्याच्या सर्व पद्धती लागू करते. जर फाइल उघडली जाऊ शकत नसेल, तर FileNotFoundException टाकला जाईल.

आउटपुटस्ट्रीम वर्ग


String text = "Hello world!"; // String to write
try(FileOutputStream fos = new FileOutputStream("C:/codegym/file.txt")){
    // Convert our string into bytes
    byte[] buffer = text.getBytes();
    fos.write(buffer, 0, buffer.length);
    System.out.println("The file has been written");
} catch(IOException ex) {
    System.out.println(ex.getMessage());
}

फाइलवर बाइट्स लिहिण्यासाठी FileOutputStream वर्ग . हे आउटपुटस्ट्रीम क्लासमधून आले आहे.

वाचक आणि लेखक वर्ग

FileReader क्लास स्ट्रीममधील कॅरेक्टर डेटा वाचू देतो आणि FileWriter क्लास कॅरेक्टर स्ट्रीम लिहिण्यासाठी वापरला जातो . फाइलमधून कसे लिहायचे आणि कसे वाचायचे ते खालील कोड दाखवते:


        String fileName = "c:/codegym/Example.txt";

        // Create a FileWriter object
        try (FileWriter writer = new FileWriter(fileName)) {

            // Write content to file
            writer.write("This is a simple example\nin which we\nwrite to a file\nand read from a file\n");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Create a FileReader object
        try (FileReader fr = new FileReader(fileName)) {
            char[] a = new char[200]; // Number of characters to read
            fr.read(a); // Read content into an array
            for (char c : a) {
                System.out.print(c); // Display characters one by one
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

आता जावा NIO बद्दल बोलूया :

चॅनल

Java IO मध्ये वापरल्या जाणार्‍या स्ट्रीम्सच्या विपरीत , चॅनेल हा द्वि-मार्गी इंटरफेस आहे, म्हणजेच तो वाचू आणि लिहू शकतो. Java NIO चॅनेल ब्लॉकिंग आणि नॉन-ब्लॉकिंग मोडमध्ये असिंक्रोनस डेटा फ्लोला समर्थन देते.


RandomAccessFile aFile = new RandomAccessFile("C:/codegym/file.txt", "rw");
FileChannel inChannel = aFile.getChannel();

ByteBuffer buf = ByteBuffer.allocate(100);
int bytesRead = inChannel.read(buf);

while (bytesRead != -1) {
  System.out.println("Read: " + bytesRead);
  buf.flip();
	  while(buf.hasRemaining()) {
	      System.out.print((char) buf.get());
	  }
  buf.clear();
  bytesRead = inChannel.read(buf);
}
aFile.close();

येथे आम्ही FileChannel वापरले . फाईलमधील डेटा वाचण्यासाठी आम्ही फाइल चॅनेल वापरतो. फाईल चॅनेल ऑब्जेक्ट फक्त फाईल ऑब्जेक्टवर getChannel() पद्धत कॉल करून तयार केले जाऊ शकते — फाइल चॅनेल ऑब्जेक्ट थेट तयार करण्याचा कोणताही मार्ग नाही.

FileChannel व्यतिरिक्त , आमच्याकडे इतर चॅनेल अंमलबजावणी आहेत:

  • फाइल चॅनेल - फाइल्ससह कार्य करण्यासाठी

  • DatagramChannel — UDP कनेक्शनवर काम करण्यासाठी एक चॅनेल

  • SocketChannel — TCP कनेक्शनवर काम करण्यासाठी एक चॅनेल

  • सर्व्हरसॉकेट चॅनेलमध्ये सॉकेट चॅनेल असतेआणि ते वेब सर्व्हर कसे कार्य करते यासारखेच असते

कृपया लक्षात ठेवा: FileChannel नॉन-ब्लॉकिंग मोडवर स्विच केले जाऊ शकत नाही. Java NIO चा नॉन-ब्लॉकिंग मोड तुम्हाला चॅनेलवरून डेटा वाचण्याची विनंती करू देतो आणि सध्या जे उपलब्ध आहे तेच मिळवू देतो (किंवा अद्याप कोणताही डेटा उपलब्ध नसल्यास काहीही नाही). ते म्हणाले, SelectableChannel आणि त्याची अंमलबजावणी कनेक्ट() पद्धत वापरून नॉन-ब्लॉकिंग मोडमध्ये ठेवली जाऊ शकते .

निवडकर्ता

Java NIO ने थ्रेड तयार करण्याची क्षमता सादर केली ज्याला माहित आहे की कोणते चॅनेल डेटा लिहिण्यास आणि वाचण्यासाठी तयार आहे आणि त्या विशिष्ट चॅनेलवर प्रक्रिया करू शकते. ही क्षमता निवडकर्ता वर्ग वापरून लागू केली जाते.

निवडकर्त्याशी चॅनेल कनेक्ट करत आहे


Selector selector = Selector.open();
channel.configureBlocking(false); // Non-blocking mode
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);

म्हणून आम्ही आमचा सिलेक्टर तयार करतो आणि तो निवडण्यायोग्य चॅनेलशी कनेक्ट करतो .

निवडकर्त्यासह वापरण्यासाठी, चॅनेल नॉन-ब्लॉकिंग मोडमध्ये असणे आवश्यक आहे. याचा अर्थ असा की तुम्ही निवडकासह FileChannel वापरू शकत नाही , कारण FileChannel नॉन-ब्लॉकिंग मोडमध्ये ठेवता येत नाही. परंतु सॉकेट चॅनेल चांगले कार्य करतील.

येथे नमूद करूया की आमच्या उदाहरणात SelectionKey ऑपरेशन्सचा एक संच आहे जो चॅनेलवर करता येतो. निवड की आम्हाला चॅनेलची स्थिती कळू देते.

SelectionKey चे प्रकार

  • SelectionKey.OP_CONNECT हे चॅनेल सूचित करते जे सर्व्हरशी कनेक्ट होण्यासाठी तयार आहे.

  • SelectionKey.OP_ACCEPT एक चॅनेल आहे जे येणारे कनेक्शन स्वीकारण्यासाठी तयार आहे.

  • SelectionKey.OP_READ डेटा वाचण्यासाठी तयार असलेले चॅनेल सूचित करते.

  • SelectionKey.OP_WRITE डेटा लिहिण्यासाठी तयार असलेले चॅनेल सूचित करते.

बफर

पुढील प्रक्रियेसाठी डेटा बफरमध्ये वाचला जातो. डेव्हलपर बफरवर मागे-पुढे जाऊ शकतो, जे डेटावर प्रक्रिया करताना आम्हाला थोडी अधिक लवचिकता देते. त्याच वेळी, बफरमध्ये योग्य प्रक्रियेसाठी आवश्यक डेटा आहे की नाही हे तपासणे आवश्यक आहे. तसेच, बफरमध्‍ये डेटा वाचताना, आपण अद्याप प्रक्रिया न केलेला विद्यमान डेटा नष्ट करणार नाही याची खात्री करा.


ByteBuffer buf = ByteBuffer.allocate (2048); 
int bytesRead = channel.read(buf);
buf.flip(); // Change to read mode
while (buf.hasRemaining()) { 
	byte data = buf.get(); // There are methods for primitives 
}

buf.clear(); // Clear the buffer - now it can be reused

बफरचे मूलभूत गुणधर्म:

मूलभूत गुणधर्म
क्षमता बफर आकार, जो अॅरेची लांबी आहे.
स्थिती डेटासह कार्य करण्यासाठी प्रारंभिक स्थिती.
मर्यादा ऑपरेटिंग मर्यादा. वाचन ऑपरेशन्ससाठी, मर्यादा ही वाचता येण्याजोग्या डेटाची मात्रा आहे, परंतु लेखन ऑपरेशन्ससाठी, ती क्षमता किंवा लेखनासाठी उपलब्ध कोटा आहे.
चिन्ह जेव्हा reset() पद्धत कॉल केली जाते तेव्हा स्थिती पॅरामीटर रीसेट केले जाईल त्या मूल्याची अनुक्रमणिका .

आता Java NIO.2 मध्ये नवीन काय आहे याबद्दल थोडे बोलूया .

मार्ग

पाथ फाईल सिस्टीममधील पथ दर्शवतो. त्यात फाईलचे नाव आणि डिरेक्ट्रीजची सूची आहे जी त्याचा मार्ग परिभाषित करते.


Path relative = Paths.get("Main.java");
System.out.println("File: " + relative);
// Get the file system
System.out.println(relative.getFileSystem());

पाथ्स हा एकल स्टॅटिक पद्धतीसह अतिशय सोपा वर्ग आहे: get() . हे केवळ पास केलेल्या स्ट्रिंग किंवा URI वरून पाथ ऑब्जेक्ट मिळवण्यासाठी तयार केले होते


Path path = Paths.get("c:\\data\\file.txt");

फाईल्स

फाइल्स हा एक उपयुक्तता वर्ग आहे जो आम्हाला थेट फाइलचा आकार, फाइल कॉपी आणि बरेच काही मिळवू देतो.


Path path = Paths.get("files/file.txt");
boolean pathExists = Files.exists(path);

फाइलसिस्टम

फाइल सिस्टम फाइल सिस्टमला इंटरफेस प्रदान करते. फाइलसिस्टम विविध वस्तू तयार करण्यासाठी कारखान्याप्रमाणे कार्य करते (मार्ग,पाथमॅचर,फाईल्स). हे आम्हाला फाइल सिस्टममधील फाइल्स आणि इतर ऑब्जेक्ट्समध्ये प्रवेश करण्यास मदत करते.


try {
      FileSystem filesystem = FileSystems.getDefault();
      for (Path rootdir : filesystem.getRootDirectories()) {
          System.out.println(rootdir.toString());
      }
  } catch (Exception e) {
      e.printStackTrace();
  }

कार्यक्षमता चाचणी

या चाचणीसाठी, दोन फाईल्स घेऊ. पहिली एक लहान मजकूर फाईल आहे आणि दुसरी मोठी व्हिडिओ आहे.

आम्ही एक फाइल तयार करू आणि काही शब्द आणि वर्ण जोडू:

% टच text.txt

आमची फाइल मेमरीमध्ये एकूण 42 बाइट्स व्यापते:

आता एक कोड लिहू जे आपली फाईल एका फोल्डरमधून दुसऱ्या फोल्डरमध्ये कॉपी करेल. IO आणि NIO आणि NIO.2 च्या गतीची तुलना करण्यासाठी लहान आणि मोठ्या फाईल्सवर त्याची चाचणी करूया .

कॉपी करण्यासाठी कोड, Java IO वापरून लिहिलेला :


public static void main(String[] args) {
        long currentMills = System.currentTimeMillis();
        long startMills = currentMills;
        File src = new File("/Users/IdeaProjects/testFolder/text.txt");
        File dst = new File("/Users/IdeaProjects/testFolder/text1.txt");
        copyFileByIO(src, dst);
        currentMills = System.currentTimeMillis();
        System.out.println("Execution time in milliseconds: " + (currentMills - startMills));
    }

    public static void copyFileByIO(File src, File dst) {
        try(InputStream inputStream = new FileInputStream(src);
            OutputStream outputStream = new FileOutputStream(dst)){

            byte[] buffer = new byte[1024];
            int length;
            // Read data into a byte array and then output to an OutputStream
            while((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

आणि येथे Java NIO साठी कोड आहे :


public static void main(String[] args) {
        long currentMills = System.currentTimeMillis();
        long startMills = currentMills;

        File src = new File("/Users/IdeaProjects/testFolder/text.txt");
        File dst = new File("/Users/IdeaProjects/testFolder/text2.txt");
        // Code for copying using NIO
        copyFileByChannel(src, dst);
        currentMills = System.currentTimeMillis();
        System.out.println("Execution time in milliseconds: " + (currentMills - startMills));
    }

    public static void copyFileByChannel(File src, File dst) {
        // 1. Get a FileChannel for the source file and the target file
        try(FileChannel srcFileChannel  = new FileInputStream(src).getChannel();
            FileChannel dstFileChannel = new FileOutputStream(dst).getChannel()){
            // 2. Size of the current FileChannel
            long count = srcFileChannel.size();
            while(count > 0) {
                /**=============================================================
                 * 3. Write bytes from the source file's FileChannel to the target FileChannel
                 * 1. srcFileChannel.position(): the starting position in the source file, cannot be negative
                 * 2. count: the maximum number of bytes transferred, cannot be negative
                 * 3. dstFileChannel: the target file
                 *==============================================================*/
                long transferred = srcFileChannel.transferTo(srcFileChannel.position(),
                        count, dstFileChannel);
                // 4. After the transfer is complete, change the position of the original file to the new one
                srcFileChannel.position(srcFileChannel.position() + transferred);
                // 5. Calculate how many bytes are left to transfer
                count -= transferred;
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Java NIO.2 साठी कोड :


public static void main(String[] args) {
  long currentMills = System.currentTimeMillis();
  long startMills = currentMills;

  Path sourceDirectory = Paths.get("/Users/IdeaProjects/testFolder/test.txt");
  Path targetDirectory = Paths.get("/Users/IdeaProjects/testFolder/test3.txt");
  Files.copy(sourceDirectory, targetDirectory);

  currentMills = System.currentTimeMillis();
  System.out.println("Execution time in milliseconds: " + (currentMills - startMills));
}

चला छोट्या फाईलने सुरुवात करूया.

Java IO साठी अंमलबजावणीची वेळ सरासरी 1 मिलीसेकंद होती. चाचणी अनेक वेळा चालवून, आम्हाला 0 ते 2 मिलीसेकंदपर्यंत परिणाम मिळतात.

अंमलबजावणीची वेळ मिलीसेकंदांमध्ये: १

Java NIO साठी अंमलबजावणीची वेळ जास्त आहे. सरासरी वेळ 11 मिलीसेकंद आहे. परिणाम 9 ते 16 पर्यंत होते. याचे कारण Java IO आमच्या ऑपरेटिंग सिस्टमपेक्षा वेगळ्या पद्धतीने कार्य करते. IO एकामागून एक फाइल्स हलवते आणि प्रक्रिया करते, परंतु ऑपरेटिंग सिस्टम डेटा एका मोठ्या भागामध्ये पाठवते. NIO ने खराब कामगिरी केली कारण ती बफर-ओरिएंटेड आहे, IO सारखी प्रवाह-केंद्रित नाही .

अंमलबजावणीची वेळ मिलीसेकंदांमध्ये: १२

आणि Java NIO.2 साठी आमची चाचणी देखील करूया . NIO.2 ने Java NIO च्या तुलनेत फाइल व्यवस्थापन सुधारले आहे . म्हणूनच अद्ययावत लायब्ररी असे भिन्न परिणाम देते:

अंमलबजावणीची वेळ मिलीसेकंदांमध्ये: 3

आता आमच्या मोठ्या फाईलची, 521 MB व्हिडिओची चाचणी करण्याचा प्रयत्न करूया. कार्य अगदी समान असेल: फाईल दुसर्या फोल्डरमध्ये कॉपी करा. दिसत!

Java IO साठी परिणाम :

मिलिसेकंदांमध्ये अंमलबजावणी वेळ: 1866

आणि येथे Java NIO साठी निकाल आहे :

अंमलबजावणीची वेळ मिलीसेकंदांमध्ये: 205

Java NIO ने पहिल्या चाचणीत 9 पट वेगाने फाईल हाताळली. पुनरावृत्ती झालेल्या चाचण्यांनी अंदाजे समान परिणाम दर्शवले.

आणि आम्ही जावा NIO.2 वर आमची चाचणी देखील पाहू :

मिलिसेकंदांमध्ये अंमलबजावणी वेळ: 360

हा निकाल का? फक्त कारण ते भिन्न उद्देश पूर्ण करत असल्यामुळे त्यांच्यामधील कामगिरीची तुलना करण्यात आम्हाला फारसा अर्थ नाही. NIO अधिक अमूर्त निम्न-स्तरीय I/O आहे, तर NIO.2 फाइल व्यवस्थापनाकडे केंद्रित आहे.

सारांश

आम्ही सुरक्षितपणे म्हणू शकतो की ब्लॉक्सच्या आत वापरल्याबद्दल धन्यवाद फाइल्ससह काम करताना Java NIO लक्षणीयरित्या अधिक कार्यक्षम आहे. आणखी एक प्लस म्हणजे NIO लायब्ररी दोन भागांमध्ये विभागली गेली आहे: एक फाइल्ससह काम करण्यासाठी, दुसरा नेटवर्कसह कार्य करण्यासाठी.

फाइल्ससह काम करण्यासाठी Java NIO.2 चे नवीन API अनेक उपयुक्त वैशिष्ट्ये ऑफर करते:

  • पाथ वापरून अधिक उपयुक्त फाइल सिस्टीम अॅड्रेसिंग ,

  • सानुकूल फाइल सिस्टम प्रदाता वापरून झिप फायली हाताळण्यात लक्षणीय सुधारणा,

  • विशेष फाइल विशेषतांमध्ये प्रवेश,

  • अनेक सोयीस्कर पद्धती, जसे की एकाच विधानासह संपूर्ण फाइल वाचणे, एकाच विधानासह फाइल कॉपी करणे इ.

हे सर्व फाइल्स आणि फाइल सिस्टम्सबद्दल आहे आणि हे सर्व खूप उच्च पातळीचे आहे.

आजची वस्तुस्थिती अशी आहे की जावा एनआयओचा फायलींसोबत सुमारे 80-90% काम आहे, जरी Java IO चा वाटा अजूनही लक्षणीय आहे.

💡 PS या चाचण्या MacBook Pro 14" 16/512 वर चालवल्या गेल्या. ऑपरेटिंग सिस्टम आणि वर्कस्टेशन वैशिष्ट्यांवर आधारित चाचणीचे परिणाम भिन्न असू शकतात.