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


111

Я знаю, что это вопрос новичка, но есть ли эквиваленты строковым операциям C # в Java?

Конкретно я про String.Formatи String.Join.


Итак, есть String.format, но мне придется свернуть свое собственное соединение.
Омар Кухеджи,

1
Для join () мне нравится этот ответ: stackoverflow.com/a/6116469/562139
scorpiodawg 08

Ответы:


92

У объекта Java String есть formatметод (начиная с версии 1.5), но нет joinметода.

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


13
Коллекции google: google-collections.googlecode.com также имеет Joiner.
Рон

10
К вашему сведению, на комментарий Рона, Google-коллекции были переименованы в Guava некоторое время назад.
Кевин Бурриллион 01

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 (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren


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), так что я, возможно, немного ржав, но я уверен, что концепция верна .

добавление редактирования: добавление строк может быть медленным, но если вы работаете над кодом графического интерфейса или какой-то непродолжительной рутиной, на самом деле не имеет значения, берете ли вы 0,005 секунды или 0,006, поэтому, если у вас есть коллекция под названием «joinMe» что вы хотите добавить к существующей строке "target", было бы не ужасно просто встроить это:

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

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

Что еще более важно, это легко запомнить, коротко, быстро и легко читается. Производительность не всегда является автоматическим победителем при выборе дизайна.


Цикл for правильный, но вы также должны сделать его Collection <String>. Если вы этого не сделаете, вам придется сказать «for (Object o: c)», потому что вы не можете гарантировать, что все в c было строкой.
Майкл Майерс

Хорошая мысль, я еще более ржавый с Generics. Я отредактирую это.
Билл К.

При объединении inline: string + string + string компилятор фактически использует StringBuilder для добавления значений. Интересно, если в методе for-loop у вас есть второй, сделает ли компилятор то же самое.
Спенсер Кормос,

@Spencer K: он использует StringBuilder, но создает новый для каждой итерации (вряд ли это самый эффективный метод, но тогда как компилятор должен знать?). Я не знаю, может ли JIT-компилятор оптимизировать это во время выполнения.
Майкл Майерс

2
Ой-подожди. Вы говорите + =? Да, это отстой. В настоящее время цикл и добавление в моем ответе используют StringBuilder, и это то, о чем я говорю. Хотя + = значительно улучшился, вы действительно не хотите использовать его внутри цикла - не столько потому, что он глючит, сколько потому, что он может работать как дерьмо (ошибка - это не термин, обычно используемый для проблем с производительностью - по крайней мере, не за мои 20 лет программирования). Также JIT может значительно улучшить это, но я бы не стал на это полагаться.
Bill K

4

Вот довольно простой ответ. Используйте, +=так как это меньше кода и позвольте оптимизатору преобразовать его 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), он не отображается в версии, которую я использую
NSjonas

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

пока вы на нем ... это текущее решение вызовет «исключение индекса за пределами границ, если вы перейдете в пустой список»
NSjonas

внесены изменения, чтобы обрезать нужную длину, если разделитель содержит более 1 символа. Также исправлена ​​проблема, из-за которой вы обрезали последнего символа, а не первого, как вам действительно было нужно
NSjonas

Спасибо за отзыв. Обновлено.
Мартин Конечны

1

Если вы хотите объединить (объединить) несколько строк в одну, вам следует использовать StringBuilder. Это намного лучше, чем использовать

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

Также есть небольшое преимущество в производительности над StringBuffer, поскольку StringBuilder не использует синхронизацию.

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

Наша многоразовая коллекция -> строковый метод сначала вычисляет размер требуемого результата, а затем создает StringBuilder с этим начальным размером; это позволяет избежать ненужного дублирования / копирования внутреннего char [], используемого при добавлении строк.


re: предварительный расчет размера: Когда это работает, это здорово. Это не всегда возможно. Я помню, как где-то читал, что удвоение размера буфера каждый раз (или действительно умножение на любой фиксированный коэффициент) дает что-то вроде производительности n log n, что на самом деле не так уж и плохо. В общем случае альтернативой является составление списка всех строк, которые вы планируете объединить. Если вы используете ArrayList, у вас снова есть копирование (если вы заранее не знаете длину своей последовательности), а если вы используете LinkedList, тогда он использует больше оперативной памяти и сборку мусора с объектами узлов. Иногда не получается победить. Попробуйте!
Ян

Приведенные выше примеры / запросы предполагали некоторую упорядоченную коллекцию или массив строк для объединения. Кроме того, предварительно вычисляя размер, вы избегаете лишних неиспользуемых символов, к которым обычно приводит рост внутреннего массива char [].
djb 07

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]" 

По сути, String ntop хранит значение всей коллекции с разделителями-запятыми и скобками.


1
Я не уверен, на какую часть вопроса это отвечает? Это не String.format, если вы не планируете добавлять биты строки побитно в массив, а [1,0.92,3] не так универсален, как обычная строка .join.
Omar Kooheji

-8

Я бы просто использовал оператор конкатенации строк «+» для соединения двух строк. s1 += s2;


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