Видалити останній символ StringBuilder?


422

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

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

Дає щось на кшталт: serverId_1, serverId_2, serverId_3,

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


11
Якщо при об'єднанні рядків ви маєте на увазі "з'єднання рядків", це залежить від кількості рядків та їх довжини. Використання конструктора струн є більш ефективним, якщо ви збираєтеся забивати багато струн незалежно від їх розміру, оскільки струни незмінні. Кожен раз, коли ви об'єднуєте рядки разом, ви створюєте новий результатний рядок (який насправді є масивом char). Структури струн - це, по суті, список символів, який не стає незмінним рядком, поки ви не зателефонуєте на метод toString ().
dislexicanaboko

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клас від Guava :)

Що стосується Java 8, StringJoinerце частина стандартного JRE.


7
@Coronatus: Ні, тому що "" - це відсутність будь-яких символів, ані одного символу.
Джон Скіт

31
не виконує префікс = ","; кожен цикл циклу впливає на продуктивність?
Харіш

21
@Harish: Можливо, крихітний, крихітний шматочок - це навряд чи буде суттєвим.
Джон Скіт

4
@Harish - і, можливо, зовсім не буде, якщо оптимізатор відкрутить першу цикл ітерації.
Стівен C

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 Редді Корраполу.
alianos-

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

Це безпечно із сурогатними парними персонажами під час гри?
rogerdpack

Якщо припустити, що останній символ є роздільником комах (як у прикладі), то сурогати не мають значення. Якщо вам потрібно узагальнити, відніміть separator.length()замість 1.
Stephen C

61

Як і в Java 8, клас String має статичний метод join. Перший аргумент - це рядок, який потрібно між кожною парою рядків, а другий - це Iterable<CharSequence>(які є обома інтерфейсами, тому щось подібне List<String>працює. Отже, ви можете просто зробити це:

String.join(",", serverIds);

Також у Java 8 ви можете використовувати новий StringJoinerклас для сценаріїв, де ви хочете почати конструювати рядок, перш ніж матимете повний перелік елементів для його введення.


nvm відредагований для вас, якщо ви не заперечуєте, також видалив мій коментар
Євген

@Eugene - я переписав відповідь повністю зосередитися на String.joinзамість StringJoiner.
ArtOfWarfare

37

Просто отримайте позицію останнього явища символів.

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

Оскільки lastIndexOfбуде здійснено зворотний пошук, і ви знаєте, що його знайдуть при першій спробі, продуктивність тут не буде проблемою.

EDIT

Оскільки я продовжую отримувати відповіді на свою відповідь (дякую людям 😊), варто подумати про це:

На 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);

Вихідні дані

(Неган, Рік, Меггі, Даріл)


Ви будете впевнені, що останній символ є тим, ,що це був останній вислів for loop. Це lastInfexOfбільше для читабельності та для того, щоб зробити його непродуманим, якщо ви не хочете пам'ятати, чи він індексований 0 чи ні. Крім того, вам не потрібно втручатися в довжину струнобудівника. Це просто для зручності.
Реуель Рібейро

34

В цьому випадку,

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

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


1
setLengthВиклик нічого останнього значення не призначаючи. Буферні рядки Java не закінчуються нулем / нулем. Насправді setLengthце просто оновлення lengthполя.
Стівен C

@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
Має бути краще, ніж видаляти останній знак, оскільки для цього потрібні розрахунки розмірів. Якщо видалення першого знака не призведе до переміщення даних ...
слот


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 не рядком.
Стівен С

Але все одно, це лише невеликий варіант рішення рішення Закі з 2010 року.
Стівен C

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.