12

标题说明了一切。有什么方法可以在不使用中间字符串的情况下从 StringBuilder 转换为 byte[] ?

问题是我正在管理非常大的字符串(数百万个字符),然后我有一个循环,最后添加一个字符并获取字节 []。将 StringBuffer 转换为 String 的过程使得这个循环非常非常非常缓慢。

有没有办法做到这一点?提前致谢!

4

6 回答 6

14

正如许多人已经建议的那样,您可以使用 CharBuffer 类,但分配一个新的 CharBuffer 只会让您的问题变得更糟。

相反,您可以直接将 StringBuilder 包装在 CharBuffer 中,因为 StringBuilder 实现了 CharSequence:

Charset charset = StandardCharsets.UTF_8;
CharsetEncoder encoder = charset.newEncoder();

// No allocation performed, just wraps the StringBuilder.
CharBuffer buffer = CharBuffer.wrap(stringBuilder);

ByteBuffer bytes = encoder.encode(buffer);

编辑: Duarte 正确地指出,该CharsetEncoder.encode方法可能返回一个缓冲区,其后备数组大于实际数据——这意味着它的容量大于其限制。必须从 ByteBuffer 本身读取,或者从 ByteBuffer 中读取保证大小正确的字节数组。在后一种情况下,不可避免地会在内存中保存两个字节副本,尽管时间很短:

ByteBuffer byteBuffer = encoder.encode(buffer);

byte[] array;
int arrayLen = byteBuffer.limit();
if (arrayLen == byteBuffer.capacity()) {
    array = byteBuffer.array();
} else {
    // This will place two copies of the byte sequence in memory,
    // until byteBuffer gets garbage-collected (which should happen
    // pretty quickly once the reference to it is null'd).

    array = new byte[arrayLen];
    byteBuffer.get(array);
}

byteBuffer = null;
于 2013-10-19T23:30:34.027 回答
2

如果您愿意StringBuilder用其他东西替换,另一种可能性是Writer由 a 支持ByteArrayOutputStream

ByteArrayOutputStream bout = new ByteArrayOutputStream();
Writer writer = new OutputStreamWriter(bout);
try {
    writer.write("String A");
    writer.write("String B");
} catch (IOException e) {
    e.printStackTrace();
}
System.out.println(bout.toByteArray());

try {
    writer.write("String C");
} catch (IOException e) {
    e.printStackTrace();
}
System.out.println(bout.toByteArray());

与往常一样,您的里程可能会有所不同。

于 2016-09-22T18:06:46.020 回答
1

不幸的是,上面处理 ByteBuffer 的 array() 方法的答案有点错误......问题是分配的 byte[] 可能比您预期的要大。因此,将有难以摆脱的尾随 NULL 字节,因为您无法在 Java 中“重新调整”数组的大小。

这是一篇更详细地解释这一点的文章: http ://worldmodscode.wordpress.com/2012/12/14/the-java-bytebuffer-a-crash-course/

于 2014-03-02T00:41:44.443 回答
1

对于初学者,您可能应该使用StringBuilder,因为StringBuffer通常不需要同步开销。

不幸的是,没有办法直接转到bytes,但您可以将 s 复制char到一个数组中,或者从0to迭代length()并读取每个s charAt()

于 2013-10-19T22:57:45.777 回答
0

如果您想要性能,我不会使用 StringBuilder 或创建字节 []。相反,您可以逐步写入将首先获取数据的流。如果你不能这样做,你可以将数据从 StringBuilder 复制到 Writer,但首先不创建 StringBuilder 会快得多。

于 2013-10-19T23:45:29.497 回答
0

你想用“百万个字符”来完成什么?这些日志需要解析吗?您可以将其仅读取为字节并坚持使用ByteBuffer吗?然后你可以这样做:

buffer.array()

得到一个byte[]

取决于你在做什么,你也可以只使用 achar[]CharBuffer

CharBuffer cb = CharBuffer.allocate(4242);
cb.put("Depends on what it is you need to do");
... 

然后你可以得到一个char[]

cp.array()

将事情重新整理出来总是好的,这很有趣并且证明了这一点。Java REPL 不是我们习惯的东西,但是,嘿,有 Clojure 可以拯救流利地说 Java 的日子:

user=> (import java.nio.CharBuffer)
java.nio.CharBuffer

user=> (def cb (CharBuffer/allocate 4242))
#'user/cb

user=> (-> (.put cb "There Be") (.array))
#<char[] [C@206564e9>

user=> (-> (.put cb " Dragons") (.array) (String.))
"There Be Dragons"
于 2013-10-19T23:05:21.067 回答