سؤال

ما هي الاختلافات (إن وجدت) بين نهجين التخزين المؤقت التاليين؟

Reader r1 = new BufferedReader(new InputStreamReader(in, "UTF-8"), bufferSize);
Reader r2 = new InputStreamReader(new BufferedInputStream(in, bufferSize), "UTF-8");
هل كانت مفيدة؟

المحلول

r1 أكثر كفاءة. ال InputStreamReader نفسها لا تحتوي على مخزن مؤقت كبير. ال BufferedReader يمكن ضبطها على وجود مخزن مؤقت أكبر من InputStreamReader. ال InputStreamReader في r2 سيكون بمثابة عنق الزجاجة.

في الجوز: يجب عليك قراءة البيانات من خلال قمع ، وليس من خلال زجاجة.


تحديث: إليك برنامج قياسي صغير ، فقط نسخه. لا تحتاج إلى إعداد الملفات.

package com.stackoverflow.q3459127;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

public class Test {

    public static void main(String... args) throws Exception {

        // Init.
        int bufferSize = 10240; // 10KB.
        int fileSize = 100 * 1024 * 1024; // 100MB.
        File file = new File("/temp.txt");

        // Create file (it's also a good JVM warmup).
        System.out.print("Creating file .. ");
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(file));
            for (int i = 0; i < fileSize; i++) {
                writer.write("0");
            }
            System.out.printf("finished, file size: %d MB.%n", file.length() / 1024 / 1024);
        } finally {
            if (writer != null) try { writer.close(); } catch (IOException ignore) {}
        }

        // Read through funnel.
        System.out.print("Reading through funnel .. ");
        Reader r1 = null;        
        try {
            r1 = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"), bufferSize);
            long st = System.nanoTime();
            for (int data; (data = r1.read()) > -1;);
            long et = System.nanoTime();
            System.out.printf("finished in %d ms.%n", (et - st) / 1000000);
        } finally {
            if (r1 != null) try { r1.close(); } catch (IOException ignore) {}
        }

        // Read through bottle.
        System.out.print("Reading through bottle .. ");
        Reader r2 = null;        
        try {
            r2 = new InputStreamReader(new BufferedInputStream(new FileInputStream(file), bufferSize), "UTF-8");
            long st = System.nanoTime();
            for (int data; (data = r2.read()) > -1;);
            long et = System.nanoTime();
            System.out.printf("finished in %d ms.%n", (et - st) / 1000000);
        } finally {
            if (r2 != null) try { r2.close(); } catch (IOException ignore) {}
        }

        // Cleanup.
        if (!file.delete()) System.err.printf("Oops, failed to delete %s. Cleanup yourself.%n", file.getAbsolutePath());
    }

}

النتائج في خط العرض الخاص بي E5500 مع أ Seagate Momentus 7200.3 القرص الصلب:

Creating file .. finished, file size: 99 MB.
Reading through funnel .. finished in 1593 ms.
Reading through bottle .. finished in 7760 ms.

نصائح أخرى

r1 هو أيضًا أكثر ملاءمة عند قراءة الدفق القائم على الخط كما BufferedReader يدعم readLine طريقة. ليس عليك قراءة المحتوى في مخزن مؤقت صفيف char أو chars واحدًا تلو الآخر. ومع ذلك ، عليك أن تلقي r1 إلى BufferedReader أو استخدم هذا النوع بشكل صريح للمتغير.

غالبًا ما أستخدم مقتطف الرمز هذا:

BufferedReader br = ...
String line;
while((line=br.readLine())!=null) {
  //process line
}

رداً على سؤال روس ستودتمان في التعليق أعلاه (ولكن أيضًا ذا صلة بـ OP):

BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputSream(inputStream), "UTF-8"));

ال BufferedInputStream لا لزوم له (ومن المحتمل أن يضر الأداء بسبب النسخ الغريب). هذا لأن ال BufferedReader يطلب الأحرف من InputStreamReader في قطع كبيرة عن طريق الاتصال InputStreamReader.read(char[], int, int), والتي بدورها (من خلال StreamDecoder) المكالمات InputStream.read(byte[], int, int) لقراءة كتلة كبيرة من البايتات من الكامنة InputStream.

يمكنك إقناع نفسك بأن هذا هو ذلك عن طريق تشغيل الكود التالي:

new BufferedReader(new InputStreamReader(new ByteArrayInputStream("Hello world!".getBytes("UTF-8")) {

    @Override
    public synchronized int read() {
        System.err.println("ByteArrayInputStream.read()");
        return super.read();
    }

    @Override
    public synchronized int read(byte[] b, int off, int len) {
        System.err.println("ByteArrayInputStream.read(..., " + off + ", " + len + ')');
        return super.read(b, off, len);
    }

}, "UTF-8") {

    @Override
    public int read() throws IOException {
        System.err.println("InputStreamReader.read()");
        return super.read();
    }

    @Override
    public int read(char[] cbuf, int offset, int length) throws IOException {
        System.err.println("InputStreamReader.read(..., " + offset + ", " + length + ')');
        return super.read(cbuf, offset, length);
    }

}).read(); // read one character from the BufferedReader

سترى الإخراج التالي:

InputStreamReader.read(..., 0, 8192)
ByteArrayInputStream.read(..., 0, 8192)

هذا يدل على أن BufferedReader يطلب جزءًا كبيرًا من الشخصيات من InputStreamReader, ، والذي يطلب بدوره جزءًا كبيرًا من البايتات من الأساس InputStream.

fwiw ، إذا كنت تفتح ملف في Java 8 ، يمكنك استخدام files.newbufferedreader (المسار). لا أعرف كيف يقارن الأداء بالحلول الأخرى الموصوفة هنا ، ولكن على الأقل يدفع قرار ما يبنيه إلى العازلة في JDK.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top