Java LZW压缩速度非常慢,持续20秒3MB

Java LZW压缩速度非常慢,持续20秒3MB,java,hashmap,compression,lzw,Java,Hashmap,Compression,Lzw,这是我的密码: public static String compress(final String input) { HashMap code=newhashmap(); 对于(int i=0;i

这是我的密码:

public static String compress(final String input)
{

HashMap code=newhashmap();
对于(int i=0;i<256;i++)
{
代码.put((char)i+“”,i);
}
StringBuilder outputString=新的StringBuilder();
int max_code=32767;
int next_代码=257;
String currentString=新字符串();
字符c;
对于(int i=0;iif(next_code需要注意的一件事。String类在Java中是不可变的。换句话说,使用+运算符附加到字符串实际上会创建一个新字符串。许多字符串赋值操作将导致取消引用的字符串对象生成并触发垃圾回收,这将大大降低您的速度

至少,我建议您切换到StringBuffer。如果不进行大量逻辑更改,您应该可以立即获得性能。但是StringBuffer仍然不是处理二进制数据的最有效的内存方式,因为它经过了调整以处理不同字符集的信息。对于压缩/解压缩,您不需要考虑字符集,只是位


java.nio包(java 6)中的ByteBuffer将是一个巨大的飞跃。

currentString
上执行的一些操作非常昂贵,尤其是随着
currentString
大小的增加

声明:

    currentString = currentString + c;
循环遍历字符串中的所有字符,并复制完整字符串+新字符

该行:

    if (!codes.containsKey(currentString))
使用
currentString
的哈希代码。由于
currentString
每次都是一个新字符串,因此需要通过循环整个字符串来计算哈希代码(如果每次都需要计算哈希代码,则会使哈希代码无效)

最后一句话:

    currentString = currentString.substring(0, currentString.length() - 1);
还需要循环遍历整个字符串并创建它的新副本


如果你想让这个程序运行得很快,你就需要消除所有时间循环相同数据的需求。不要创建新的<强> String 。每次你想添加或删除一个字符时,使用一个缓冲区,你可以在两端添加和移除字符。你不需要重新计算完整的散列(通过在完整的字符串上循环),仅仅因为你用一个字符扩展了

currentString

在post I中声明,实际的赋值不是时间密集的部分。它是!HashMap.containsKey()行。由于字符串是一个不可变的类,并且hashmap是缓存的,我相信我已经优化了这一行。在我看来,创建一个封装的ByteBuffer类不会加快解决方案的速度。如果您通过探查器运行一个小的独立测试,您可能看不到GC的影响。我坚持我的主张,您不应该在为该操作使用不可变字符串。Java中的字符串与C中的字符串完全不同。我不确定为什么您以后会在containsKey而不是get方法上看到如此沉重的负载。在评估过程中肯定有很多积极的结果。您是否考虑过使用TreeMap而不是HashMap?具体取决于数据集它可能会加快你的查找速度。它们会占用更多的内存(大约多20%)。我确实尝试更改为树映射,但是压缩输出不再正确。我只是替换了它。我使用它错了吗?只要不设置比较器,就不会有任何区别。我认为树映射不会给您带来很多好处,因为您需要离散查找,而不是通过映射进行迭代。还有一些其他事情。如果您可以使用启动cmdline-XX:+UseCompressedStrings,该命令在Java6上可用(我相信已从Java7中删除)。这将导致字符串为单字节字符,这将减少密钥的大小并提高哈希计算性能。此外,请重新考虑使用ByteBuffer,因为您的哈希映射密钥大小将减少1/2。我非常困惑为什么这会被否决?这是不正确的。问题的解决方案是在意识到每个字典条目都可以定义为前一个代码和一个字符,而不是像您的答案所建议的那样使用缓冲区。@Danny Rancher您通过重写算法解决了您的问题,这并不会使我的答案不正确。您的问题中的实现会进行过多不必要的字符串操作,从而导致实现不正确慢-我在回答中指出了这一点。请不要认为我否定了你的答案。在我重写算法之前,我在我的解决方案中实现了它,并在其中产生了最小的差异。我感谢你的回答,但它没有提供一个可行的解决方案。理论上,你的方法是正确的。实际上,它从来都不起作用。我曾经这条路线和我的解码器的速度慢了10倍多。根据这个粗略的估计,在你的情况下最多只需要2秒。看看这个答案,特别是这里提供的链接
    currentString = currentString.substring(0, currentString.length() - 1);