Улучшение производительности конкатенации строк в Java

Возможный дубликат:
java Конкатенация строк

Как повысить производительность этого фрагмента кода:

public static String concatStrings(Vector strings) {
    String returnValue = "";

    Iterator iter = strings.iterator();
    while( iter.hasNext() ) {
        returnValue += (String)iter.next();
    }

    return returnValue;
}

Ответ 1

Вы можете использовать StringBuilder, вместо того, чтобы делать + = с отдельными строками. Строки неизменны в Java, а это означает, что после создания объекта String вы не можете его изменить. Использование + = для строк в цикле приведет к созданию множества отдельных экземпляров String, что может вызвать проблемы с производительностью. StringBuilder может конкатенировать строки без необходимости создавать новые экземпляры, что может сэкономить некоторое время, в зависимости от точного сценария.

Ответ 2

public static String concatStrings(List<String> strings) {
    StringBuilder sb = new StringBuilder();
    for (String s : strings) {
       sb.append(s);
    }    
    return sb.toString();
}

Некоторые замечания:

  • Используйте StringBuilder, когда вам нужно построить строку в цикле
    • + отлично подходит для простой конкатенации, но ужасно для инкрементной сборки
  • По возможности используйте для каждого для удобства чтения
  • java.util.Vector synchronized; если вам не нужна эта (дорогостоящая) функция, просто используйте ArrayList.

Не использовать необработанные типы

  • JLS 4.8 Необработанные типы

    Использование типов raw допускается только как уступка совместимости устаревшего кода. Использование необработанных типов в коде, написанном после введения родословности в язык программирования Java, настоятельно не рекомендуется. Возможно, что будущие версии языка программирования Java будут запрещать использование необработанных типов.

  • Эффективное Java 2nd Edition: Пункт 23: Не используйте необработанные типы в новом коде

    Если вы используете необработанные типы, вы теряете все преимущества безопасности и выразительности дженериков.

См. также

Ответ 3

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

Код, заданный в вопросе, будет фактически скомпилирован (с Sun javac) к чему-то по следующей строке:

public static String concatStrings(Vector strings) {
    String returnValue = "";

    Iterator iter = strings.iterator();
    while( iter.hasNext() ) {
        String str = (String)iter.next();

        StringBuilder sb = new StringBuilder(returnValue);
        sb.append(str);

        returnValue = sb.toString();
    }

    return returnValue;
}

Компилятор изменит конкатенацию += на ту, которая использует StringBuilder. Однако компилятор, вероятно, перепишет код внутри цикла, поэтому на каждой итерации будет создан новый экземпляр StringBuilder, который не очень удобен для пользователя.

Следовательно, в этом случае, вероятно, было бы лучшей идеей создать StringBuilder вне цикла самостоятельно и выполнить ручную конкатенацию строк:

public static String concatStrings(Vector strings) {
    StringBuidler returnValueBuilder;

    Iterator iter = strings.iterator();
    while( iter.hasNext() ) {
        returnValueBuilder.append((String)iter.next());
    }

    return returnValueBuilder.toString();
}

Ответ 4

private static final int AVERAGE_STRING_LENGTH = 10;  // Using 10 is arbitrary

public static final String concatStrings(final Collection<String> strings) {
    if (strings == null)   return null;

    final int size = strings.size();
    if (size == 0)         return "";
    if (size == 1)         return strings.get(0);

    final StringBuilder returnValue =
        new StringBuilder(AVERAGE_STRING_LENGTH * size);

    for (String s : strings) {
        returnValue.append(s);
    }

    return returnValue.toString();
}

Возможно, немного за бортом, здесь каждая оптимизация, о которой я мог подумать для concatStrings() - продемонстрировал выше - некоторые из них могут быть неприменимы к вашей среде:

  • Используйте StringBuilder - это намного эффективнее для этих последовательных конкатенаций
  • Используйте StringBuilder(int capacity), чтобы указать вероятную необходимую емкость, если есть способ ее предвидеть (используется средний размер выше, но другие методы может быть более удобным)
  • Используйте параметр параметра Collection, чтобы обеспечить более эффективную структуру данных, чем Vector, который синхронизирован - плюс вызывающий имеет гораздо большую гибкость (например, нет необходимости копировать Set<String> в Vector<String> только для вызова этого метода).
  • Простые случаи жесткого кода, если они вероятны (например, null, размер 0 и размер 1 выше).
  • Используйте final, чтобы облегчить встраивание и оптимизацию JIT
  • Загрузите размер strings, если он используется несколько раз. (например, используется 3 раза в приведенном выше коде.)

Наконец, если эта операция выполняется очень часто над большим количеством строк, загляните в Веревки для Java.

Ответ 5

Также, если вы хотите сделать это быстрее, вы можете реорганизовать код для использования ArrayList вместо Vector. ArrayList не является потокобезопасным, поэтому он немного быстрее, чем Vector (зависит от ситуации, может быть 0% разница, может быть разница в 5%).

Ответ 6

Вы создаете строку каждый раз, когда вы вызываете + =. Например

String theString = "1"; //Makes an immutable String object "1"
theString +="2"; //Makes a new immutable String object "12"
theString +="3"; //makes a new immutable String object "123"

Использование построителя строк позволяет избежать этой проблемы.

StringBuilder sb = new StringBuilder("1"); //Makes a StringBuilder object holding 1
sb.append("2"); //The same StringBuilder object now has "12" in it.
sb.append("3"); //The same StringBuidler object now has "123" in it. 
String theString = sb.toString(); //Creates a new String object with "123" in it 

Обратите внимание, что в первом примере мы сделали все эти промежуточные строки, где во втором примере мы создали только StringBuilder и финальную строку (в обоих примерах мы создали "1" "2" и "3", когда мы их использовали как аргументы). Вы можете видеть, что в первом примере создано меньше объектов, и если вы много добавляете к String, вы можете себе представить, как это складывается!

Ответ 7

В дополнение к использованию StringBuilder вы можете заранее пройти список строк и рассчитать точный размер, необходимый для StringBuilder. Затем передайте это значение в конструктор StringBuilder. Обратите внимание, что это относится к категории преждевременной оптимизации, но вы просили производительность... (Вы должны посмотреть на код для выращивания буферов StringBuilder/StringBuffer, его образовательных)

Ответ 8

Помимо использования ArrayList и StringBuilder, рассмотрим это.

В современной парадигме информатики пространство можно почти всегда торговать во времени (возможно, это субъективное утверждение). Для данного сценария с приведенным ниже кодом используется дополнительное пространство O (N), где N = нет строк (для нового буфера, который содержит list.toArray()). Это лучше, чем использование Iterator по крайней мере (open AbstractList.iterator()). Важно отметить, что сложность времени значительно лучше, вычисляя конкатенацию двух строк сразу, на одной итерации, тем самым уменьшая количество итераций наполовину! Это что-то вроде использования подхода с динамическим программированием (помните, вычислив Fibonacci nos с помощью динамического программирования)!!

    StringBuilder sb = new StringBuilder();
    Object[] o = list.toArray();
    //For even no of Strings
    if(o.length % 2 == 0){
        concatFaster(sb, o);
    } else {
        //For odd no of Strings
        concatFaster(sb, o);
        sb.append(o[o.length-1]); // For the odd index
    }

    public static void concatFaster(StringBuilder sb, Object[] o) {
    for (int i = 0; i < o.length - 1; i+=2) {
        sb.append(o[i]).append(o[i+1]);
    }
}