Соединять строки с разными разделителями в 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")));

Дает: один, два, три и четыре

Другие вопросы по тегам