web-dev-qa-db-fra.com

La manière la plus sophistiquée de créer des chaînes séparées par des virgules à partir d'une collection / groupe / liste?

Au cours de mon travail avec les bases de données, j'ai remarqué que j'écrivais des chaînes de requête et, dans cette chaîne, je devais mettre plusieurs restrictions dans la clause where d'une liste/tableau/collection. Devrait ressembler à ceci:

select * from customer 
where customer.id in (34, 26, ..., 2);

Vous pouvez simplifier ceci en réduisant ceci à la question que vous avez une collection de chaînes et que vous souhaitez créer une liste séparée par des virgules de ces chaînes en une seule chaîne.

Mon approche que j'ai utilisée jusqu'ici ressemble à ceci:

String result = "";
boolean first = true;
for(String string : collectionOfStrings) {
    if(first) {
        result+=string;
        first=false;
    } else {
        result+=","+string;
    }
}

Mais c'est comme vous pouvez le voir très moche. Vous ne pouvez pas voir ce qui se passe au premier abord, en particulier lorsque les chaînes construites (comme toutes les requêtes SQL) se compliquent.

Quelle est votre manière (plus) élégante?

97
maerch

Les chaînes étant immuables, vous pouvez utiliser la classe StringBuilder si vous souhaitez modifier la chaîne dans le code.

La classe StringBuilder peut être vue comme un objet String mutable qui alloue plus de mémoire lorsque son contenu est modifié.

La suggestion initiale de la question peut être écrite encore plus clairement et efficacement, en prenant soin des virgule de fin redondante:

    StringBuilder result = new StringBuilder();
    for(String string : collectionOfStrings) {
        result.append(string);
        result.append(",");
    }
    return result.length() > 0 ? result.substring(0, result.length() - 1): "";
85
gimel

Utilisez la méthode Google Guava APIjoin :

Joiner.on(",").join(collectionOfStrings);
88
Julie

Je viens de regarder le code qui a fait cela aujourd'hui. Ceci est une variation de la réponse de AviewAnew.

collectionOfStrings = /* source string collection */;
String csList = StringUtils.join(collectionOfStrings.toArray(), ",");

Le StringUtils (<- commons.lang 2.x, ou lien commons.lang 3.x ) que nous avons utilisé provient de Apache Commons .

74
Ogre Psalm33

La façon dont j'écris cette boucle est la suivante:

StringBuilder buff = new StringBuilder();
String sep = "";
for (String str : strs) {
    buff.append(sep);
    buff.append(str);
    sep = ",";
}
return buff.toString();

Ne vous inquiétez pas de la performance de sep. Une affectation est très rapide. De toute façon, Hotspot a tendance à décoller de la première itération d'une boucle (car il doit souvent faire face à des bizarreries telles que les vérifications de ligne null et mono/bimorphique).

Si vous l'utilisez beaucoup (plus d'une fois), mettez-le dans une méthode partagée.

Il y a une autre question sur stackoverflow concernant l'insertion d'une liste d'identifiants dans une instruction SQL.

47

Depuis Java 8, vous pouvez utiliser:

40
Abdull

J'ai trouvé l'idiome de l'itérateur élégant, car il comporte un test pour plusieurs éléments (test null ompt/empty pour la brièveté):

public static String convert(List<String> list) {
    String res = "";
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
        res += iterator.next() + (iterator.hasNext() ? "," : "");
    }
    return res;
}
11
Miguel Ping

Il y a beaucoup de solutions manuelles à cela, mais je voulais répéter et mettre à jour la réponse de Julie ci-dessus. Utilisez classe de menuiserie Google Collections .

Joiner.on(", ").join(34, 26, ..., 2)

Il gère les arguments var, iterables et les tableaux et gère correctement les séparateurs de plus d'un caractère (contrairement à la réponse de gimmel). Il gérera également les valeurs NULL dans votre liste si vous en avez besoin.

8
case nelson

Voici une version incroyablement générique que j'ai construite à partir d'une combinaison des suggestions précédentes:

public static <T> String buildCommaSeparatedString(Collection<T> values) {
    if (values==null || values.isEmpty()) return "";
    StringBuilder result = new StringBuilder();
    for (T val : values) {
        result.append(val);
        result.append(",");
    }
    return result.substring(0, result.length() - 1);
}
7
Jeff
String.join(", ", collectionOfStrings)

disponible dans l'api Java8.

alternative à (sans la nécessité d’ajouter une dépendance à Google Guava):

Joiner.on(",").join(collectionOfStrings);
7
robjwilkins

Tu pourrais essayer

List collections = Arrays.asList(34, 26, "...", 2);
String asString = collection.toString();
// justValues = "34, 26, ..., 2"
String justValues = asString.substring(1, asString.length()-1);
5
Peter Lawrey

Ce sera la solution la plus courte à ce jour, excepté l’utilisation de Guava ou d’Apache Commons.

String res = "";
for (String i : values) {
    res += res.isEmpty() ? i : ","+i;
}

Bon avec 0,1 et n liste d'éléments. Mais vous devrez vérifier la liste NULL. J'utilise ceci dans GWT, donc je suis bon sans StringBuilder. Et pour les listes courtes avec seulement quelques éléments, ça va aussi ailleurs;)

4
iTake

Dans Android vous devriez utiliser ceci:

TextUtils.join(",",collectionOfStrings.toArray());
4
Pascalius

Au cas où quelqu'un tomberait sur cela plus récemment, j'ai ajouté une variante simple utilisant Java 8 reduce()]. Il inclut également certaines des solutions déjà mentionnées par d'autres:

import Java.util.Arrays;
import Java.util.List;

import org.Apache.commons.lang.StringUtils;    

import com.google.common.base.Joiner;

public class Dummy {
  public static void main(String[] args) {

    List<String> strings = Arrays.asList("abc", "de", "fg");
    String commaSeparated = strings
        .stream()
        .reduce((s1, s2) -> {return s1 + "," + s2; })
        .get();

    System.out.println(commaSeparated);

    System.out.println(Joiner.on(',').join(strings));

    System.out.println(StringUtils.join(strings, ","));

  }
}
4
Christof

Je pense que ce n’est pas une bonne idée de structurer le SQL concaténant les valeurs de la clause where comme vous le faites:

SELECT.... FROM.... WHERE ID IN( value1, value2,....valueN)

valueX provient d'une liste de chaînes.

Premièrement, si vous comparez des chaînes, elles doivent être citées. Ce n'est pas anodin si les chaînes peuvent contenir une citation.

Deuxièmement, si les valeurs proviennent de l'utilisateur, ou d'un autre système, une attaque par injection SQL est possible.

C'est beaucoup plus bavard, mais vous devez créer une chaîne comme ceci:

SELECT.... FROM.... WHERE ID IN( ?, ?,....?)

et ensuite lier les variables avec Statement.setString(nParameter,parameterValue).

4
Telcontar

Juste une autre méthode pour traiter ce problème. Pas le plus court, mais c'est efficace et fait le travail.

/**
 * Creates a comma-separated list of values from given collection.
 * 
 * @param <T> Value type.
 * @param values Value collection.
 * @return Comma-separated String of values.
 */
public <T> String toParameterList(Collection<T> values) {
   if (values == null || values.isEmpty()) {
      return ""; // Depending on how you want to deal with this case...
   }
   StringBuilder result = new StringBuilder();
   Iterator<T> i = values.iterator();
   result.append(i.next().toString());
   while (i.hasNext()) {
      result.append(",").append(i.next().toString());
   }
   return result.toString();
}
3
silverminken

Si vous utilisez Spring, vous pouvez faire:

StringUtils.arrayToCommaDelimitedString(
    collectionOfStrings.toArray()
)

(package org.springframework.util)

2
weekens

Certaines bibliothèques tierces Java) fournissent une méthode de jointure de chaîne, mais vous ne voulez probablement pas commencer à utiliser une bibliothèque pour quelque chose d'aussi simple. Je créerais simplement une méthode d'assistance telle que Je pense que cela, qui est un peu meilleur que votre version, utilise StringBuffer, ce qui sera plus efficace si vous devez joindre plusieurs chaînes, et cela fonctionne sur une collection de tout type.

public static <T> String join(Collection<T> values)
{
    StringBuffer ret = new StringBuffer();
    for (T value : values)
    {
        if (ret.length() > 0) ret.append(",");
        ret.append(value);
    }
    return ret.toString();
}

Une autre suggestion consistant à utiliser Collection.toString () est plus courte, mais elle repose sur le fait que Collection.toString () renvoie une chaîne dans un format très spécifique, sur lequel je ne voudrais personnellement pas compter.

2
Denis Fradlin

si vous avez un tableau, vous pouvez faire:

Arrays.asList(parameters).toString()
1
cloudy_weather

Je ne sais pas à quel point cela est "sophistiqué", mais c'est certainement un peu plus court. Cela fonctionnera avec différents types de collection, par ex. Définir <Integer>, List <String>, etc.

public static final String toSqlList(Collection<?> values) {

    String collectionString = values.toString();

    // Convert the square brackets produced by Collection.toString() to round brackets used by SQL
    return "(" + collectionString.substring(1, collectionString.length() - 1) + ")";
}

Exercice pour le lecteur: modifiez cette méthode pour qu'elle gère correctement une collection null/empty :)

1
Dónal

Une autre option, basée sur ce que je vois ici (avec de légères modifications).

public static String toString(int[] numbers) {
    StringBuilder res = new StringBuilder();
    for (int number : numbers) {
        if (res.length() != 0) {
            res.append(',');
        }
        res.append(number);
    }
    return res.toString();
}
1
elcuco

Je viens de faire un test pour ma bibliothèque dollar :

@Test
public void join() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    String string = $(list).join(",");
}

il crée un wrapper fluide autour des listes/tableaux/chaînes/etc en utilisant une seule importation statique : $.

[~ # ~] nb [~ # ~] :

en utilisant les gammes, la liste précédente peut être réécrite en tant que $(1, 5).join(",")

1
dfa

Ce qui rend le code laid, c'est le traitement spécial pour le premier cas. La plupart des lignes de ce petit extrait sont consacrées non pas à l'exécution du travail de routine du code, mais à la gestion de ce cas particulier. Et c’est ce que les solutions alternatives comme Gimel résolvent, en déplaçant la manipulation spéciale en dehors de la boucle. Il y a un cas particulier (bon, vous pouvez voir le début et la fin comme des cas particuliers - mais un seul d'entre eux doit être traité de manière spéciale), aussi sa manipulation dans la boucle est-elle inutilement compliquée.

1
Carl Manaster

Les méthodes de jointure sont disponibles dans les tableaux et les classes qui étendent la méthode AbstractCollections mais ne remplacent pas la méthode toString() (comme pratiquement toutes les collections de Java.util).

Par exemple:

String s= Java.util.Arrays.toString(collectionOfStrings.toArray());
s = s.substing(1, s.length()-1);// [] are guaranteed to be there

C'est assez bizarre, car cela ne fonctionne que pour les nombres comme pour les données SQL.

1
xss

Bien que je pense que votre meilleur pari est d'utiliser Joiner de Guava, si je devais le coder à la main, je trouverais cette approche plus élégante que le "premier" drapeau ou la dernière virgule coupée.

private String commas(Iterable<String> strings) {
    StringBuilder buffer = new StringBuilder();
    Iterator<String> it = strings.iterator();
    if (it.hasNext()) {
        buffer.append(it.next());
        while (it.hasNext()) {
            buffer.append(',');
            buffer.append(it.next());
        }
    }

    return buffer.toString();
}
1
Victor

La bonne chose à propos de l'expression IN est que si vous avez répété des valeurs, cela ne change pas le résultat. Donc, il suffit de dupliquer le premier élément et de traiter la liste entière. Cela suppose qu’il existe au moins un élément dans la liste. S'il n'y a pas d'éléments, je suggérerais de vérifier d'abord et ensuite de ne pas exécuter le code SQL du tout.

Cela fera l'affaire, est évident dans ce qu'il fait et ne repose sur aucune bibliothèque externe:

StringBuffer inString = new StringBuffer(listOfIDs.get(0).toString());
for (Long currentID : listOfIDs) {
  inString.append(",").append(currentID);
}
1
VIM

Vous pourrez peut-être utiliser LINQ (en SQL) et utiliser l'exemple Dynamic Query LINQ de MS. http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

0
GregUzelac

Liste de jeton = new ArrayList (result); constructeur final de StringBuilder = new StringBuilder ();

    for (int i =0; i < tokens.size(); i++){
        builder.append(tokens.get(i));
        if(i != tokens.size()-1){
            builder.append(TOKEN_DELIMITER);
        }
    }

builder.toString ();

0
Ups

La méthode la plus simple dans Android pour convertir une liste en chaînes séparées par des virgules) consiste à utiliser Android.text.TextUtils

 ArrayList<String>Myli = new ArrayList<String>();
 String ArayCommase=Android.text.TextUtils.join(",", Myli);
0
Jayman Jani

Il y a un moyen facile. Vous pouvez obtenir votre résultat en une seule ligne.

String memberIdsModifiedForQuery = memberIds.toString().replace("[", "(").replace("]", ")");

Pour avoir une idée complète, vérifiez le code ci-dessous

 public static void main(String[] args) {       
        List<Integer>memberIds=new ArrayList<Integer>();  //This contain member ids we want to process
        //adding some sample values for example
        memberIds.add(3); 
        memberIds.add(4);
        memberIds.add(2);
        String memberIdsModifiedForQuery = memberIds.toString().replace("[", "(").replace("]", ")"); //here you will get (3,4,5) That you can directly use in query
        System.out.println(memberIdsModifiedForQuery);
        String exampleQuery="select * from customer where customer.id in "+memberIdsModifiedForQuery+" ";
    }
0
Fathah Rehman P
String commaSeparatedNames = namesList.toString().replaceAll( "[\\[|\\]| ]", "" );  // replace [ or ] or blank

La représentation sous forme de chaîne consiste en une liste des éléments de la collection dans l'ordre dans lequel ils sont renvoyés par son itérateur, placés entre crochets ("[]"). Les éléments adjacents sont séparés par les caractères "," (virgule et espace).

AbstractCollection javadoc

0
Todd Gatts
Java.util.List<String> lista = new Java.util.ArrayList<String>();
lista.add("Hola");
lista.add("Julio");
System.out.println(lista.toString().replace('[','(').replace(']',')'));

$~(Hola, Julio)
0
Julio César