Java-еквіваленти C # String.Format () та String.Join ()


111

Я знаю, що це дещо питання для новачків, але чи є еквіваленти струнним операціям C # на Java?

Зокрема, я говорю про String.Formatі String.Join.


Отже, є String.format, але мені доведеться скачати власний приєднання.
Омар Кухеджі

1
Для приєднатися (), я , як ця відповідь: stackoverflow.com/a/6116469/562139
scorpiodawg

Відповіді:


92

Об'єкт Java String має formatметод (станом на 1,5), але немає joinметоду.

Щоб отримати купу корисних методів String, які ще не включені, ви можете використовувати org.apache.commons.lang.StringUtils .


13
Колекції google: google-collections.googlecode.com також має столяр.
Рон

10
FYI, за коментарем Рона, Google-колекції деякий час тому було перейменовано на Guava.
Кевін Бурліон

3
Вам слід оновити цю відповідь, щоб відобразити, що Java 8 представляє String.join()метод.
Дункан Джонс

46

String.format . Що стосується приєднання, вам потрібно написати своє:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

Сказане вище походить від http://snippets.dzone.com/posts/show/91


6
Точніше: StringBuffer для jdk1.4 і нижче, StringBuilder для jdk1.5 і пізніше, оскільки останній не синхронізований, отже, трохи швидше.
VonC

2
Замість двох викликів iter.hasNext () я зазвичай додаю деліметр, а потім "повертаю buf.substring (0, buf.length () - delimeter.length ())".
Вільмантас Баранаускас

2
Ви можете впорядкувати його невеликим шматочком, вийшовши рано, щоб уникнути розмежувача: while (правда) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13,


29

Як і в Java 8, join()тепер він доступний як два методи класу для класу String. В обох випадках перший аргумент - це роздільник.

Ви можете передати окремі CharSequences як додаткові аргументи :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

Або ви можете передатиIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 також додає новий клас StringJoiner, який ви можете використовувати так:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"


12

Ви також можете використовувати змінні аргументи для рядків наступним чином:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }

4

Щодо приєднання, я вважаю, що це може виглядати трохи менш складно:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

Я не отримую синтаксис Java 5 так сильно, як мені хотілося (вірите чи ні, останнім часом я використовую 1.0.x), тому я можу трохи іржавий, але я впевнений, що концепція правильна .

редагування додатку: додавання рядків може бути повільним, але якщо ви працюєте над кодом графічного інтерфейсу або деяким короткочасним розпорядком, це дійсно не має значення, чи берете ви .005 секунд або .006, тому якщо у вас була колекція під назвою "joinMe" що ви хочете додати до наявного рядка "target", не було б жахливо просто вписати це:

for(String s : joinMe)
    target += s;

Це досить неефективно (і погана звичка), але не все, що ви зможете сприймати, якщо не буде або тисяч рядків, або це всередині величезного циклу, або ваш код справді критичний для продуктивності.

Що ще важливіше, це легко запам'ятовується, короткий, швидкий і дуже читабельний. Продуктивність не завжди є автоматичним переможцем у виборі дизайну.


Цикл for-правильний, але ви також повинні зробити його колекцією <String>. Якщо ви цього не зробите, вам доведеться сказати "for (Object o: c)", тому що ви не могли гарантувати, що все в c є String.
Майкл Майерс

Хороший момент, я ще більше іржавий з Generics. Я відредагую його.
Білл К

Коли конкретизується inline: string + string + string, компілятор фактично використовує StringBuilder для додавання значень. Цікаво, якщо у методі for-loop у вас є другий, чи зробив би компілятор те саме.
Спенсер Кормос

@Spencer K: Він використовує StringBuilder, але він створює новий для кожної ітерації (навряд чи найефективніший метод, але як тоді повинен знати компілятор?). Я не знаю, чи компілятор JIT може оптимізувати це під час виконання.
Майкл Майерс

2
Чекай. Ви говорите + =? Так, це смокче. Наразі цикл та додавання у моїй відповіді використовує StringBuilder, і саме про це я говорю. Хоча + = значно покращився, ви насправді не хочете використовувати його всередині циклу - не стільки, щоб він баггі, але тим, що він може виконуватись як лайно (помилка не є терміном, який зазвичай використовується для проблем із продуктивністю - принаймні, не за мої 20 років програмування). Також JIT може надзвичайно покращити це - але я б на це не покладався.
Білл К

4

Ось досить проста відповідь. Використовуйте, +=оскільки це менше коду, і дозвольте оптимізатору перетворити його на A StringBuilderдля вас. Використовуючи цей метод, вам не потрібно робити жодних "останніх" перевірок у вашому циклі (покращення продуктивності), і вам не доведеться турбуватися про те, щоб зняти будь-які роздільники в кінці.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }

1
Дуже елегантне рішення, не залучаючи сторонніх бібліотек!
Денис Іцкович

2

Я не хотів імпортувати всю бібліотеку Apache, щоб додати просту функцію приєднання, тому ось мій злом.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }

@MrSnowflake Чи має стандартний конструктор рядків метод "RemoveCharAt (int index)" Він не відображається у версії Im running
NSjonas

@NSjonas - так, його зміна на мою відповідь неправильна. removeCharAtНе існує, а вся функція більше не повертає рядок ... Буде чи це виправити.
Мартін Конечний

в той час як ваш на це ... це поточне рішення викине "індекс поза межами меж, якщо ви перейдете в порожній список"
NSjonas

зробив редагування, щоб отримати обрізку правильної довжини, якщо делім більше 1 символу.
Виправили

Thx для зворотного зв'язку. Оновлено.
Мартін Конечний

1

Якщо ви хочете об'єднати (об'єднати) кілька рядків в одну, вам слід використовувати StringBuilder. Це набагато краще, ніж використовувати

for(String s : joinMe)
    target += s;

Існує також невеликий виграш у продуктивності над StringBuffer, оскільки StringBuilder не використовує синхронізацію.

Для такого загальноприйнятого способу корисності, як це, він (зрештою) буде називатися багато разів у багатьох ситуаціях, тому ви повинні зробити його ефективним і не виділяти багато перехідних об'єктів. Ми перепрофілювали багато, багато різних програм Java і майже завжди виявляємо, що об'єднання рядків і виділення рядків / char [] займають значну кількість часу / пам'яті.

Наш рядковий метод колекції -> спочатку обчислює розмір необхідного результату, а потім створює StringBuilder з цим початковим розміром; це дозволяє уникнути непотрібного подвоєння / копіювання внутрішнього знака [], який використовується під час додавання рядків.


re: попередній розрахунок розміру: коли це працює, це чудово. Це не завжди можливо. Я пригадую, що десь читав, що подвоєння розміру буфера щоразу (або насправді множення на будь-який фіксований коефіцієнт) дає щось на кшталт n log n продуктивності, що насправді не все так погано. У загальному випадку альтернативою є скласти список усіх рядків, які ви плануєте об'єднати. Якщо ви використовуєте ArrayList, ви знову копіюєте (якщо ви не знаєте заздалегідь тривалість своєї послідовності), а якщо ви використовуєте LinkedList, то він використовує більше оперативної пам’яті та збирання сміття з об’єктами вузла. Іноді ви не можете перемогти. Спробуйте!
Ян

Наведені вище приклади / запити передбачали приєднати деякий упорядкований збір або масив Strings. Також, попередньо обчисливши розмір, ви уникаєте зайвих невикористаних символів, до яких зазвичай призводить зростання внутрішнього масиву char [].
djb

1

Я написав:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

але Collectionне підтримується JSP, тому для функції тегів я написав:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

і поставити у .tldфайл:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

і використовувати його у файлах JSP як:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}



0

Тут я бачу дуже багато складних реалізацій String.Join. Якщо у вас немає Java 1.8, і ви не хочете імпортувати нову бібліотеку, наступної реалізації має бути достатньо.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}

-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Таким чином, ntop String зберігає значення всієї колекції з роздільниками та скобами.


1
Я не впевнений, на яку частину питання це відповідає? Це не String.format, якщо ви не плануєте додавати біти рядка по бітах до масиву, і [1,0,92,3] не є настільки універсальним, як загальний рядок .join.
Омар Кухеджі

-8

Я б просто скористався оператором конкатенації рядків "+" для об'єднання двох рядків. s1 += s2;


Тому що це погана практика і повільна.
MrSnowflake
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.