هل يجب أن أقوم بتخزين inputstream أو inputstreamreader؟
-
27-09-2019 - |
سؤال
ما هي الاختلافات (إن وجدت) بين نهجين التخزين المؤقت التاليين؟
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.