CharBuffer与char []

时间:2008-11-16 20:54:13

标签: java io buffer

以下是否有理由选择CharBufferchar[]

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

inReader中的outWriter中的{{1}}?

7 个答案:

答案 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()中存在错误。由于实现混淆了capacityremaining,因此无法从缓冲区的后半部分获取子序列。我在java 6-0-11和6-0-12中观察到了这个bug。