Frage

Gibt es einen Grund, eine CharBuffer zu einem char[] im folgenden bevorzugen:

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

(wobei in ist ein Reader und out in einem Writer)?

War es hilfreich?

Lösung

Nein, es gibt wirklich keinen Grund, einen CharBuffer in diesem Fall zu bevorzugen.

In der Regel aber CharBuffer (und ByteBuffer) können wirklich APIs vereinfachen und die korrekte Verarbeitung fördern. Wenn Sie eine öffentliche API entwarfen, es ist auf jeden Fall lohnt sich ein Pufferorientierte API berücksichtigen.

Andere Tipps

ich Mini-Benchmark wollte diesen Vergleich.

Im Folgenden ist die Klasse, habe ich geschrieben.

Die Sache ist, ich kann nicht glauben, dass die CharBuffer so schlecht ausgeführt. Was habe ich falsch gemacht?

EDIT: Seit dem 11. Kommentar unten Ich habe den Code und die Ausgabezeit, eine bessere Leistung rundum, aber immer noch einen signifikanten Unterschied in Zeiten bearbeitet. Ich habe auch versucht out2.append ((CharBuffer) buff.flip ()) Option in den Kommentaren erwähnt, aber es war viel langsamer als die Schreib Option im Code unten.

Ergebnisse: (Zeit in 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()));
    }
}

Wenn dies das einzige, was Sie mit dem Puffer zu tun, dann ist das Array ist wahrscheinlich die bessere Wahl in diesem Fall.

CharBuffer hat viele zusätzliche Chrom auf, aber nichts davon ist in diesem Fall relevant - und wird nur langsam Dinge nach unten einen Bruchteil

.

Sie können jederzeit später Refactoring, wenn Sie Dinge machen müssen komplizierter.

Der Unterschied in der Praxis ist eigentlich <10%, 30%, wie andere berichtet.

Um eine 5 MB-Datei 24 Mal, meine Zahlen lesen und schreiben einen Profiler genommen werden. Sie waren im Durchschnitt:

char[] = 4139 ms
CharBuffer = 4466 ms
ByteBuffer = 938 (direct) ms

Einzeltests ein paar Mal begünstigt CharBuffer.

Ich habe auch versucht, die Datei-basierten IO mit In-Memory-IO ersetzt und die Performance war ähnlich. Wenn Sie von einem nativen Stream in einem anderen zu übertragen versuchen, dann sind Sie besser dran mit einem „direkten“ ByteBuffer.

Mit weniger als 10% Unterschied in der Leistung, in der Praxis würde ich die CharBuffer begünstigt. Es ist Syntax ist klarer, gibt es weniger Störvariablen, und Sie können mehr direkte Manipulation tun auf sie (das heißt alles, was für eine CharSequence fragt).

Benchmark unten ist ... es ist etwas falsch, wie die BufferedReader in der Testmethode zugeordnet ist und nicht außerhalb ... ermöglicht jedoch das folgende Beispiel Ihnen die IO Zeit zu isolieren und Faktoren wie ein String oder Byte-Strom zu beseitigen seine internen Speicherpuffer Ändern der Größe, 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();
        }
    }
}

Ich denke, dass CharBuffer und ByteBuffer (sowie andere xBuffer) für Wiederverwertbarkeit gedacht waren, so können Sie buf.clear (), um sie statt jedes Mal durch Umschichtung gehen

Wenn Sie nicht wiederverwenden, die Sie verwenden nicht ihr volles Potenzial und es wird zusätzlichen Aufwand hinzufügen. Allerdings, wenn Sie auf die Skalierung dieser Funktion planen dies könnte eine gute Idee sein, sie dort zu halten

Die CharBuffer Version ist etwas weniger kompliziert (eine weniger variable), kapselt Handhabung Puffergröße und nutzt einen Standard-API. Generell würde ich das vorziehen.

Allerdings gibt es immer noch einen guten Grund, die Array-Version, in manchen Fällen zumindest zu bevorzugen. CharBuffer wurde nur in Java eingeführt 1.4 also, wenn Sie zu einer früheren Version Sie bereitstellen können nicht verwenden CharBuffer (es sei denn, Sie Rolle-your-own / verwenden, um einen Backport).

P. S Wenn Sie einen Backport vergessen, es zu entfernen, sobald Sie auf die Version enthält, die „echten“ Version des zurückportiert Code aufholen.

Sie sollten CharBuffer in den letzten Java-Versionen vermeiden, gibt es einen Fehler in #subsequence(). Sie können keine Teilfolge aus der zweiten Hälfte des Puffers erhalten, da die Umsetzung capacity und remaining verwirrt. Ich beobachtete den Fehler in Java 6-0-11 und 6-0-12.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top