103

During my work with databases I noticed that I write query strings and in this strings I have to put several restrictions in the where-clause from a list/array/collection. Should look like this:

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

You can simplify this by reducing this to the question that you have collection of strings and want to create a comma-separated list of this strings in just one string.

My approach I have used so far is something like that:

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

But this is as you can see very ugly. You cannot see what happens there on the first look, especially when the constructed strings (like every SQL query) is getting complicated.

What is your (more) elegant way?

bluish
  • 26,356
  • 27
  • 122
  • 180
maerch
  • 2,055
  • 3
  • 17
  • 24
  • Presumably the SQL shown above should actually look like this: select * from customer where customer.id in (34, 26, 2); – Dónal Oct 15 '08 at 17:27
  • There is a tricky part, when list items (strings) themselves contain commas or double-quotes and they need to be escaped with quotes. If I did not miss anything, the examples above do not consider it and I hate the idea of looping through all the texts and searching for commas.. Do you thing there is a better way of solving this? – Samurai Girl Feb 01 '12 at 12:18
  • check this answer out... http://stackoverflow.com/a/15815631/728610 – Arvind Sridharan Aug 16 '13 at 08:52
  • Have you ever checked http://stackoverflow.com/questions/10850753/how-to-convert-a-liststring-into-a-comma-separated-string-without-iterating-li/29029927#29029927 ? – Hiren Patel Mar 13 '15 at 10:30
  • This ought to do it. http://stackoverflow.com/a/15815631/3157062 – Parag Jadhav Jul 09 '16 at 08:00

32 Answers32

91

Use the Google Guava API's join method:

Joiner.on(",").join(collectionOfStrings);
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Julie
  • 6,221
  • 3
  • 31
  • 37
  • 4
    Nowadays the class is called `Joiner`; http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Joiner.html – Jonik Nov 04 '09 at 22:10
  • 2
    And today, Collections is deprecated. Use [Google Guava](http://code.google.com/p/guava-libraries/) instead. – darioo Oct 07 '11 at 14:13
  • 12
    Meanwhile, org.apache.commons.lang.StringUtils remains unchanged. :-) – Ogre Psalm33 Mar 28 '12 at 19:42
  • 1
    guava has moved. see https://github.com/google/guava/wiki/StringsExplained – gimel Aug 03 '16 at 16:08
88

Note: This answers was good when it was written 11 years ago, but now there are far better options to do this more cleanly in a single line, both using only Java built-in classes or using a utility library. See other answers below.


Since strings are immutable, you may want to use the StringBuilder class if you're going to alter the String in the code.

The StringBuilder class can be seen as a mutable String object which allocates more memory when its content is altered.

The original suggestion in the question can be written even more clearly and efficiently, by taking care of the redundant trailing comma:

    StringBuilder result = new StringBuilder();
    for(String string : collectionOfStrings) {
        result.append(string);
        result.append(",");
    }
    return result.length() > 0 ? result.substring(0, result.length() - 1): "";
Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
gimel
  • 83,368
  • 10
  • 76
  • 104
  • 7
    Note that this requires your collection to have at least one element. – Guus Dec 21 '10 at 13:59
  • 3
    See top voted answer - http://code.google.com/p/guava-libraries/wiki/StringsExplained – gimel Mar 10 '12 at 10:23
  • See suggested fix for an empty list. – gimel Jun 21 '12 at 05:07
  • 1
    the guava answer is better. no need to reinvent the wheel. – davidjnelson Nov 15 '12 at 19:34
  • @davidjnelson maybe guava answer is better in practice, but this one shows how this really works. By the way, @gimel, I think you don't need `StringBuilder` here at all. You can use normal `String` and replace the appender method by `+=`. – Aritz Mar 15 '13 at 08:56
  • 1
    @xtreme-biker With a reasonably modern compiler, StringBuilder might be used automatically. Check your environment before using += . See http://stackoverflow.com/questions/1532461/stringbuilder-vs-string-concatenation-in-tostring-in-java – gimel Mar 15 '13 at 09:46
  • For better performance, it's recommended to append comma as a char rather than as a String: use result.append(','); instead of using result.append(","); – bolei May 05 '16 at 21:25
  • Dislike this practice of forming an incorrect result (redundant trailing separator) and then "fixing it up" -- it's highly complex & a recipe for developer/ coding errors. Better approaches are to use a boolean 'first' flag or Tom Hawtin's answer https://stackoverflow.com/a/205976/768795. – Thomas W Jun 09 '17 at 00:51
  • The worst part of this answer is always appending the common and then having to remove the last one. The Guava Joiner does pretty much the same thing as this code, but uses an initial `if` to conditionally loop through the rest of the items and add commas before each (not after). – Samuel Neff Jan 03 '20 at 21:04
79

I just looked at code that did this today. This is a variation on AviewAnew's answer.

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

The StringUtils ( <-- commons.lang 2.x, or commons.lang 3.x link) we used is from Apache Commons.

Ogre Psalm33
  • 21,366
  • 16
  • 74
  • 92
  • ...and where does StringUtils come from? – vwegert Sep 19 '10 at 17:18
  • 1
    Ah, good point. Been a while since I looked at that code, but I believe we were using org.apache.commons.lang.StringUtils. – Ogre Psalm33 Sep 20 '10 at 18:28
  • Here's a live link to the join method of StringUtils http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringUtils.html#join(char[],%20char) – Ryan S Jun 14 '14 at 21:52
  • 2
    Nice, thanks. StringUtils#join also works on an Iterable, so there's probably no need to convert your collection to an array first. – Roy Apr 21 '15 at 07:47
48

The way I write that loop is:

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

Don't worry about the performance of sep. An assignment is very fast. Hotspot tends to peel off the first iteration of a loop anyway (as it often has to deal with oddities such as null and mono/bimorphic inlining checks).

If you use it lots (more than once), put it in a shared method.

There is another question on stackoverflow dealing with how to insert a list of ids into an SQL statement.

bluish
  • 26,356
  • 27
  • 122
  • 180
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
47

Since Java 8, you can use:

RamonBoza
  • 8,898
  • 6
  • 36
  • 48
Abdull
  • 26,371
  • 26
  • 130
  • 172
  • 3
    This is good! If you are manipulating objects that need a special string conversion not covered by toString(), replace the Object::toString with a java.util.function.Function that maps your class to a String. – Torben Oct 15 '15 at 06:54
  • 2
    Also, you can use it like this: `cats.stream().map(cat -> cat.getName()).collect(Collectors.joining(","));` for a single variable from your collection. – numsu Aug 18 '16 at 08:16
  • I wonder about the performance of that `stream`. For int[] or long[] or other arrays where the value can simply be cast to `String`, I'd look for a non-streaming solution. In fact I am looking. – Adam Nov 06 '17 at 12:11
12

I found the iterator idiom elegant, because it has a test for more elements (ommited null/empty test for brevity):

public static String convert(List<String> list) {
    String res = "";
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
        res += iterator.next() + (iterator.hasNext() ? "," : "");
    }
    return res;
}
Miguel Ping
  • 18,082
  • 23
  • 88
  • 136
  • ... and probably less efficient that the accepted solution, depending on how complex the 'hasNext()' call is. Besides, you should probably be using a StringBuilder rather than String concatenation. – Stephen C Jul 23 '09 at 23:06
  • OK, if you want to be picky about efficiency, use a StringWriter ;) – Miguel Ping Jul 14 '11 at 16:48
8

There's a lot of manual solutions to this, but I wanted to reiterate and update Julie's answer above. Use google collections Joiner class.

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

It handles var args, iterables and arrays and properly handles separators of more than one char (unlike gimmel's answer). It will also handle null values in your list if you need it to.

sth
  • 222,467
  • 53
  • 283
  • 367
case nelson
  • 3,537
  • 3
  • 30
  • 37
8
String.join(", ", collectionOfStrings)

available in the Java8 api.

alternative to (without the need to add a google guava dependency):

Joiner.on(",").join(collectionOfStrings);
robjwilkins
  • 5,462
  • 5
  • 43
  • 59
7

Here's an incredibly generic version that I've built from a combination of the previous suggestions:

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);
}
Jeff
  • 71
  • 1
  • 1
5

You could try

List collections = Arrays.asList(34, 26, "...", 2);
String asString = collection.toString();
// justValues = "34, 26, ..., 2"
String justValues = asString.substring(1, asString.length()-1);
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
4

This will be the shortest solution so far, except of using Guava or Apache Commons

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

Good with 0,1 and n element list. But you'll need to check for null list. I use this in GWT, so I'm good without StringBuilder there. And for short lists with just couple of elements its ok too elsewhere ;)

iTake
  • 4,082
  • 3
  • 33
  • 26
4

I think it's not a good idea contruct the sql concatenating the where clause values like you are doing :

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

Where valueX comes from a list of Strings.

First, if you are comparing Strings they must be quoted, an this it isn't trivial if the Strings could have a quote inside.

Second, if the values comes from the user,or other system, then a SQL injection attack is possible.

It's a lot more verbose but what you should do is create a String like this:

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

and then bind the variables with Statement.setString(nParameter,parameterValue).

Telcontar
  • 4,794
  • 7
  • 31
  • 39
4

In case someone stumbled over this in more recent times, I have added a simple variation using Java 8 reduce(). It also includes some of the already mentioned solutions by others:

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, ","));

  }
}
Christof
  • 779
  • 8
  • 21
4

In Android you should use this:

TextUtils.join(",",collectionOfStrings.toArray());
Pascalius
  • 14,024
  • 4
  • 40
  • 38
3

Just another method to deal with this problem. Not the most short, but it is efficient and gets the job done.

/**
 * 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();
}
2

There are some third-party Java libraries that provide string join method, but you probably don't want to start using a library just for something simple like that. I would just create a helper method like this, which I think is a bit better than your version, It uses StringBuffer, which will be more efficient if you need to join many strings, and it works on a collection of any 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();
}

Another suggestion with using Collection.toString() is shorter, but that relies on Collection.toString() returning a string in a very specific format, which I would personally not want to rely on.

Denis Fradlin
  • 376
  • 2
  • 8
2
List<String> collectionOfStrings = // List of string to concat
String csvStrings = StringUtils.collectionToDelimitedString(collectionOfStrings, ",");

StringUtils from springframeowrk:spring-core

Sridhar
  • 23
  • 3
2

If you use Spring, you can do:

StringUtils.arrayToCommaDelimitedString(
    collectionOfStrings.toArray()
)

(package org.springframework.util)

weekens
  • 8,064
  • 6
  • 45
  • 62
1

While I think your best bet is to use Joiner from Guava, if I were to code it by hand I find this approach more elegant that the 'first' flag or chopping the last comma off.

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();
}
Victor
  • 3,395
  • 1
  • 23
  • 26
1

if you have an array you can do:

Arrays.asList(parameters).toString()
cloudy_weather
  • 2,837
  • 12
  • 38
  • 63
1

I'm not sure how "sophisticated" this is, but it's certainly a bit shorter. It will work with various different types of collection e.g. Set<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) + ")";
}

Exercise for reader: modify this method so that it correctly handles a null/empty collection :)

Dónal
  • 185,044
  • 174
  • 569
  • 824
1

Join 'methods' are available in Arrays and the classes that extend AbstractCollections but doesn't override toString() method (like virtually all collections in java.util).

For instance:

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

That's quite weird way since it works only for numbers alike data SQL wise.

xss
  • 31
  • 1
1

I've just checked-in a test for my library dollar:

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

it create a fluent wrapper around lists/arrays/strings/etc using only one static import: $.

NB:

using ranges the previous list can be re-writed as $(1, 5).join(",")

dfa
  • 114,442
  • 31
  • 189
  • 228
1

Another option, based on what I see here (with slight 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();
}
elcuco
  • 8,948
  • 9
  • 47
  • 69
1

The nice thing about the IN expression is that if you have repeated values, it does not change the result. So, just duplicate the first item and process the entire list. This assumes that there is at least one item in the list. If there are no items, I'd suggest checking for that first and then not executing the SQL at all.

This will do the trick, is obvious in what it is doing and does not rely on any external libraries:

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

What makes the code ugly is the special-handling for the first case. Most of the lines in this small snippet are devoted, not to doing the code's routine job, but to handling that special case. And that's what alternatives like gimel's solve, by moving the special handling outside the loop. There is one special case (well, you could see both start and end as special cases - but only one of them needs to be treated specially), so handling it inside the loop is unnecessarily complicated.

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
0
String commaSeparatedNames = namesList.toString().replaceAll( "[\\[|\\]| ]", "" );  // replace [ or ] or blank

The string representation consists of a list of the collection's elements in the order they are returned by its iterator, enclosed in square brackets ("[]"). Adjacent elements are separated by the characters ", " (comma and space).

AbstractCollection javadoc

Fabien Sa
  • 9,135
  • 4
  • 37
  • 44
Todd Gatts
  • 11
  • 1
0

You may be able to use LINQ (to SQL), and you may be able to make use of the Dynamic Query LINQ sample from MS. http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

GregUzelac
  • 462
  • 2
  • 6
0

List token=new ArrayList(result); final StringBuilder builder = 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();

Ups
  • 13
  • 3
0

There is an easy way. You can get your result in a single line.

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

To get complete idea check the code below

 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+" ";
    }
Fathah Rehman P
  • 8,401
  • 4
  • 40
  • 42
0

The Most easier way in android for convert List to Comma separated String is By useing android.text.TextUtils

 ArrayList<String>Myli = new ArrayList<String>();
 String ArayCommase=android.text.TextUtils.join(",", Myli);
Jayman Jani
  • 1,251
  • 10
  • 15
0
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)
  • 1
    This is a bad practice. You can't make the assumption that the toString implementation changes. – drindt Aug 19 '15 at 08:27