【问题标题】:How can this Java algorithm be faster这个Java算法怎么能更快
【发布时间】:2012-02-17 20:48:14
【问题描述】:

我有这个循环,它将布尔 LinkedList 拆分为 8 位,并返回缓冲区中每个字节的 ASCII 值。该函数返回字符串缓冲区。

如果 LinkedList 的大小很大,这段代码会非常慢。我尝试通过简单的循环更改Iterator,但它仍然很慢。

这个算法怎么可能真的很快?也许使用多线程?

注意:linkedList的大小并不总是能被8整除。

public String toString(){

        String buffer = "";
        String output = "";

        LinkedList<Boolean> bits = this.bits;

        for(Iterator it = this.bits.iterator(); it.hasNext();){
            if(buffer.length() >= 8){
                output += (char)Integer.parseInt(buffer, 2);
                buffer = "";
            }

            buffer += ((Boolean)it.next() == false) ? "0" : "1";
        }

        if(buffer != "")
            output += (char)Integer.parseInt(buffer, 2);

        return output;
}

【问题讨论】:

  • 最后一个非 8bits 的缓冲区将被转换成它的 ascii 值,并且该值被连接到输出。

标签: java algorithm binary linked-list ascii


【解决方案1】:

这些建议将为您提供足够的性能,同时保持代码简单易读。首先使用这些更改进行测试,如果不满足您的性能要求,然后慢慢引入其他答案中建议的优化技术

  1. 使用BitSet 而不是LinkedList&lt;Boolean&gt;
  2. 使用StringBuilder output; 而不是String output;
  3. 使用StringBuilder buffer; 而不是String buffer;
  4. 使用Integer.valueOf() 而不是Integer.parseIntvalueOf 我认为低于 128 的值使用缓存。

【讨论】:

  • @beny23 OP 正在从他可以使用 valueOf 的字符串中创建整数
【解决方案2】:
  1. 使用以预期容量初始化的StringBuilder 输出:

    StringBuilder out = new StringBuilder(bits.size() / 8 + 1);
    
  2. 使用按位运算代替parseInt(),如下所示:

    int i = 0;          
    int c = 0;
    for(Boolean b: bits){
        if (i > 0 && i % 8 == 0){
            out.append((char) c);
            c = 0;
        }
        c = (c << 1) | (b ? 1 : 0);
        i++;
    }
    out.append((char) c); // Not sure about the desired behaviour here
    

【讨论】:

  • 哇,我用我的代码压缩的演示文件用了 1 多分钟,而你用的不到 1 秒。我只是不明白你为什么要这样做 i%8 !=1。模数可以是 2, 3, 4.. ?里面的代码必须在字符串生成器中附加非 8 位 char 值。
  • @Pier-alexandreBouchard:是的,此代码包含一个错误,请稍等。
  • @Pier-alexandreBouchard:现在好像没事了。
  • 我做了几个测试,但似乎没问题。
  • 我谦虚地相信我下面的代码效率更高,因为它不使用 StringBuilder。
【解决方案3】:

字符串连接很慢,特别是对于大型列表(因为字符串是不可变的,它们必须被复制,这需要一些时间,而且每次复制也需要更多空间)。使用 StringBuilder 而不是 String 追加。换句话说:bufferoutput 应该是 StringBuilder 实例。

【讨论】:

    【解决方案4】:

    正如其他人建议的那样 - 使用 BitSet。其余的,我认为下面的方法非常有效:

        public String toString() {
            char[] bytes = new char[bits.size() / 8 + ((bits.size() % 8 > 0) ? 1 : 0)];
            int bitCounter = 0;
            int word = 0;
            int byteCounter = 0;
            for (boolean b : bits) {
                word = (word << 1) | (b ? 1 : 0);
                if (bitCounter == 7) {
                    bytes[byteCounter] = (char) word;
                    ++byteCounter;
                    bitCounter = 0;
                    word = 0;
                } else {
                    ++bitCounter;
                } // else
            } // foreach
            bytes[byteCounter] = (char) word;
            return new String(bytes);
        } // toString() method
    

    这可能是不使用字节计数器的更好选择:

            public String toString() {
                int size = bits.size() / 8 + ((bits.size() % 8 > 0) ? 1 : 0);
                if (size == 0) {
                    return "";
                } // if
                char[] bytes = new char[size];
                int bitCounter = 0;
                int word = 0;
                for (boolean b : bits) {
                    if (bitCounter % 8 == 0
                            && bitCounter > 0) {
                        bytes[(bitCounter - 1) / 8] = (char) word;
                        word = 0;
                    } // if
                    word = (word << 1) | (b ? 1 : 0);
                    ++bitCounter;
                } // foreach
                bytes[size - 1] = (char) word;
                return new String(bytes);
            } // toString() method
    

    【讨论】:

      【解决方案5】:

      尝试将缓冲区保留为int。我是说

       buffer = buffer << 1 + (((Boolean)it.next() == false) ? 0 : 1);
      

      而不是

       buffer += ((Boolean)it.next() == false) ? "0" : "1";
      

      也使用StringBuilder 输出。这是一个很小的变化,但总是一点点。

      【讨论】:

      • 我谦虚地建议您在第一个代码示例中使用buffer * 2buffer = (buffer &lt;&lt; 1) | ((Boolean)it.next() ? 1 : 0);。在一个表达式中,最好不要混合算术和位操作,即使它们是相同的。
      【解决方案6】:

      尝试以下方法:

       StringBuilder b = new StringBuilder();
       int ch = 0;
       int n = 0;
      
       for (Boolean bit : bits) {
         ch <<= 1;
         if (bit) {
           ch++;
         }
         if (++n == 8) {
           b.append((char)ch);
           n = 0;
           ch = 0;
         }
       }
      
       if (n > 0) {
         b.append((char)ch);
       }  
      
       System.out.println(b.toString());
      

      【讨论】:

        【解决方案7】:

        使用 StringBuffer 或 stringBuilder 代替 String 作为缓冲区和输出变量。

        String var 是不可变的,因此每个操作都会在堆中创建一个新实例,而 StringBuilder 和 StringBuffer 则不是。

        【讨论】:

          猜你喜欢
          • 2011-09-27
          • 1970-01-01
          • 2016-03-19
          • 2010-09-13
          • 1970-01-01
          • 2016-08-17
          • 2014-07-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多