以下是否有理由选择CharBuffer
到char[]
:
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 );
}
(in
是Reader
中的out
和Writer
中的{{1}}?
答案 0 :(得分:17)
不,在这种情况下,没有理由更喜欢CharBuffer
。
通常,CharBuffer
(和ByteBuffer
)可以真正简化API并鼓励正确处理。如果您正在设计公共API,那么绝对值得考虑面向缓冲区的API。
答案 1 :(得分:6)
我想对这种比较进行微型基准测试。
以下是我写的课程。
事情是我无法相信CharBuffer的表现如此糟糕。我有什么问题?
编辑:由于下面的第11条评论我已经编辑了代码和输出时间,所以整体性能更好,但时间上仍然存在显着差异。我还尝试了注释中提到的out2.append((CharBuffer)buff.flip())选项,但它比下面代码中使用的write选项慢得多。
结果:(以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()));
}
}
答案 2 :(得分:4)
如果这是你用缓冲区做的唯一事情,那么在这个例子中,数组可能是更好的选择。
CharBuffer上有很多额外的铬,但在这种情况下它们都不相关 - 并且只会减慢一小部分。
如果你需要让事情变得更复杂,你可以随后重构。
答案 3 :(得分:3)
实际上,差异实际上是<10%,而不是其他人报告的30%。
要读取和写入5MB文件24次,我的号码使用Profiler。他们平均来说:
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();
}
}
}
答案 4 :(得分:2)
我认为CharBuffer和ByteBuffer(以及任何其他xBuffer)都是为了可重用性,所以你可以buf.clear()它们而不是每次重新分配
如果你不重复使用它们,你就没有充分利用它们,它会增加额外的开销。但是,如果您计划扩展此功能,那么将它们保留在那里可能是一个好主意
答案 5 :(得分:1)
CharBuffer版本稍微复杂一点(变量少一个),封装缓冲区大小处理并使用标准API。一般来说我更喜欢这个。
然而,至少在某些情况下,仍然有一个很好的理由更喜欢阵列版本。 CharBuffer仅在Java 1.4中引入,因此如果您要部署到早期版本,则无法使用Charbuffer(除非您自己角色/使用backport)。
P.S如果您使用backport,请记住在您赶上包含“real”版本的反向移植代码的版本后将其删除。
答案 6 :(得分:1)
在最近的Java版本中应避免使用CharBuffer
,#subsequence()
中存在错误。由于实现混淆了capacity
和remaining
,因此无法从缓冲区的后半部分获取子序列。我在java 6-0-11和6-0-12中观察到了这个bug。