在用Java编码时,我偶然发现了一件奇怪的事情:
我将文件读入字节数组(byte[] file_bytes
)中,我想要的是十六进制转储输出(例如Linux中的实用程序hexdump
或xxd
)。基本上,这是可行的(请参见未注释掉的for循环代码),但是对于较大的文件(> 100 KiB),需要一些时间来遍历bytearray块,进行正确的格式化,依此类推。
但是,如果我将for-loop-code替换为 注释掉的代码(使用具有相同for-loop-code的类进行计算!),它的工作速度非常快。 / p>
此行为的原因是什么?
代码段:
[...]
long counter = 1;
int chunk_size = 512;
int chunk_count = (int) Math.ceil((double) file_bytes.length / chunk_size);
for (int i = 0; i < chunk_count; i++) {
byte[] chunk = Arrays.copyOfRange(file_bytes, i * chunk_size, (i + 1) * chunk_size);
// this commented two lines calculate way more faster than the for loop below, even though the calculation algorithm is the same!
/*
* String test = new BytesToHexstring(chunk).getHexstring();
* hex_string = hex_string.concat(test);
*/
for (byte b : chunk) {
if( (counter % 4) != 0 ){
hex_string = hex_string.concat(String.format("%02X ", b));
} else{
hex_string = hex_string.concat(String.format("%02X\n", b));
}
counter++;
}
}
[...]
BytesToHexstring类:
class BytesToHexstring {
private String m_hexstring;
public BytesToHexstringTask(byte[] ba) {
m_hexstring = "";
m_hexstring = bytes_to_hex_string(ba);
}
private String bytes_to_hex_string(byte[] ba) {
String hexstring = "";
int counter = 1;
// same calculation algorithm like in the codesnippet above!
for (byte b : ba) {
if ((counter % 4) != 0) {
hexstring = hexstring.concat(String.format("%02X ", b));
} else {
hexstring = hexstring.concat(String.format("%02X\n", b));
}
counter++;
}
return hexstring;
}
public String getHexstring() {
return m_hexstring;
}
}
字符串hex_string:
00 11 22 33
44 55 66 77
88 99 AA BB
CC DD EE FF
基准:
file_bytes.length = 102400字节= 100 KiB
file_bytes.length = 256000字节= 250 KiB
答案 0 :(得分:2)
两个选项之间有一个重要区别。在慢速版本中,您将每次迭代连接到为每个字节建立的整个十六进制字符串上。字符串连接是一个缓慢的操作,因为它需要复制整个字符串。当字符串变大时,此复制将花费更长的时间,并且每个字节都会复制整个内容。
在更快的版本中,您将分别构建每个块,并且仅将整个块与输出字符串而不是每个单独的字节连接在一起。这意味着昂贵的串联要少得多。在构建uyp块时,您仍在使用串联,但是由于块比整个输出小得多,因此这些串联更快。
尽管通过使用字符串生成器而不是字符串连接,您可以做得更好。 StringBuilder是一个用于高效地逐步构建字符串的类。它避免了串联所做的每个追加上的完整副本。我希望如果将其重新构造为使用StringBuilder,则两个版本的性能大致相同,并且比您已经拥有的两个版本都要快。