Соединять строки с разными разделителями в Java
У меня есть список строк, например, дни недели. И я хотел бы присоединиться к ним через запятую, с "и" перед последним элементом. Например: "Я доступен по вт, ср, чт и пт".
Что-то элегантное, как
Joiner.on(", ").join(days)
Не работает. Есть ли элегантный способ сделать это с Guava или подобной библиотекой?
Спасибо
4 ответа
Java 6/7 совместимое решение с использованием Guava:
static String join(List<String> strings, String separator, String lastSeparator)
{
checkNotNull(strings);
switch (strings.size()) {
case 0:
return "";
case 1:
return strings.get(0);
default:
return Joiner.on(separator).join(strings.subList(0, strings.size() - 1))
+ lastSeparator + strings.get(strings.size() - 1);
}
}
или если вы хотите принять Iterable
(и / или использовать больше гуавы):
static String join(Iterable<String> strings, String separator, String lastSeparator)
{
checkNotNull(strings);
int size = Iterables.size(strings);
switch (size) {
case 0:
return "";
case 1:
return strings.iterator().next();
default:
return Joiner.on(separator).join(Iterables.limit(strings, size - 1))
+ lastSeparator + Iterables.getLast(strings);
}
}
Он обрабатывает все крайние случаи, кроме нулевых элементов в strings
- если вы хотите справиться с ними, а не бросать NPE, добавьте .useForNull(stringForNull)
вашему столяру.
Нет простого решения, но вы можете рассмотреть мое:
final String COMMA = ", ", AND = " and ";
List<String> list = Arrays.asList("Tue", "Wed", "Thu", "Fri");
int last = list.size() - 1;
Joiner.on(AND).join(
list.stream().limit(last).collect(joining(COMMA)),
list.get(last)); // Tue, Wed, Thu and Fri
Другой более короткий путь:
list.stream().limit(last).collect(joining(COMMA, "", AND))
.concat(list.get(last));
Эти методы отлично работают на 2+ дня.
Краевые случаи (list == null || list.isEmpty()
а также list.size() < 2
) может обрабатываться if
заявления.
Учитывая, что вы "не дружите с Java 8 в данный момент" (вы, вероятно, имеете в виду лямбды и потоки), как насчет использования StringJoiner
:
public static String join(List<String> parts, String delimiter, String lastDelimiter) {
StringJoiner joiner = new StringJoiner(delimiter, "", lastDelimiter);
for (int i = 0; i < parts.size() - 1; i++) {
joiner.add(parts.get(i));
}
return joiner.toString() + parts.get(parts.size() - 1);
}
Однако, делать то же самое с потоками:
public static String join(List<String> parts, String delimiter, String lastDelimiter) {
return parts.stream()
.limit(parts.size() - 1)
.collect(Collectors.joining(delimiter, "", lastDelimiter))
.concat(parts.get(parts.size() - 1));
}
РЕДАКТИРОВАТЬ: только что нашел String#join(CharSequence, Iterable<? extends CharSequence>)
:
public static String join(List<String> parts, String delimiter, String lastDelimiter) {
return String.join(delimiter, parts.subList(0, parts.size() - 1))
+ lastDelimiter + parts.get(parts.size() - 1);
}
Чтобы справиться с угловыми случаями, я бы выбрал решение для переключения Xaerxess.
Из-за того, что у Java есть replaceFirst, но нет replaceLast и предполагается, что "_,_" не появится ни в одном из элементов / частей, я взломал следующую функцию:
public static String join(Iterable<?> parts) {
String reverse = new StringBuilder(Joiner.on("_,_").join(parts)).reverse().toString();
reverse = reverse.replaceFirst("_,_", " dna ").replaceAll("_,_", " ,");
return new StringBuilder(reverse).reverse().toString();
}
А потом:
System.out.println(join(Arrays.asList("One", "Two", "Three", "Four")));
Дает: один, два, три и четыре