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

发布于 2024-09-03 03:07:28 字数 788 浏览 4 评论 0 原文

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

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% 的运行时间花费在这个方法上。我可以进一步优化吗?

I have this code to concate some array elements:

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

Important the array holds max 10 items, so it's not quite much.

My trace output tells me this method is called 18864 times, 16% of the runtime was spent in this method. Can I optimize more?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

情丝乱 2024-09-10 03:07:29

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

允许消除至少一个 if 语句的另一个小技巧是重写代码,如下所示:

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

First of all I won't reuse StringBuilder and always create new instance. That will be certainly faster, because it would allow GC to use young generation heap area.

Another little trick that allows to eliminate at least one if statement is to rewrite your code like this:

    String separator = "";
    for (int i = 0; i <= step; i++) {
        ...
        sb.append(separator);
        sb.append(rm.msg);
        separator = ", ";
    }
反目相谮 2024-09-10 03:07:29

一些想法:

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

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

some ideas:

1) Do you initialize the StringBuilder with the estimated max capacity? This can save the time spent in the inner array re-allocation & copying.

2) Maybe you can append a trailing comma in the loop, and avoid the condition for string length inside the loop. Instead, add a single condition at the end of the method, and remove the trailing comma if needed.

人海汹涌 2024-09-10 03:07:29

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

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

它会消除 9 次额外的 if,但代价是添加一次空字符串。在决定保留此更改之前,您应该衡量它是否对您正在使用的数据有帮助:-)

You can make the following change (showing only the differences):

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

It gets rid if an extra if 9 times at the cost of adding an empty string once. You should measure if it helps at all with the data you are using before you decide to keep this change :-)

冷月断魂刀 2024-09-10 03:07:29

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

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

If your function is supposed to concatenate array elements, why are you passing in all these crazy values and unused parameters?

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();
}
流云如水 2024-09-10 03:07:29

首先遍历堆栈中的每个元素,计算所有字符串长度的总和。

然后,您可以使用

sb.ensureCapacity(totalEndLength);

字符串构建器,就像数组列表一样工作,因此您可能会使用大部分附加内容来重建该数组。

Take a step through each element in the stack first, taking a count of the sum of all the string lengths.

Then you can use

sb.ensureCapacity(totalEndLength);

String builder works like an array list, so you might be rebuilding that array with most of your appends.

思念满溢 2024-09-10 03:07:29

一点小小的优化...将逗号测试放在循环之外。

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 语句。

A bit of a mini-optimization... take the test-for-comma outside of the loop.

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

Other suggestions would be:

  • Make sure that when the StringBuilder is constructed you set its initial length to a decent value
  • I'm not sure of the context of the rest of the code, but maybe you can pre-ensure that mStack[i] will not be null, and that mStack[i].getCurrentMessage() is not null or empty - this will allow you to take more if statements outside of the loop.
故事与诗 2024-09-10 03:07:29

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

If your mStack is a Collection instead of an array, you can just do mStack.toString(), which will print a readable string of the array. That might be easier than writing your own.

狼性发作 2024-09-10 03:07:29

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

我建议从堆栈中取出所有需要的字符串,然后

除此之外,

使用 。 org/lang/api-2.5/org/apache/commons/lang/StringUtils.html#join" rel="nofollow noreferrer">Apache 公共库。尝试依靠经过测试的代码来完成如此低级的事情,而不是每隔一天自己优化它。最终,这将为您带来更好的优化结果,因为您将能够专注于大局(即软件的整体设计)。

16% runtime in this method including or excluding called methods? The getCurrentMsg() call might be a hidden problem, if it creates lots of objects.

Besides that, I suggest to take all the needed Strings out of your stack and afterwards call

StringUtils.join(myStrings, ", ")

using the Apache commons library. Try relying on tested code for such low level things instead of optimizing it yourself every other day. In the end that will give you better optimization results because you will be able to concentrate on the big picture (i.e. the overall design of your software).

猛虎独行 2024-09-10 03:07:29

有一个带有字符串表示形式的 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% 也不算太糟糕。

Have a separate copy of the mStack array with the string representation, by default initialized with empty String, so your loop would be:

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

Also, create the StringBuilder with enough capacity:

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

So, when you need to create the message you would simply:

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

And empty parts won't cause a problem because they are blank already:

You may even hard code it:

 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]);

But this would cause more pain than relief in the future, guaranteed.

When you add something to your mStack:

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

 .... 

Just make a copy of the message and append the ", " already.

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

And depending on the occurrence of nulls ( if its low ) you can catch them

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

Which is horrendous

Or validate it:

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

I'm pretty sure your method is doing something else that you don't show us. Probably the reason is there.

Also, 16% is not that bad, if 100% is 1sec.

睫毛溺水了 2024-09-10 03:07:29

有时没有什么可以优化的。我认为这是这样的案例之一。您可以尝试删除一两条指令,但原则上您不会更快地得到它。

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

Sometimes there's just nothing else to optimize. I think this is one of such cases. You can try to shave off an instruction or two maybe, but you won't get it much faster in principle.

I think the only thing left to optimize is to consider why you are calling it 18864 times and whether some of those calls can be avoided altogether. Perhaps some are not needed, or perhaps you can cache the result in some cases.

酒解孤独 2024-09-10 03:07:29

使用 Apache Commons Lang 中的 StringBuilder + StringUtils。使用分隔符循环遍历字符串并进行 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/

Use StringBuilder + StringUtils from Apache Commons Lang. Looping through a String with a separator and chomping is what StringUtils is all about!

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 is here: http://commons.apache.org/lang/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文