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

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

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

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

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

20 ответов

Решение

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

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

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

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

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

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));
sb.deleteCharAt(sb.length() - 1) 

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

String.join(",", serverIds);

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

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

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

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

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

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

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

С Java-8 Вы можете использовать статический метод String учебный класс,

String#join(CharSequence delimiter,Iterable<? extends CharSequence> elements) ,


public class Test {

    public static void main(String[] args) {

        List<String> names = new ArrayList<>();
        names.add("James");
        names.add("Harry");
        names.add("Roy");
        System.out.println(String.join(",", names));
    }
}

ВЫХОД

James,Harry,Roy

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

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

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

StringBuilder result = new StringBuilder();
for(String string : collection) {
    result.append(string);
    result.append(',');
}
return result.substring(0, result.length() - 1) ;
StringBuilder sb = new StringBuilder();
sb.append("abcdef");
sb.deleteCharAt(sb.length() - 1);
assertEquals("abcde",sb.toString());
// true

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

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

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

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

String Builder.Remove(stringBuilder.Length - 1, 1);

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

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

sb.append('\b');

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

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

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

Вы можете попытаться использовать класс 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"

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

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

Я обнаружил, что делаю это довольно много, поэтому я написал тест для 3 основных методов разделителей добавлений:

(тест с правильной разминкой и 100 раундов по 100000 итераций)

"Добавить после"

      static void appendAfter()
{
    sb.append('{');
    for (int i = 0; i < 10; i++)
    {
        sb.append('"');
        sb.append(i);
        sb.append('"');
        sb.append(':');
        sb.append(i);
        sb.append(',');
    }
    sb.setLength(sb.length() - 1);
    sb.append('}');
}

"Добавить перед"

      static void appendBefore()
{
    sb.append('{');
    String delimiter = "";
    for (int i = 0; i < 10; i++)
    {
        sb.append(delimiter);
        sb.append('"');
        sb.append(i);
        sb.append('"');
        sb.append(':');
        sb.append(i);
        delimiter = ",";
    }
    sb.append('}');
}

"Может быть, добавить"

      static void appendMaybe()
{
    sb.append('{');
    for (int i = 0; i < 10; i++)
    {
        sb.append('"');
        sb.append(i);
        sb.append('"');
        sb.append(':');
        sb.append(i);
        if (i < 9)
        {
            sb.append(',');
        }
    }
    sb.append('}');
}

Получил следующие результаты:

Помимо того, что она самая быстрая, я считаю, что реализация «Append Maybe» лучше всего показывает замысел кода. Обычно это более важно, чем доля наносекунд, получаемая за итерацию.

Я оставил тестовый код здесь на случай, если кто-то захочет попробовать его на своей платформе. Пожалуйста, поделитесь своими результатами выше, если вы это сделаете!

Поскольку вы знаете персонажа, которого хотите удалить, вы можете использовать это

sb.TrimEnd(",");

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

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

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

Гораздо более простое и компактное решение требует небольшой модификации вашего кода.

      for (String serverId : serverIds) {
  if (!sb.isEmpty()) sb.append(",")
  sb.append(serverId);
}
Другие вопросы по тегам