是否有一个更快的方法,然后StringBuilder最多9-10步字符串连接?

时间:2010-05-25 20:32:24

标签: java concatenation stringbuilder

我有这个代码来连接一些数组元素:

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 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:3)

一些想法:

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

2)也许你可以在循环中附加一个尾随逗号,并避免循环内字符串长度的条件。相反,在方法结束时添加单个条件,并在需要时删除尾随逗号。

答案 2 :(得分:1)

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

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

如果额外增加9次,则需要花费额外费用。在决定保留此更改之前,您应该测量它是否对您使用的数据有所帮助: - )

答案 3 :(得分:0)

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

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();
}

答案 4 :(得分:0)

首先逐步浏览堆栈中的每个元素,计算所有字符串长度的总和。

然后你可以使用

sb.ensureCapacity(totalEndLength);

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

答案 5 :(得分:0)

一点迷你优化......在循环之外使用test-for-comma。

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]不会为null,并且mStack [i] .getCurrentMessage()不为null或为空 - 这将允许你在循环之外采取更多if语句。

答案 6 :(得分:0)

使用字符串表示形式的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添加内容时:

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%并不坏。

答案 7 :(得分:0)

如果您的mStack是Collection而不是数组,则可以执行mStack.toString(),这将打印数组的可读字符串。这可能比写自己更容易。

答案 8 :(得分:0)

此方法的运行时间为16%,包括,不包括被称为方法?如果getCurrentMsg()调用创建了大量对象,则它可能是一个隐藏的问题。

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

StringUtils.join(myStrings, ", ")

使用Apache commons library。尝试依靠经过测试的代码来处理这些低级别的事情,而不是每隔一天自己优化一次。最后,这将为您提供更好的优化结果,因为您将能够专注于整体情况(即软件的整体设计)。

答案 9 :(得分:0)

有时候没有什么可以优化的。我认为这是其中一种情况。你可以尝试削减一两条指令,但原则上你不会得到更快的速度。

我认为唯一要优化的是考虑为什么你将它称为18864次以及是否可以完全避免这些调用中的一些。也许有些不需要,或者你可以在某些情况下缓存结果。

答案 10 :(得分:0)

使用来自Apache Commons Lang的StringBuilder + StringUtils。通过带有分隔符的String循环和chomping是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/