Удалить последний символ StringBuilder?


423

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

for (String serverId : serverIds) {
  sb.append(serverId);
   sb.append(",");
}

Дает что-то вроде: serverId_1, serverId_2, serverId_3,

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


11
Если, соединяя строки, вы имеете в виду «конкатенацию строк», это зависит от количества строк и их длины. Использование строителя строк более эффективно, если вы собираетесь использовать большое количество строк независимо от их размера, поскольку строки являются неизменяемыми. Каждый раз, когда вы объединяете строки вместе, вы создаете новую результирующую строку (которая на самом деле является массивом символов). Строители строк - это, по сути, список символов, которые не становятся неизменной строкой, пока вы не вызовете метод toString ().
дислексиканабоко

4
Если вы используете Java 8, просто используйте StringJoiner: stackoverflow.com/a/29169233/901641
ArtOfWarfare

Ответы:


640

Другие указали на deleteCharAtметод, но вот еще один альтернативный подход:

String prefix = "";
for (String serverId : serverIds) {
  sb.append(prefix);
  prefix = ",";
  sb.append(serverId);
}

В качестве альтернативы используйте Joinerкласс из гуавы :)

Начиная с Java 8, StringJoinerявляется частью стандарта JRE.


7
@Coronatus: Нет, потому что "" - это отсутствие каких-либо символов, а не одного символа.
Джон Скит

31
не будет выполнять префикс = ","; каждый цикл цикла влияет на производительность?
Хариш

21
@ Хариш: Возможно, крошечный, крошечный кусочек - хотя вряд ли будет значительным.
Джон Скит

4
@Harish - и, возможно, вовсе нет, если оптимизатор развернет первую итерацию цикла.
Стивен С.

6
Apache Commons имеет другую альтернативу гуава это Joinerтоже в их StringUtils. commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… , java.lang.String)
GoRoS

419

Другое простое решение:

sb.setLength(sb.length() - 1);

Более сложное решение:

Приведенное выше решение предполагает, что sb.length() > 0... то есть есть "последний символ", который нужно удалить. Если вы не можете сделать это предположение и / или не можете справиться с исключением, которое возникнет, если это предположение неверно, то сначала проверьте длину StringBuilder; например

// Readable version
if (sb.length() > 0) {
   sb.setLength(sb.length() - 1);
}

или

// Concise but harder-to-read version of the above.
sb.setLength(Math.max(sb.length() - 1, 0));

23
Очень хорошее решение. Наименьшее влияние на производительность и минимум кода требуется :)
Ален О'Ди

186
if(sb.length() > 0){
    sb.deleteCharAt(sb.length() - 1);
}

33
За это слишком много проголосуют, но это не эффективно, это делает system.arraycopy. Что сказал @Rohit Reddy Korrapolu.
alianos-

13
Это небезопасно для sb.length() == 0также
Matthias

Это безопасно с суррогатной парой персонажей в игре?
rogerdpack

Если предположить, что последний символ является разделителем запятой (как в примере), то суррогаты не имеют значения. Если вам нужно обобщить, то вычтите separator.length()вместо 1.
Стивен C

61

Начиная с Java 8, класс String имеет статический метод join. Первый аргумент - это строка, которую вы хотите видеть между каждой парой строк, а второй - это Iterable<CharSequence>(оба являются интерфейсами, поэтому что-то вроде List<String>работает. Итак, вы можете просто сделать это:

String.join(",", serverIds);

Также в Java 8 вы можете использовать новый StringJoinerкласс для сценариев, в которых вы хотите начать конструирование строки до того, как у вас будет полный список элементов для ее добавления.


nvm отредактировал для вас, если вы не возражаете, удалил мой комментарий также
Евгений

@ Евгений - я переписал ответ полностью, чтобы сосредоточиться String.joinвместо StringJoiner.
ArtOfWarfare

37

Просто получите позицию последнего появления персонажа.

for(String serverId : serverIds) {
 sb.append(serverId);
 sb.append(",");
}
sb.deleteCharAt(sb.lastIndexOf(","));

Поскольку lastIndexOfобратный поиск будет выполнен, и вы знаете, что он будет найден с первой попытки, производительность здесь не будет проблемой.

РЕДАКТИРОВАТЬ

Так как я продолжаю поднимать свой ответ (спасибо, ребята 😊), стоит учитывать следующее:

На Java 8 вперед это только будет более четким и ясным , чтобы использовать StringJoiner . У него есть один метод для простого разделителя и перегрузка для префикса и суффикса.

Примеры взяты отсюда: пример

Пример использования простого разделителя:

    StringJoiner mystring = new StringJoiner("-");    

    // Joining multiple strings by using add() method  
    mystring.add("Logan");  
    mystring.add("Magneto");  
    mystring.add("Rogue");  
    mystring.add("Storm");  

    System.out.println(mystring);

Вывод:

Логан-магнито-Разбойник-Буря

Пример с суффиксом и префиксом:

    StringJoiner mystring = new StringJoiner(",", "(", ")");    

    // Joining multiple strings by using add() method  
    mystring.add("Negan");  
    mystring.add("Rick");  
    mystring.add("Maggie");  
    mystring.add("Daryl");  

    System.out.println(mystring);

Вывод

(Negan, Рик, Мэгги, Дэрил)


Вы будете уверены, что последний символ является, ,потому что это было последним утверждением for loop. Это lastInfexOfбольше для удобочитаемости и делает это легким делом, если вы не хотите помнить, индексирован ли он 0 или нет. Кроме того, вам не нужно вмешиваться в длину строителя строки. Это просто для удобства.
Рюэль Рибейро,

34

В этом случае,

sb.setLength(sb.length() - 1);

предпочтительнее, так как просто присваивает последнее значение, '\0'тогда как удаление последнего символаSystem.arraycopy


1
setLengthВызов ничего последнего значения не назначая. Строковые буферы Java не заканчиваются нулем / нулем. На самом деле, setLengthпросто обновление lengthполя.
Стивен С.

@Rohit Reddy Korrapolu: Но arraycopyкопирует 0 элементов, так что я думаю, что это может быть оптимизировано.
Maaartinus

2
Если аргумент newLength больше или равен текущей длине, добавляется достаточное количество нулевых символов ('\ u0000'), так что длина становится аргументом newLength. Что не так.
Fglez


11

Еще одна альтернатива

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}
sb.deleteCharAt(0);

2
Это должно быть лучше, чем удаление последнего символа, так как это требует расчета размера. Если удаление первого символа не приводит к перемещению данных ...
slott

8

С другой стороны,

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;

Можно использовать как вы можете добавить "." в конце.
artfullyContrived

6
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true

5

Еще одна альтернатива:

public String join(Collection<String> collection, String seperator) {
    if (collection.isEmpty()) return "";

    Iterator<String> iter = collection.iterator();
    StringBuilder sb = new StringBuilder(iter.next());
    while (iter.hasNext()) {
        sb.append(seperator);
        sb.append(iter.next());
    }

    return sb.toString();
}

3

Чтобы избежать переустановки (повлиять на производительность) prefixиспользования TextUtils.isEmpty:

            String prefix = "";
            for (String item : list) {
                sb.append(prefix);
                if (TextUtils.isEmpty(prefix))
                    prefix = ",";
                sb.append(item);
            }

К какому пакету относится TestUtils?
Маркус

@Markus android.text.TextUtils
NickUnuchek

1

Вы можете попытаться использовать класс Joiner вместо удаления последнего символа из вашего сгенерированного текста;

                List<String> textList = new ArrayList<>();
                textList.add("text1");
                textList.add("text2");
                textList.add("text3");

                Joiner joiner = Joiner.on(",").useForNull("null");
                String output = joiner.join(textList);

               //output : "text1,text2,text3"

1

Я делаю что-то вроде ниже:

    StringBuilder stringBuilder = new StringBuilder();
    for (int i = 0; i < value.length; i++) {
        stringBuilder.append(values[i]);
        if (value.length-1) {
            stringBuilder.append(", ");
        }
    }

0

Вот еще одно решение:

for(String serverId : serverIds) {
   sb.append(",");
   sb.append(serverId); 
}

String resultingString = "";
if ( sb.length() > 1 ) {
    resultingString = sb.substring(1);
}

1
А ну понятно. Вы вызываете подстроку в StringBuilder, а не в String.
Стивен С

Но в любом случае, это всего лишь небольшой вариант решения Заки от 2010 года.
Стивен С.

0

Мне лично нравится добавлять символ возврата (или больше для более длинного «разделителя») в конце:

for(String serverId : serverIds) {
    sb.append(serverId);
    sb.append(",");
}

sb.append('\b');

Обратите внимание, что у него есть проблемы:

  • как \bотображается зависит от окружающей среды,
  • length()от Stringсодержания может отличаться от длины «» символов Записи видны

Когда все в \bпорядке и длина не имеет значения, например, при входе в консоль, мне это кажется достаточно хорошим.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.