Существует ли более быстрый метод, чем StringBuilder, для конкатенации строк с максимальным шагом 9-10? - PullRequest
6 голосов
/ 26 мая 2010

У меня есть этот код для объединения некоторых элементов массива:

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 ]

5 голосов
/ 26 мая 2010

Прежде всего я не буду повторно использовать StringBuilder и всегда буду создавать новый экземпляр. Это, безусловно, будет быстрее, потому что это позволит GC использовать область кучи молодого поколения.

Еще один маленький трюк, который позволяет исключить хотя бы один оператор if, чтобы переписать ваш код следующим образом:

    String separator = "";
    for (int i = 0; i <= step; i++) {
        ...
        sb.append(separator);
        sb.append(rm.msg);
        separator = ", ";
    }
3 голосов
/ 26 мая 2010

некоторые идеи:

1) Вы инициализируете StringBuilder с предполагаемой максимальной емкостью? Это может сэкономить время, затрачиваемое на перераспределение и копирование внутреннего массива.

2) Возможно, вы можете добавить в цикл запятую trailing и избежать условия для длины строки внутри цикла. Вместо этого добавьте одно условие в конце метода и удалите запятую, если необходимо.

1 голос
/ 26 мая 2010

Можно внести следующие изменения (показаны только различия):

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

Избавляется, если дополнительно, если 9 раз за счет добавления пустой строки один раз. Прежде чем вы решите сохранить это изменение, вам следует оценить, поможет ли это вообще с данными, которые вы используете: -)

0 голосов
/ 26 мая 2010

Используйте StringBuilder + StringUtils из Apache Commons Lang. StringUtils - это циклическая переброска String с разделителем и сжатие!

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/

0 голосов
/ 26 мая 2010

Иногда просто оптимизировать нечего. Я думаю, что это один из таких случаев. Вы можете попробовать сбрить одну или две инструкции, но в принципе вы не получите ее намного быстрее.

Я думаю, что единственное, что нужно оптимизировать, это рассмотреть , почему вы звоните 18864 раза и можно ли вообще избежать некоторых из этих вызовов. Возможно, некоторые из них не нужны, или, возможно, в некоторых случаях вы можете кэшировать результат.

0 голосов
/ 26 мая 2010

16% времени выполнения в этом методе , включая или , исключая вызываемые методы? Вызов getCurrentMsg () может быть скрытой проблемой, если он создает много объектов.

Кроме того, я предлагаю вынуть все необходимые строки из вашего стека, а затем вызвать

StringUtils.join(myStrings, ", ")

с использованием библиотеки Apache commons . Попробуйте полагаться на проверенный код для таких вещей низкого уровня, а не оптимизировать его каждый день. В конце концов это даст вам лучшие результаты оптимизации, потому что вы сможете сосредоточиться на общей картине (то есть на общем дизайне вашего программного обеспечения).

0 голосов
/ 26 мая 2010

Если ваш mStack является коллекцией, а не массивом, вы можете просто сделать mStack.toString(), который выведет читаемую строку массива. Это может быть проще, чем написать свой собственный.

0 голосов
/ 26 мая 2010

Имеется отдельная копия массива 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() + ", ";
 }

Я почти уверен, что ваш метод делает что-то еще, что вы нам не показываете. Вероятно, причина здесь.

Кроме того, 16% - это не так уж плохо, если 100% - 1 сек.

0 голосов
/ 26 мая 2010

Немного мини-оптимизации ... выведите тест на запятую за пределы цикла.

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 вне цикла.
0 голосов
/ 26 мая 2010

Сначала пройдитесь по каждому элементу стека, посчитав сумму всех длин строк.

Тогда вы можете использовать

sb.ensureCapacity(totalEndLength);

Строитель строк работает как список массивов, так что вы можете перестраивать этот массив с большинством добавлений.

...