代码之家  ›  专栏  ›  技术社区  ›  Pentium10

对于最多9-10步的字符串连接,是否有比StringBuilder更快的方法?

  •  6
  • Pentium10  · 技术社区  · 14 年前

    我有这段代码来具体化一些数组元素:

    StringBuilder sb = new StringBuilder();
    private RatedMessage joinMessage(int step, boolean isresult) {
            sb.delete(0, sb.length());
            RatedMessage rm;
            for (int i = 0; i <= step; i++) {
                if (mStack[i] == null)
                    continue;
                rm = mStack[i].getCurrentMsg();// msg is built upfront, this just returns, it's a getter method call
                if (rm == null || rm.msg.length() == 0)
                    continue;
                if (sb.length() != 0) {
                    sb.append(", ");
                }
                sb.append(rm.msg);
            }
            rm.msg=sb.toString();
            return rm;
        }
    

    重要的是,该数组最多可容纳10个项目,所以不太多。

    我的跟踪输出告诉我这个方法被调用了18864次,16%的运行时是在这个方法中花费的。我可以优化更多吗?

    11 回复  |  直到 14 年前
        1
  •  3
  •   Eyal Schneider    14 年前

    一些想法:

    1)您是否使用估计的最大容量初始化StringBuilder?这样可以节省内部数组重新分配和复制所花费的时间。

    2)也许您可以附加一个 拖尾 循环中使用逗号,并避免循环中字符串长度的条件。相反,在方法的末尾添加一个条件,并根据需要删除尾随逗号。

        2
  •  5
  •   Eugene Kuleshov    14 年前

    首先,我不会重用StringBuilder并总是创建新实例。这肯定会更快,因为它将允许GC使用年轻一代堆区域。

    另一个允许消除至少一个if语句的小技巧是这样重写代码:

        String separator = "";
        for (int i = 0; i <= step; i++) {
            ...
            sb.append(separator);
            sb.append(rm.msg);
            separator = ", ";
        }
    
        3
  •  1
  •   rsp    14 年前

    您可以进行以下更改(仅显示差异):

        String separator = "";
        for (int i = 0; i <= step; i++) {
        // ...
            sb.append(separator).append(rm.msg);
            separator = ", ";
        }
    

    如果一个额外的if 9次,则以添加一次空字符串为代价将其除去。在决定保留此更改之前,您应该测量它是否对您正在使用的数据有任何帮助:—)

        4
  •  0
  •   Nate Noonen    14 年前

    如果您的函数应该连接数组元素,为什么要传入所有这些疯狂的值和未使用的参数?

    private string joinMessage( string[] myArray)
    {
      StringBuilder sbr = new StringBuilder();
      for(int i = 0; i < myArray.Length; i++)
      {
         if(!string.IsNullOrEmpty(myArray[i])
         {
           sbr.Append(myArray[i]);
           sbr.Append(",")
         }
      }
      return sbr.ToString();
    }
    
        5
  •  0
  •   corsiKa    14 年前

    首先对堆栈中的每个元素执行一步,对所有字符串长度的总和进行计数。

    然后你可以用

    sb.ensureCapacity(totalEndLength);
    

    字符串生成器的工作方式类似于数组列表,因此您可能正在用大部分附加值重建该数组。

        6
  •  0
  •   Burg    14 年前

    有点小优化…在循环外测试逗号。

    private RatedMessage joinMessage(int step, boolean isresult) {
        sb.delete(0, sb.length());
        for (int i = 0; i <= step; i++) {
            if (mStack[i] == null)
                continue;
            rm = mStack[i].getCurrentMsg();
            if (rm == null || rm.msg.length() == 0)
                continue;
            sb.append(rm.msg).append(", ");
        }
        if (sb.length() > 2) {
            sb.delete(sb.length() - 2, 2);
        }
        return sb.toString();
    }
    

    其他建议包括:

    • 确保在构造StringBuilder时,将其初始长度设置为合适的值。
    • 我不确定其余代码的上下文,但也许您可以预先确保mstack[i]不为空,mstack[i].getcurrentmessage()不为空-这将允许您在循环之外获取更多if语句。
        7
  •  0
  •   James Kingsbery    14 年前

    如果你的mstack是一个集合而不是一个数组,你可以 mStack.toString() ,将打印数组的可读字符串。这可能比写自己的要容易。

        8
  •  0
  •   Bananeweizen    14 年前

    此方法中16%的运行时间 包括 排除 调用方法?getcurrentmsg()调用可能是一个隐藏的问题,如果它创建了很多对象。

    除此之外,我建议从堆栈中取出所有需要的字符串,然后调用

    StringUtils.join(myStrings, ", ")

    使用 Apache commons library . 尝试依赖测试过的代码来处理这些低级的事情,而不是每隔一天自己对其进行优化。最后,这将给您带来更好的优化结果,因为您将能够专注于全局(即软件的总体设计)。

        9
  •  0
  •   OscarRyz    14 年前

    有一个单独的副本 mStack 带有字符串表示形式的数组,默认情况下用空字符串初始化,因此循环将为:

    String [] mStackCopy = new String[]{"","","","","","","","","","",};
    // or mstackCopy = new String[mStack.length]; 
    // for( int i = 0 ; i < mStackCopy.lenght ; i++ ) { mStack[i] = "" }
    

    另外,创建具有足够容量的StringBuilder:

    StringBuilder sb = new StringBuilder( 10000 );// 10k chars or whatever makes sense.
    

    因此,当您需要创建消息时,您只需:

    for (int i = 0; i <= step; i++) {
       sb.append( mStackCopy[i] );
    }
    

    空零件不会造成问题,因为它们已经是空的:

    你甚至可以硬编码:

     sb.append( mStackCopy[0]);
     sb.append( mStackCopy[1]);
     sb.append( mStackCopy[2]);
     sb.append( mStackCopy[3]);
     sb.append( mStackCopy[4]);
     sb.append( mStackCopy[5]);
     sb.append( mStackCopy[6]);
     sb.append( mStackCopy[7]);
     sb.append( mStackCopy[8]);
     sb.append( mStackCopy[9]);
    

    但这将导致更多的痛苦,而不是救济在未来,保证。

    当你在你的邮箱里添加东西时:

    MStack item = new MStack();
    item.setCurrentMessage("Some message");
    
     .... 
    

    只需复制消息并附加“,”。

      addToMStack(int position,  MStackItem item ) {
        mStack[position] = item;
        mStackCopy[position] = item.getCurrentMessage() + ", ";
    }
    

    根据空值的出现(如果它很低),你可以捕捉到它们。

      addToMStack(int position,  MStackItem item ) {
        if( item == null ) { return; }
        mStack[position] = item;
        try {
            mStackCopy[position] = item.getCurrentMessage() + ", ";
        } catch( NullPointerException npe ){}
     }
    

    哪个是 可怕的

    或验证它:

      addToMStack(int position,  MStackItem item ) {
        if( item == null ) { return; }
        mStack[position] = item;
        mStackCopy[position] = item.getCurrentMessage() + ", ";
     }
    

    我很确定你的方法是做一些你没有告诉我们的事情。可能原因就在那里。

    另外,如果100%是1秒,16%也不是那么糟糕。

        10
  •  0
  •   Vilx-    14 年前

    有时候,没有什么可以优化的。我认为这就是其中一个例子。你可以试着删掉一两条指令,但原则上你得不到太快的指令。

    我认为唯一需要优化的就是考虑 为什么? 你打了18864次电话,是否可以完全避免其中的一些电话。也许有些是不需要的,或者在某些情况下您可以缓存结果。

        11
  •  0
  •   MetroidFan2002    14 年前

    使用ApacheCommonsLang中的StringBuilder+StringUtils。使用分隔符循环字符串,然后选择StringUtils就是为了这个!

    private RatedMessage joinMessage(int step, boolean isresult) {
            StringBuilder builder = new StringBuilder();
            for (int i = 0; i <= step; i++) {
                WhateverTypeIsFromMStackVariable stackVariable = mStack[i];
                String message = getMessage(stackVariable);
                if(StringUtils.isNotEmpty(message)) {
                    builder.append(message).append(", ");
                }
            }
            RatedMessage rm = new RatedMessage();
            rm.msg = StringUtils.chomp(builder.toString(), ", ");
            return rm;
        }
    
    private static String getMessage(WhateverTypeIsFromMStackVariable stackVariable) {
        if(stackVariable != null) {
            RatedMessage message = stackVariable.getCurrentMsg();
            if(message != null) {
                return message.msg;
            }
         }
         return null;
     }
    

    Apache Commons Lang在这里: http://commons.apache.org/lang/