Подтвердить что ты не робот

CharBuffer против char []

Есть ли причина предпочесть a CharBuffer для char[] в следующем:

CharBuffer buf = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
while( in.read(buf) >= 0 ) {
  out.append( buf.flip() );
  buf.clear();
}

против.

char[] buf = new char[DEFAULT_BUFFER_SIZE];
int n;
while( (n = in.read(buf)) >= 0 ) {
  out.write( buf, 0, n );
}

(где in есть Reader и out в Writer)?

4b9b3361

Ответ 1

Нет, в этом случае нет причин предпочитать a CharBuffer.

В общем случае CharBufferByteBuffer) может действительно упростить API и способствовать правильной обработке. Если вы разрабатываете публичный API, то определенно стоит рассмотреть буферный API.

Ответ 2

Я хотел бы сравнить это сравнение.

Ниже приведен класс, который я написал.

Вещь в том, что я не могу поверить в то, что CharBuffer сделал так плохо. Что я не понял?

EDIT: после 11-го комментария ниже я отредактировал код и время вывода, улучшая производительность, но все же значительную разницу во времени. Я также попробовал вариант out2.append((CharBuffer) buff.flip()), упомянутый в комментариях, но он был намного медленнее, чем опция записи, используемая в приведенном ниже коде.

Результаты: (время в мс)
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()));
    }
}

Ответ 3

Если это единственное, что вы делаете с буфером, то этот массив, вероятно, лучший выбор в этом случае.

В CharBuffer есть много дополнительных хром, но в этом случае это не имеет значения, и это замедлит процесс.

Вы всегда можете реорганизовать позже, если вам нужно усложнить ситуацию.

Ответ 4

Разница на практике составляет 10%, а не 30%, как сообщают другие.

Чтобы прочитать и записать файл 5 МБ 24 раза, мои номера были сделаны с использованием Profiler. Они были в среднем:

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

Индивидуальные тесты пару раз благоприятствовали CharBuffer.

Я также попытался заменить IO на основе файлов In-Memory IO, и производительность была аналогичной. Если вы пытаетесь перевести из одного родного потока в другой, вам лучше использовать "прямой" ByteBuffer.

С разницей в производительности менее 10% на практике я бы предпочел CharBuffer. Синтаксис более ясен, есть меньше посторонних переменных, и вы можете делать больше прямых манипуляций с ним (т.е. Все, что запрашивает CharSequence).

Контрольный показатель ниже... он немного ошибочен, поскольку BufferedReader выделяется внутри тестового метода, а не снаружи... однако приведенный ниже пример позволяет вам изолировать время ввода-вывода и исключать факторы, такие как поток строк или байтов изменение размера внутреннего буфера памяти и т.д.

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

Ответ 5

Я думаю, что CharBuffer и ByteBuffer (как и любой другой xBuffer) предназначены для повторного использования, поэтому вы можете buf.clear() вместо того, чтобы каждый раз перераспределять ресурсы

Если вы не используете их повторно, вы не используете их полный потенциал, и это добавит дополнительные накладные расходы. Однако, если вы планируете масштабировать эту функцию, это может быть хорошей идеей держать их там.

Ответ 6

Версия CharBuffer немного менее сложна (одна менее переменная), инкапсулирует обработку размера буфера и использует стандартный API. Обычно я бы предпочел это.

Однако есть еще одна веская причина предпочесть версию массива, в некоторых случаях, по крайней мере. CharBuffer был представлен только в Java 1.4, поэтому, если вы используете более раннюю версию, вы не можете использовать Charbuffer (если только вы не используете роль или используете backport).

P.S Если вы используете backport, помните, чтобы удалить его, как только вы догоните версию, содержащую "реальную" версию кода с обратным кодом.

Ответ 7

Вам следует избегать CharBuffer в последних версиях Java, есть ошибка в #subsequence(). Вы не можете получить подпоследовательность со второй половины буфера, поскольку реализация смущает capacity и remaining. Я заметил ошибку в java 6-0-11 и 6-0-12.