Найшвидший спосіб розмістити вміст Set <String> в одному рядку зі словами, розділеними пробілами?


76

У мене є кілька Set<String>s, і я хочу перетворити кожну з них в одну, Stringде кожен елемент оригіналу Setвідокремлений пробілом "". Перший наївний підхід робить це так

Set<String> set_1;
Set<String> set_2;

StringBuilder builder = new StringBuilder();
for (String str : set_1) {
  builder.append(str).append(" ");
}

this.string_1 = builder.toString();

builder = new StringBuilder();
for (String str : set_2) {
  builder.append(str).append(" ");
}

this.string_2 = builder.toString();

Хтось може придумати швидший, гарніший чи ефективніший спосіб зробити це?


Відповіді:


141

За допомогою commons / lang ви можете зробити це за допомогою StringUtils.join :

String str_1 = StringUtils.join(set_1, " ");

Ви не можете перемогти це для стислості.

Оновлення:

Перечитуючи цю відповідь, я б віддав перевагу іншій відповіді щодо Столяра Гуави зараз. Насправді, в наші дні я не наближаюсь до apache commons.

Ще одне оновлення:

Java 8 представила метод String.join()

String joined = String.join(",", set);

Хоча це не настільки гнучко, як версія Guava, це зручно, коли у вас немає бібліотеки Guava на шляху до вашого курсу.


2
короткий, але негнучкий. Я "" замінюю null, хочу я цього чи ні, і не маю опції пропустити nulls ... ось чому ми створили Joiner для Гуави (див. Іншу відповідь).
Kevin Bourrillion

Я зазвичай не маю нульових значень у своїх колекціях, тому короткий підхід мені підходить, але Гуава породжує! Дякую за те, що це сталося ...
Шон Патрік Флойд,

Оскільки ви не очікуєте нульових значень, ви хотіли б, щоб ваш столяр підірвався, якщо у вас все- таки було нульове значення - це те, що Столяр Гуави робить за замовчуванням. :-)
Кевін Бурріллон,

1
@ smp7d не повинно бути. Нуль не є дійсним введенням. Припускаючи, що це було б небезпечно. Відхиліть нульові значення за допомогою перевірок попередніх умов, і вам більше ніколи не доведеться турбуватися про нульову безпеку
Шон Патрік Флойд,

1
@SeanPatrickFloyd Хм, цікаво виглядає прочитане, дякую за посилання. Я дуже обережний, навмисний і мінімальний із своїм використанням null загалом, тому, можливо, я не помітив стільки. Найголовніше, однак, я Завжди ЗАВЖДИ завжди ЗАВЖДИ перевіряю значення / параметри на нуль перед використанням і викидаю виняток, якщо вони є, майже у всіх випадках.
java-addict301

31

Якщо ви використовуєте Java 8, ви можете використовувати рідну

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

метод:

Повертає нове, що Stringскладається з копій CharSequenceелементів, об'єднаних разом з копією зазначеного роздільника. Наприклад:

 Set<String> strings = new LinkedHashSet<>();
 strings.add("Java"); strings.add("is");
 strings.add("very"); strings.add("cool");
 String message = String.join("-", strings);
 //message returned is: "Java-is-very-cool"

Setзнаряддя Iterable, тому просто використовуйте:

String.join(" ", set_1);

21

Як контрапункт відповіді Seanizer commons-lang, якщо ви використовуєте бібліотеки гуави Google (яку я б багато в чому вважав "наступницею" commons-lang), ви використовуєте Joiner :

Joiner.on(" ").join(set_1);

з перевагою декількох допоміжних методів, щоб зробити такі речі, як:

Joiner.on(" ").skipNulls().join(set_1);
// If 2nd item was null, would produce "1, 3"

або

Joiner.on(" ").useForNull("<unknown>").join(set_1);
// If 2nd item was null, would produce "1, <unknown>, 3"

Він також підтримує пряме додавання до StringBuilders та Writers та інші подібні приємності.


У чому різниця між бібліотеками гуави та бібліотекою колекцій Google?
Шервін Асгарі,

головна відмінність полягає в тому, що гуава знає про загальні засоби, а спільноти / колекції - ні, але крім цього: це дві різні бібліотеки, написані двома різними групами, які вирішують деякі подібні проблеми (і деякі не схожі) з використанням різних підходів
Шон Patrick Floyd

2
@seanizer, Шервін запитував про гуаву проти google-колекцій, а не про гуаву проти спільного :) Шервін - гуава - це просто заміна google-колекцій. По мірі збільшення масштабу проекту він перестав обмежуватися лише колекціями, тому зміна назви була в порядку. google-колекції в основному слід вважати застарілими, guava - це заміна, яка виправляється помилками та іншими можливостями.
Cowan

7

У мене немає бібліотеки StringUtil (у мене немає вибору), тому, використовуючи стандартну Java, я придумав це ..

Якщо ви впевнені, що дані набору не включатимуть коми або квадратні дужки, ви можете використовувати:

mySet.toString().replaceAll("\\[|\\]","").replaceAll(","," ");

Набір "a", "b", "c" перетворює через .toString () у рядок "[a, b, c]".

Потім зайві розділові знаки замініть як необхідні.

Бруд.


2
Приємно! Завжди круто дивитись, що ти можеш зробити зі стандартними бібліотеками JDK :)
Ларс Андрен

чисто, просто, не залежить від бібліотек, отже, не додає ваги додатку - любіть це!
Lukas Novicky

1
... до тих пір, поки у вашому наборі, звичайно, немає "[" або "]" або ",";)
Теділ

Я вже згадав коми, але ви маєте рацію щодо [та] також. Відповідь оновлено.
user2808054

1
Деякі люди, на жаль, не мають Java8 + і не мають легкого доступу до бібліотек. Такі відповіді дуже корисні для цих людей.
lewdev

6

Можливо коротше рішення:

public String test78 (Set<String> set) {
    return set
        .stream()
        .collect(Collectors.joining(" "));
}

або

public String test77 (Set<String> set) {
    return set
        .stream()
        .reduce("", (a,b)->(a + " " + b));
}

але рідний, безумовно швидший

public String test76 (Set<String> set) {
    return String.join(" ", set);
}

4

Я використовую цей метод:

public static String join(Set<String> set, String sep) {
    String result = null;
    if(set != null) {
        StringBuilder sb = new StringBuilder();
        Iterator<String> it = set.iterator();
        if(it.hasNext()) {
            sb.append(it.next());
        }
        while(it.hasNext()) {
            sb.append(sep).append(it.next());
        }
        result = sb.toString();
    }
    return result;
}

2

Мене бентежить реплікація коду, чому б не розбити його на функцію, яка приймає один набір і повертає один рядок?

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

Для цього також є функції бібліотеки, але я сумніваюся, що вони значно ефективніші.


Дякую за вашу відповідь! Я не виділяв це в окрему функцію, щоб виділити той факт, що я намагаюся повторно використати змінну 'builder'. Може, це не має значення?
Ларс Андрен,

3
Це не має значення. Переназначення змінних насправді може призвести до неприємностей. Натомість просто вимкніть змінні, коли вони більше не потрібні.
Gunslinger47

@Gunslinger, бачу, дякую! @Uri, дякую за початкові поради щодо місткості.
Ларс Андрен

1
@Lars, бувають випадки, коли повторне використання має сенс, але в інших краще скидати і починати з нуля. Я не впевнений, що тут краще. Ви можете написати клас утиліти, який має спільний конструктор змінних екземпляра. Однією з переваг одного конструктора за пробіг є те, що ви можете перетворювати велику кількість наборів паралельно з кількома потоками.
Урі

2

Це можна зробити, створивши потік з набору, а потім комбінуючи елементи, використовуючи операцію зменшення, як показано нижче (докладніше про потоки Java 8 див. Тут ):

Optional<String> joinedString = set1.stream().reduce(new 
BinaryOperator<String>() {

     @Override
     public String apply(String t, String u) {

       return t + " " + u;
    }
});
return joinedString.orElse("");
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.