Нет, нет действительно никакой причины предпочесть CharBuffer
в этом случае.
В целом, тем не менее, CharBuffer
(и ByteBuffer
) может действительно упростить API и поощрить корректную обработку. При разработке общедоступного API это определенно достойно рассмотрения ориентированный на буфер API.
Я хотел мини-сравнить этого сравнения.
Ниже класс, который я записал.
вещь, я не могу полагать что CharBuffer, выполненный так плохо. Что я понял превратно?
РЕДАКТИРОВАНИЕ: Начиная с 11-го комментария ниже я отредактировал код и выходное время, лучшая производительность повсюду вокруг, но все еще значительная разница во времена. Я также попробовал out2.append ((CharBuffer)buff.flip ()) опция, упомянутая в комментариях, но это было намного медленнее, чем опция записи, используемая в коде ниже.
Результаты: (время в мс)
символ []: 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()));
}
}
Если это - единственная вещь, Вы делаете с буфером, то массив является, вероятно, лучшим выбором в этом экземпляре.
CharBuffer имеет много дополнительного хрома на нем, но ни один из него не релевантен в этом случае - и только замедлит вещи вниз часть.
можно всегда осуществить рефакторинг позже, если необходимо сделать вещи более сложными.
Различие, на практике, на самом деле < 10%, не 30%, поскольку другие сообщают.
, Чтобы читать и записать файлу 24 5 МБ времена, мои числа, взятые с помощью Профилировщика. Они были в среднем:
char[] = 4139 ms
CharBuffer = 4466 ms
ByteBuffer = 938 (direct) ms
Отдельные тесты пару раз способствовали CharBuffer.
я также пытался заменить Основанный на файле IO IO В оперативной памяти, и производительность была подобна. При попытке передать от одного собственного потока до другого, то Вы - более обеспеченное использование "прямого" ByteBuffer.
меньше чем с 10%-м различием в производительности, на практике, я одобрил бы CharBuffer. Это - синтаксис, более ясно, существуют менее посторонние переменные, и можно сделать более непосредственное управление на нем (т.е. что-либо, что просит CharSequence).
Сравнительный тест ниже..., это немного неправильно, поскольку BufferedReader выделяется в методе тестирования, а не снаружи... однако, пример ниже позволяет Вам изолировать время IO и устранять факторы как строка или поток байтов, изменяющий размеры его буфера внутренней памяти, и т.д.
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();
}
}
}
Я думаю, что CharBuffer и ByteBuffer (а также любой другой xBuffer) были предназначены для возможности многократного использования, таким образом, Вы можете buf.clear () их вместо того, чтобы пройти перераспределение каждый раз
, Если Вы не снова используете их, Вы не используете их полный потенциал, и это добавит дополнительные издержки. Однако, если Вы - планирование масштабирования этой функции, это могло бы быть хорошей идеей сохранить их там
Версия CharBuffer немного менее сложна (один меньше переменной), инкапсулирует обработку размера буфера и использует стандартный API. Обычно я предпочитал бы это.
Однако существует все еще одно серьезное основание предпочесть версию массива, в некоторых случаях по крайней мере. CharBuffer был только представлен в Java 1.4 поэтому, если Вы развертываетесь к более ранней версии, Вы не можете использование Charbuffer (если Вы role-your-own/use бэкпорт).
P.S., Если Вы используете бэкпорт, не забывают удалять его, как только Вы ловите до версии, содержащей "реальный" версия бэкпортированного кода.
Необходимо избежать CharBuffer
в недавних версиях Java, существует ошибка в #subsequence()
. Вы не можете получить подпоследовательность от второй половины буфера, так как реализация путает capacity
и remaining
. Я наблюдал ошибку в Java 6-0-11 и 6-0-12.