Java: перетворити список <String> у рядок


595

JavaScript має Array.join()

js>["Bill","Bob","Steve"].join(" and ")
Bill and Bob and Steve

Чи є у Java щось подібне? Я знаю, що я можу щось спірити з StringBuilder:

static public String join(List<String> list, String conjunction)
{
   StringBuilder sb = new StringBuilder();
   boolean first = true;
   for (String item : list)
   {
      if (first)
         first = false;
      else
         sb.append(conjunction);
      sb.append(item);
   }
   return sb.toString();
}

... але немає сенсу робити це, якщо щось подібне вже є частиною JDK.


1
Дивіться також це запитання для списків
Casebash

18
Не строго пов'язаний, але Android має вбудовану функцію приєднання як частина класу TextUtils: developer.android.com/reference/android/text/… , java.lang.Iterable)
Xavi

16
У Java 8 є String.join()метод. Подивіться на цю відповідь, якщо ви використовуєте Java 8 (або новішу версію
micha

Відповіді:


763

З Java 8 ви можете це зробити без будь-якої сторонньої бібліотеки.

Якщо ви хочете приєднатися до колекції рядків, ви можете скористатися новим методом String.join () :

List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"

Якщо у вас є колекція іншого типу, ніж String, ви можете використовувати API Stream для приєднання колектора :

List<Person> list = Arrays.asList(
  new Person("John", "Smith"),
  new Person("Anna", "Martinez"),
  new Person("Paul", "Watson ")
);

String joinedFirstNames = list.stream()
  .map(Person::getFirstName)
  .collect(Collectors.joining(", ")); // "John, Anna, Paul"

StringJoinerКлас також може бути корисним.


7
На жаль, String.join приймає лише CharSequences, а не так, як можна було б сподіватися на об'єкти. Навіть не впевнений , якщо це nullsafe
Marc

@MarcassertThat(String.join(", ", Lists.newArrayList("1", null)), is("1, null"));
Sanghyun Lee

StringJoiner особливо корисний, якщо хочеться приєднатися до чогось типу, [a, b, c]включаючи брекети.
коппор

@Marc String.join також приймає Iterable<CharSequence>; Інтерфейсні відносини:Iterable -> Collection -> List
афіл

306

Всі посилання на Apache Commons є чудовими (і саме цим користуються більшість людей), але я вважаю, що еквівалент Guava , Joiner , має набагато приємніший API.

Ви можете зробити просту справу приєднання за допомогою

Joiner.on(" and ").join(names)

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

Joiner.on(" and ").skipNulls().join(names);

або

Joiner.on(" and ").useForNull("[unknown]").join(names);

і (достатньо корисно, наскільки я бажаю використовувати його для переваги commons-lang), можливість працювати з Картами:

Map<String, Integer> ages = .....;
String foo = Joiner.on(", ").withKeyValueSeparator(" is ").join(ages);
// Outputs:
// Bill is 25, Joe is 30, Betty is 35

що надзвичайно корисно для налагодження тощо.


10
спасибі за штекер! Наш столяр також надає вам можливість додавати безпосередньо до додатка (наприклад, StringBuilder або будь-якого сценариста) без створення проміжних рядків, яких, схоже, не вистачає бібліотеці Apache.
Кевін Бурліон

2
Це чудово, але ви можете додати будь-який .useForLastSeparator()чи подібний метод? Таким чином, ви можете отримати щось на зразок "і" між лише останніми двома пунктами (з "," для решти).
Джефф Еванс

2
Так, це безпечно для ниток. Єдиний стан, збережений у столярі, - це separator(який є final). Все, що я бачив у Guava, де я використовував еквівалент Apache Commons, був набагато кращим (читайте: чистіше, швидше, безпечніше і просто надійніше взагалі) у Гуаві з меншим числом збоїв у крайньому випадку та проблемами безпеки потоку та менший слід пам’яті. Здається, їх керівний принцип "найкращим можливим способом" відповідає дійсності поки що.
Shadow Man

Якщо вам потрібно піти в зворотному напрямку (тобто, розділити String на шматки), перегляньте спліттер класу Guava - також дуже добре розроблений / реалізований.
Метт Пасселл

У Java 8 є String.join()метод і StringJoinerклас.
theTechnoKid

138

Не вийшло, але багато бібліотек мають подібне:

Commons Lang:

org.apache.commons.lang.StringUtils.join(list, conjunction);

Весна:

org.springframework.util.StringUtils.collectionToDelimitedString(list, conjunction);


49

Ні, у стандартному Java API немає такого способу зручності.

Не дивно, що Apache Commons надає таку річ у своєму класі StringUtils, якщо ви не хочете писати це самостійно.


11
Мене завжди помиляє, що String має розкол, але не з'єднання. У певному сенсі це має сенс, але це просто дратує, що в String немає принаймні статичного методу.
Powerlord

3
Я з тобою Бемроуз. Принаймні, вони дали нам isEmpty()метод замість (статичний) join()метод у String...: rollseyes: :)
Bart Kiers

41

Три можливості в Java 8:

List<String> list = Arrays.asList("Alice", "Bob", "Charlie")

String result = String.join(" and ", list);

result = list.stream().collect(Collectors.joining(" and "));

result = list.stream().reduce((t, u) -> t + " and " + u).orElse("");

27

З колектором java 8 це можна зробити за допомогою наступного коду:

Arrays.asList("Bill", "Bob", "Steve").stream()
.collect(Collectors.joining(" and "));

Також найпростіше рішення в java 8:

String.join(" and ", "Bill", "Bob", "Steve");

або

String.join(" and ", Arrays.asList("Bill", "Bob", "Steve"));

21

Я написав це (я використовую його для бобів та експлуатуйте toString, тому не пишіть Collection<String>):

public static String join(Collection<?> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<?> 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, тому для TLD я написав:

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, ", ")}

1
запропонувати зміни Collection<>в Iterable<>?
Джейсон S

@JasonS +1 Хороше запитання, але читати stackoverflow.com/questions/1159797 / ...
gavenkoa

19

Код у вас - це правильний спосіб зробити це, якщо ви хочете використовувати JDK без жодних зовнішніх бібліотек. Немає простого "одного вкладиша", який можна було б використовувати в JDK.

Якщо ви можете користуватися зовнішніми вкладками, рекомендую заглянути в org.apache.commons.lang.StringUtils клас у бібліотеці Apache Commons.

Приклад використання:

List<String> list = Arrays.asList("Bill", "Bob", "Steve");
String joinedResult = StringUtils.join(list, " and ");

17

Ортодоксальним способом її досягнення є визначення нової функції:

public static String join(String joinStr, String... strings) {
    if (strings == null || strings.length == 0) {
        return "";
    } else if (strings.length == 1) {
        return strings[0];
    } else {
        StringBuilder sb = new StringBuilder(strings.length * 1 + strings[0].length());
        sb.append(strings[0]);
        for (int i = 1; i < strings.length; i++) {
            sb.append(joinStr).append(strings[i]);
        }
        return sb.toString();
    }
}

Зразок:

String[] array = new String[] { "7, 7, 7", "Bill", "Bob", "Steve",
        "[Bill]", "1,2,3", "Apple ][","~,~" };

String joined;
joined = join(" and ","7, 7, 7", "Bill", "Bob", "Steve", "[Bill]", "1,2,3", "Apple ][","~,~");
joined = join(" and ", array); // same result

System.out.println(joined);

Вихід:

7, 7, 7 і Білл, і Боб, і Стів, і [Білл], і 1,2,3 і Apple] [і ~, ~


5
надати параметру методу те саме ім'я, що і сам метод, мабуть, не найкраща ідея. separatorбуло б набагато більше.
ccpizza

10

Рішення Java 8 с java.util.StringJoiner

У Java 8 є StringJoinerклас. Але вам потрібно ще трохи написати котельну табличку, адже це Java.

StringJoiner sj = new StringJoiner(" and ", "" , "");
String[] names = {"Bill", "Bob", "Steve"};
for (String name : names) {
   sj.add(name);
}
System.out.println(sj);

Вам не потрібно писати трохи на панель котлів, якщо ви використовуєте більш зручний метод String.join () .
Енді Томас

9

Ви можете використовувати бібліотеку apache commons, яка має клас StringUtils та метод з'єднання.

Перевірте це посилання: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Зауважте, що вищезазначене посилання може з часом застаріти, і в цьому випадку ви можете просто шукати в Інтернеті "apache commons StringUtils", що дозволяє вам знайти останню посилання.

(з посиланням на цю тему) Java-еквіваленти C # String.Format () та String.Join ()


7

Ви можете зробити це:

String aToString = java.util.Arrays.toString(anArray);
// Do not need to do this if you are OK with '[' and ']'
aToString = aToString.substring(1, aToString.length() - 1);

Або однолінійний (лише коли ви не хочете "[" і "]")

String aToString = java.util.Arrays.toString(anArray).substring(1).replaceAll("\\]$", "");

Сподіваюсь, це допомагає.


1
Це не додасть поєднання вибору.
гексий

OOPS !! Вибачте, я цього не помітив.
NawaMan

6

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

String[] array = new String[] { "Bill", "Bob", "Steve","[Bill]","1,2,3","Apple ][" };
String join = " and ";

String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "");

System.out.println(joined);

Вихід:

Білл і Боб, Стів і [Білл] і 1,2,3 і Apple] [


Не надто досконалий і не надто веселий спосіб!

String[] array = new String[] { "7, 7, 7","Bill", "Bob", "Steve", "[Bill]",
        "1,2,3", "Apple ][" };
String join = " and ";

for (int i = 0; i < array.length; i++) array[i] = array[i].replaceAll(", ", "~,~");
String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "").replaceAll("~,~", ", ");

System.out.println(joined);

Вихід:

7, 7, 7 і Білл, Боб і Стів і [Білл], 1,2,3 і Apple] [


Спробуйте це з "[Білл]", "1,2,3" та "Яблуко] [". Творче, але в ньому є випадки, коли це неправильно.
Jason S

1
Тепер спробуйте це з "~, ~". Ви не можете зробити програму з такою архітектурою повністю захищеною від куль.
Jason S

Так, я знаю ... Я зняв протиріччя. Але вам варто подумати трохи ретельніше над розміщенням відповідей тут, особливо для таких питань, яким вже кілька років. Відповіді одразу корисні оригінальному плакату, але й відображаються в пошуку Google. (Насправді, на запитання, яким вже кілька років, нові відповіді можуть не мати значення для оригінального плаката.) Рішення, які мають недоліки або помилки, можуть бути шкідливими для людей, які виявляють це пізніше поза контекстом.
Jason S

1
Я багато чого тут вчусь на інших посадах, таких як моя, з не ідеальним рішенням, але дійсно багатим знаннями та стороннім мисленням. Відпочиньте для інших, кожен повинен зрозуміти, що робить кожен рядок свого коду. Пам'ятайте, що програмування - це мистецтво, і навіть кожен рядок коду означає щось про особистість програміста. І так, я не буду використовувати цей код у виробництві!
Даніель Де Леон


4

Якщо ви використовуєте колекції Eclipse (раніше колекції GS ), ви можете використовувати makeString()метод.

List<String> list = Arrays.asList("Bill", "Bob", "Steve");

String string = ListAdapter.adapt(list).makeString(" and ");

Assert.assertEquals("Bill and Bob and Steve", string);

Якщо ви можете перетворити свій Listтип на колекції Eclipse Collections, тоді ви можете позбутися адаптера.

MutableList<String> list = Lists.mutable.with("Bill", "Bob", "Steve");
String string = list.makeString(" and ");

Якщо ви просто хочете відокремити комою рядок, ви можете використовувати версію, makeString()яка не має параметрів.

Assert.assertEquals(
    "Bill, Bob, Steve", 
    Lists.mutable.with("Bill", "Bob", "Steve").makeString());

Примітка. Я є членом колекції Eclipse.


3

З потоком java 1.8 можна використовувати,

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<String> list = Arrays.asList("Bill","Bob","Steve").
String str = list.stream().collect(Collectors.joining(" and "));

3

EDIT

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

Оскільки у мене є два коментарі з цього приводу, я змінюю свою відповідь на:

static String join( List<String> list , String replacement  ) {
    StringBuilder b = new StringBuilder();
    for( String item: list ) { 
        b.append( replacement ).append( item );
    }
    return b.toString().substring( replacement.length() );
}

Що схоже на оригінальне запитання.

Тож якщо ви не хочете додавати всю банку до свого проекту, ви можете скористатися цією.

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

Ось він, поряд з ліцензією Apache 2.0.

public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer
    if (iterator == null) {
        return null;
    }
    if (!iterator.hasNext()) {
        return EMPTY;
    }
    Object first = iterator.next();
    if (!iterator.hasNext()) {
        return ObjectUtils.toString(first);
    }

    // two or more elements
    StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
    if (first != null) {
        buf.append(first);
    }

    while (iterator.hasNext()) {
        if (separator != null) {
            buf.append(separator);
        }
        Object obj = iterator.next();
        if (obj != null) {
            buf.append(obj);
        }
    }
    return buf.toString();
}

Тепер ми знаємо, дякую відкритим кодом


Це буде працювати зараз, але ви не можете бути впевнені, як List.toString () буде вести себе в майбутньому. Я б ніколи не довіряв операції toString (), окрім друку якоїсь інформаційної рядки про відповідний об'єкт. На вашу захист ви не єдиний, хто пропонує це рішення.
Ганс Догген

Що робити, якщо вміст елемента в списку містить підрядку ", "?
Барт Кіерс

@Hans & @Bart: Ти маєш рацію. Я думав, що ніхто більше не помітить: P Я змінюю свою відповідь.
OscarRyz

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

2

Google Guava API також має .join (), хоча (як це очевидно з іншими відповідями), Apache Commons є майже стандартним тут.


1

Java 8 дійсно приносить

Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

метод, тобто безпечний за допомогою використання prefix + suffix для null значень.

Його можна використовувати таким чином:

String s = stringList.stream().collect(Collectors.joining(" and ", "prefix_", "_suffix"))

Collectors.joining(CharSequence delimiter)Метод просто викликає joining(delimiter, "", "")внутрішньо.


0

Ви можете використовувати це з StringUtils Spring Framework. Я знаю, що це вже згадувалося, але ви дійсно можете просто взяти цей код, і він працює негайно, не потребуючи в ньому Spring.

// from https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/StringUtils.java

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class StringUtils {
    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
        if(coll == null || coll.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            sb.append(prefix).append(it.next()).append(suffix);
            if (it.hasNext()) {
                sb.append(delim);
            }
        }
        return sb.toString();
    }
}

-3

Спробуйте це:

java.util.Arrays.toString(anArray).replaceAll(", ", ",")
                .replaceFirst("^\\[","").replaceFirst("\\]$","");

1
Можливо, тому, що питання вимагає використання списку проти масиву?
знизують

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