7
votes

Comment rejoindre des éléments de liste, mais utilisez un délimiteur différent pour le dernier élément?

Liste donnée comme:

Liste noms = lists.NewarrayList ("George", "John", "Paul", "Ringo") Je voudrais transformer en une chaîne comme ceci:

George, John, Paul et Ringo

Je peux le faire avec plutôt maladroit Stringbuilder comme tellement: xxx

existe-t-il une approche un peu plus élégante? Cela ne me dérange pas d'utiliser une bibliothèque si nécessaire.

notes :

  • Je pourrais utiliser un ancien pour boucle avec un index, mais je ne cherche pas une telle solution
  • Il n'y a pas de virgule dans les valeurs (noms)

4 commentaires

Sont des virgules jamais dans les valeurs?


@Bohemian Nope (noté que dans la question)


Pourquoi ne pas encapsuler sa fonctionnalité dans une méthode externe? Quelque chose comme String Public String MixListofnamesAndrePlacAlastCommaMaPhOt (Liste Noms)


@Anatoly bien oui, mais maintenant nous parlons de la mise en œuvre


7 Réponses :


2
votes

Vous pouvez rejoindre tous les éléments sauf le dernier à l'aide d'un subliste:

static String joinList(List<String> names, String firstDelimiter, String secondDelimiter) {
    if (names.isEmpty()) {
         return "";
    } else if (names.size() == 1) {
         return names.get(0);
    } else {
        return names.subList(0, names.size() - 1)
                    .stream().collect(Collectors.joining(firstDelimiter))
             + secondDelimiter + names.get(names.size() - 1);
    }
}


9 commentaires

Serait délicat d'utiliser pour une liste vide et une liste de 1 élément seulement


@Xorty en cas d'une liste vide, renvoyez une chaîne vide;).


Dans le cas d'un élément, il retournerait "et George" au lieu de "George" (contrairement à la solution d'origine)


@XORTY Vous pouvez mettre des instructions si avant de renvoyer le résultat souhaité, vous savez? Parce que sur la base de votre théorie, ceci et l'autre solution lancera nullpointexception si les noms sont sont null et il n'y a pas de vérification nulle.


Et c'est bien :) Ce code fait exactement ce qu'il faut faire et il s'écrase exactement quand il devrait s'écraser. Je cherchais juste une manière plus élégante. Votre solution n'est pas mauvaise, mais elle nécessiterait un groupe de si -s dans ce scénario.


J'ai ajouté une implémentation avec un contrôle de flux plus explicite. Il peut être plus facile de comprendre ce qu'il fait de cette façon (et cela ne crée pas de sous-langue vide dans le cas à un élément unique).


@Rasmuskaj Je voulais écrire ce code mais OP répondit dans les commentaires: Je cherchais juste une manière plus élégante. Votre solution n'est pas mauvaise, mais cela nécessiterait des tas de si-S dans ce scénario J'ai donc essayé d'éviter tout si. En outre, tout programmeur décent peut comprendre ce que le code fait et la création d'un subliste vide ne fera pas de mal aux performances, du moins en utilisant les implémentations de la liste fournies par le JDK.


Utilisez-vous le mot "élégant" quand ils signifient "courte"? Pendant que je ressemble personnellement à la? : Opérateur, je refuse d'appeler n'importe quel code qui contient imbriqué? : Opérateurs "Élégant". :-)


@Rasmuskaj J'appelle Elegant au code de haute performance, malgré la rédaction si elle est si écrite que j'ai besoin d'une pierre de Rosetta pour le traduire de manière plus lisible et comprendre ce que le code fait. Mais pour OP, on dirait que le code élégant est plus lisible et plus court que d'autres choses.



0
votes

Si les virgules ne sont jamais dans les valeurs, c'est une doublure: xxx


2 commentaires

Il peut devenir un cauchemar à quelqu'un pour soutenir ce code :(


Pendant que nous sommes sur le sujet de soutenir ce code ... pourriez-vous casser la réponse et expliquer ses parties?



-2
votes

Vous pouvez avoir une fonction personnalisée pour ajouter le dernier délimiteur, mais pour le délimiteur entre vous pouvez utiliser stringutils.join () pour accomplir votre tâche.Vérifiez ce lien pour API


1 commentaires

Je n'ai même pas besoin de cela, je les ai déjà rejoints avec la nature Java 8 - voir Collect (jointure (",")) Partie



1
votes

Si cela ne vous dérange pas d'utiliser un itérateur, cela fonctionne:

private static String specialJoin(Iterable<?> list, String sep, String lastSep) {
    StringBuilder result = new StringBuilder();
    final Iterator<?> i = list.iterator();
    if (i.hasNext()) {
        result.append(i.next());
        while (i.hasNext()) {
            final Object next = i.next();
            result.append(i.hasNext() ? sep : lastSep);
            result.append(next);
        }
    }
    return result.toString();
}


3 commentaires

Cela peut être subjectif, mais je discuterais effectivement que le code d'origine est meilleur que celui-ci :)


C'est une solution élégante , car elle ne traverse pas la collection plus d'une fois et ne dépend pas du contenu de la dernière valeur de la liste. :-)


J'ai écrit une autre réponse en utilisant l'API des flux / collect. Mais dans la vie réelle, j'utiliserais cette méthode à base d'itérateur.



2
votes

Je ne suis pas sûr à quel point cela est élégant, mais cela fonctionne. La partie ennuyeuse est que vous devez inverser la liste code>. xxx pré>

Cependant, je pense que la meilleure solution est la suivante: P>

StringBuilder sb = new StringBuilder();
int n = list.size();
for (String string : list) {
    sb.append(string);
    if (--n > 0)
        sb.append(n == 1 ? " and " : ", ");
}
System.out.println(sb);


0 commentaires

2
votes

Comme vous l'avez déjà fait la plupart, j'introduisais une deuxième méthode "Remplacement" qui n'est pas dans la JDK pour Java.Lang.string jusqu'à présent:

@org.junit.Test
public void test() {
 List<String> names = Arrays.asList("George", "John", "Paul", "Ringo");
 assertEquals("George, John, Paul and Ringo", StringUtils.asLiteralNumeration(names));

 List<String> oneItemList = Arrays.asList("Paul");
 assertEquals("Paul", StringUtils.asLiteralNumeration(oneItemList));

 List<String> emptyList = Arrays.asList("");
 assertEquals("", StringUtils.asLiteralNumeration(emptyList));

}


2 commentaires

Pas mal, si je n'ai pas besoin de paramétrer les délimiteurs, cela peut être noms de retour.stream (). Recueillir (jointure (",")). RemplaceFirst ("(? S), (?!.! ,) "," et ");


Oui, bien sûr, vous pouvez le modifier à un one-liner :-)



1
votes

Voici une solution élégante à l'aide de l'API de flux: xxx pré>

malheur, cela dépend de cette fonction, qui pourrait être blanchi dans certaines catégories d'utilité: P>

class Ack {
    private StringBuilder result = null;
    private Object last;

    public void add(Object u, String sep) {
        if (last != null) {
            doAppend(sep, last);
        }
        last = u;
    }

    private void doAppend(String sep, Object t) {
        if (result == null) {
            result = new StringBuilder();
        } else {
            result.append(sep);
        }
        result.append(t);
    }

    public Ack merge(Ack other, String sep) {
        if (other.last != null) {
            doAppend(sep, last);
            if (other.result != null) {
                doAppend(sep, other.result);
            }
            last = other.last;
        }
        return this;
    }

    public String toString(String lastSep) {
        if (result == null) {
            return last == null ? "" : String.valueOf(last);
        }
        result.append(lastSep).append(last);
        return result.toString();
    }
}


4 commentaires

Un collecteur personnalisé est en effet une approche élégante. Maintenant, il suffit de goûter si la plupart des développeurs sont plus heureux de comprendre peu de lignes de concaténations à cordes ou de collectionneurs personnalisés.


C'est ma première tentative d'écrire un collecteur personnalisé, il peut probablement être amélioré sur ...


@Rasmuskaj Je pense que c'est vraiment bien. Cela fonctionne certainement. La seule amélioration que je ferais, c'est que toutes ces classes anonymes dans Naturalcollector peuvent être remplacées par Lambdas. Le code pour fournisseur () peut être juste retour ack :: nouveau; .


Merci pour l'indice, @pbabcdefp, j'ai mis à jour le code.