definicion - import java io bufferedreader
CharBuffer vs. char (7)
Si esto es lo único que estás haciendo con el búfer, entonces la matriz es probablemente la mejor opción en esta instancia.
CharBuffer tiene mucho cromo adicional, pero nada de esto es relevante en este caso, y solo ralentizará las cosas una fracción.
Siempre puedes refactorizar más adelante si necesitas complicar las cosas.
¿Hay alguna razón para preferir un CharBuffer
a un char[]
en lo siguiente?
CharBuffer buf = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
while( in.read(buf) >= 0 ) {
out.append( buf.flip() );
buf.clear();
}
vs.
char[] buf = new char[DEFAULT_BUFFER_SIZE];
int n;
while( (n = in.read(buf)) >= 0 ) {
out.write( buf, 0, n );
}
(¿dónde está un Reader
y out
en un Writer
)?
Creo que CharBuffer y ByteBuffer (así como cualquier otro xBuffer) están pensados para su reutilización, por lo que puedes buf.clear () en lugar de recurrir a la reasignación cada vez que lo hagas.
Si no los reutilizas, no estás utilizando todo su potencial y agregará un gasto adicional. Sin embargo, si está planeando escalar esta función, esta podría ser una buena idea para mantenerlos allí.
La versión CharBuffer es un poco menos complicada (una variable menos), encapsula el manejo del tamaño del búfer y hace uso de una API estándar. En general, preferiría esto.
Sin embargo, todavía hay una buena razón para preferir la versión del arreglo, en algunos casos al menos. CharBuffer solo se introdujo en Java 1.4, por lo que si se está implementando en una versión anterior, no se puede usar Charbuffer (a menos que sea un role-your-own / use un backport).
PD: si usa un backport, recuerde eliminarlo una vez que haya alcanzado la versión que contiene la versión "real" del código backported.
No, realmente no hay ninguna razón para preferir un CharBuffer
en este caso.
En general, sin embargo, CharBuffer
(y ByteBuffer
) realmente pueden simplificar las API y fomentar el procesamiento correcto. Si estuviera diseñando una API pública, definitivamente vale la pena considerar una API orientada a búfer.
La diferencia, en la práctica, es en realidad <10%, no 30% como otros informan.
Para leer y escribir un archivo de 5MB 24 veces, mis números se toman usando un Profiler. Estaban en promedio:
char[] = 4139 ms
CharBuffer = 4466 ms
ByteBuffer = 938 (direct) ms
Pruebas individuales un par de veces favorecido CharBuffer.
También traté de reemplazar el IO basado en archivos con In-Memory IO y el rendimiento fue similar. Si está tratando de transferir de una transmisión nativa a otra, entonces es mejor usar un ByteBuffer "directo".
Con menos del 10% de diferencia de rendimiento, en la práctica, yo preferiría el CharBuffer. Su sintaxis es más clara, hay menos variables extrañas, y puedes hacer una manipulación más directa sobre ella (es decir, cualquier cosa que pida una secuencia de caracteres).
Benchmark está por debajo ... es un poco incorrecto ya que BufferedReader se asigna dentro del método de prueba en lugar de afuera ... sin embargo, el ejemplo siguiente le permite aislar el tiempo de IO y eliminar factores como una cadena o un flujo de bytes redimensionando su interno memoria intermedia, etc.
public static void main(String[] args) throws Exception {
File f = getBytes(5000000);
System.out.println(f.getAbsolutePath());
try {
System.gc();
List<Main> impls = new java.util.ArrayList<Main>();
impls.add(new CharArrayImpl());
//impls.add(new CharArrayNoBuffImpl());
impls.add(new CharBufferImpl());
//impls.add(new CharBufferNoBuffImpl());
impls.add(new ByteBufferDirectImpl());
//impls.add(new CharBufferDirectImpl());
for (int i = 0; i < 25; i++) {
for (Main impl : impls) {
test(f, impl);
}
System.out.println("-----");
if(i==0)
continue; //reset profiler
}
System.gc();
System.out.println("Finished");
return;
} finally {
f.delete();
}
}
static int BUFFER_SIZE = 1000;
static File getBytes(int size) throws IOException {
File f = File.createTempFile("input", ".txt");
FileWriter writer = new FileWriter(f);
Random r = new Random();
for (int i = 0; i < size; i++) {
writer.write(Integer.toString(5));
}
writer.close();
return f;
}
static void test(File f, Main impl) throws IOException {
InputStream in = new FileInputStream(f);
File fout = File.createTempFile("output", ".txt");
try {
OutputStream out = new FileOutputStream(fout, false);
try {
long start = System.currentTimeMillis();
impl.runTest(in, out);
long end = System.currentTimeMillis();
System.out.println(impl.getClass().getName() + " = " + (end - start) + "ms");
} finally {
out.close();
}
} finally {
fout.delete();
in.close();
}
}
public abstract void runTest(InputStream ins, OutputStream outs) throws IOException;
public static class CharArrayImpl extends Main {
char[] buff = new char[BUFFER_SIZE];
public void runTest(InputStream ins, OutputStream outs) throws IOException {
Reader in = new BufferedReader(new InputStreamReader(ins));
Writer out = new BufferedWriter(new OutputStreamWriter(outs));
int n;
while ((n = in.read(buff)) >= 0) {
out.write(buff, 0, n);
}
}
}
public static class CharBufferImpl extends Main {
CharBuffer buff = CharBuffer.allocate(BUFFER_SIZE);
public void runTest(InputStream ins, OutputStream outs) throws IOException {
Reader in = new BufferedReader(new InputStreamReader(ins));
Writer out = new BufferedWriter(new OutputStreamWriter(outs));
int n;
while ((n = in.read(buff)) >= 0) {
buff.flip();
out.append(buff);
buff.clear();
}
}
}
public static class ByteBufferDirectImpl extends Main {
ByteBuffer buff = ByteBuffer.allocateDirect(BUFFER_SIZE * 2);
public void runTest(InputStream ins, OutputStream outs) throws IOException {
ReadableByteChannel in = Channels.newChannel(ins);
WritableByteChannel out = Channels.newChannel(outs);
int n;
while ((n = in.read(buff)) >= 0) {
buff.flip();
out.write(buff);
buff.clear();
}
}
}
Quería mini-benchmark esta comparación.
A continuación está la clase que he escrito.
La cuestión es que no puedo creer que el CharBuffer haya funcionado tan mal. ¿Qué tengo mal?
EDITAR: Desde el 11 ° comentario a continuación, he editado el código y el tiempo de salida, mejor rendimiento en general, pero todavía hay una diferencia significativa en los tiempos. También probé la opción out2.append ((CharBuffer) buff.flip ()) mencionada en los comentarios, pero fue mucho más lenta que la opción de escritura utilizada en el siguiente código.
Resultados: (tiempo en ms)
char []: 3411
CharBuffer: 5653
public class CharBufferScratchBox
{
public static void main(String[] args) throws Exception
{
// Some Setup Stuff
String smallString =
"1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000";
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
stringBuilder.append(smallString);
}
String string = stringBuilder.toString();
int DEFAULT_BUFFER_SIZE = 1000;
int ITTERATIONS = 10000;
// char[]
StringReader in1 = null;
StringWriter out1 = null;
Date start = new Date();
for (int i = 0; i < ITTERATIONS; i++)
{
in1 = new StringReader(string);
out1 = new StringWriter(string.length());
char[] buf = new char[DEFAULT_BUFFER_SIZE];
int n;
while ((n = in1.read(buf)) >= 0)
{
out1.write(
buf,
0,
n);
}
}
Date done = new Date();
System.out.println("char[] : " + (done.getTime() - start.getTime()));
// CharBuffer
StringReader in2 = null;
StringWriter out2 = null;
start = new Date();
CharBuffer buff = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
for (int i = 0; i < ITTERATIONS; i++)
{
in2 = new StringReader(string);
out2 = new StringWriter(string.length());
int n;
while ((n = in2.read(buff)) >= 0)
{
out2.write(
buff.array(),
0,
n);
buff.clear();
}
}
done = new Date();
System.out.println("CharBuffer: " + (done.getTime() - start.getTime()));
}
}
Deberías evitar CharBuffer
en las últimas versiones de Java, hay un error en #subsequence()
. No se puede obtener una subsecuencia de la segunda mitad del búfer ya que la implementación confunde la capacity
y el remaining
. Observé el error en Java 6-0-11 y 6-0-12.