2

This is profoundly simple, yet I've seen many variants, and I don't see my own practice much elsewhere. Given:

Iterable<String> strings;

create a String containing all the strings, separated by commas.

Ed Staub
  • 15,480
  • 3
  • 61
  • 91

5 Answers5

7

Java-8 version

With Java 8, luckily, there is finally a standard way to do this:

String string = StreamSupport.stream(strings.spliterator(), false)
                             .collect(Collectors.joining(", "));

Unfortunately, there's not an easier way to get from an Iterable to a Stream (yet)

Pre Java-8 version

Use

org.apache.commons.lang.StringUtils.join(Iterator iterator, String separator)

From

http://commons.apache.org/lang/

If you want to do it yourself, I sometimes do

StringBuilder sb = new StringBuilder();
String separator = "";
for (String string : iterable) {
  sb.append(separator);
  sb.append(string);
  separator = ", ";
}
Community
  • 1
  • 1
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
5
final StringBuilder sb = new StringBuilder();
final Iterator<String> it = iterable.iterator();
if(it.hasNext()){
  sb.append(it.next());
  while(it.hasNext()){
    sb.append(',').append(it.next());
  }
}
return sb.toString();

but of course I would always prefer to use Guava's version

String joined = Joiner.on(',').join(iterable);

(To be fair: I have previously asked a very similar question)

Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • +1, For using a simple construct like `StringBuilder` and not being implementation-dependent. – mre Jul 12 '11 at 14:11
  • FWIW, I prefer Lukas's simpler hand-implementation because of its simplicity (it's what I use), but prefer Guava's Joiner over Apache StringUtils. – Ed Staub Jul 12 '11 at 14:35
  • @Ed I just don't like to re-assign variables. I make them final whenever I can (added final to post now). – Sean Patrick Floyd Jul 12 '11 at 14:37
  • @Sean - why - for clarity? If you final everything else, and in the middle there's a non-final `String separator = "";`, it very strongly hints that it will change, from both the non-final and the empty-string value. Or are you just trying to be as "functional" as possible? – Ed Staub Jul 12 '11 at 14:45
  • @Ed. Guava has some nice features. I might switch, too, since commons is a bit rusty, also with generics... – Lukas Eder Jul 12 '11 at 14:46
  • @Sean: weird. With really similar answers too! :) – Lukas Eder Jul 12 '11 at 14:47
  • @Ed *are you just trying to be as "functional" as possible?* whenever I can, yes. I have made the experience that it leads to more understandable and reusable code. – Sean Patrick Floyd Jul 12 '11 at 14:48
1

If you use Java 8 or higher, you can use String.join.

Iterable<String> strings = Arrays.asList("a", "b", "c");
String result = String.join(",", strings);
Assert.assertEquals("a,b,c", result);

If you use Eclipse Collections, you can use the Iterate utility class which works with any Iterable.

Iterable<String> strings = Arrays.asList("a", "b", "c");
String result = Iterate.makeString(strings, ",");
Assert.assertEquals("a,b,c", result);

Eclipse Collections 7.x works with Java 5 or higher. Eclipse Collections 8.x-9.x works with Java 8 or higher.

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44
1

Here's the join function I include in most projects:

public static String join(Iterable<? extends Object> iterable, String delimiter) {
    return join(iterable, delimiter, "", "");
}

public static String join(Iterable<? extends Object> iterable, String delimiter,
        String prepend, String append) {

    StringBuilder buffer = new StringBuilder();
    Iterator<?> iterator = iterable.iterator();
    boolean hasNext = iterator.hasNext();

    while (hasNext) {

        buffer.append(prepend);
        buffer.append(String.valueOf(iterator.next()));
        buffer.append(append);

        if (iterator.hasNext()) {
            buffer.append(delimiter);
        } else {
            hasNext = false;
        }
    }

    return buffer.toString();
}

Some examples:

String[] list = { "hello", "mellow", "jello" };

Util.join(list, ",");                    // "hello,mellow,jello"
Util.join(list, ", and ");               // "hello, and mellow, and jello"
Util.join(list, "&", "x=", "1");         // "x=hello1&x=mellow1&x=jello1"
Util.join(list, " OR ", "(Title:", ")"); // "(Title:hello) OR (Title:mellow) OR (Title:jello)"
epalm
  • 4,283
  • 4
  • 43
  • 65
  • Why not use one of the various `org.apache.commons.lang.StringUtils.join()` – Lukas Eder Jul 12 '11 at 14:12
  • @Lukas, I would think it's because some developers prefer to roll their own utility methods rather than rely on a third-party library. – mre Jul 12 '11 at 14:23
  • @Lukas, I considered it, but `StringUtils.join` didn't (and still doesn't) have the prepend/append functionality I needed. – epalm Jul 12 '11 at 14:37
  • @epalm: OK, true. I wasn't noticing your third and fourth examples. That makes sense – Lukas Eder Jul 12 '11 at 14:45
  • @little bunny Yes, but after the fifth project where you had to write the same StringUtils class again from scratch, you prefer to take an existing one. – Sean Patrick Floyd Jul 12 '11 at 15:07
  • 2
    @Sean, or the one you wrote yourself four projects ago! – epalm Jul 12 '11 at 15:10
  • @epalm unfortunately I'm talking about projects for different employers, so I wouldn't have the right to re-use the code. But anyway, I've been there about 3 years ago, I'm not re-inventing wheels anymore – Sean Patrick Floyd Jul 12 '11 at 15:16
1

Just for fun, a recursive implementation:

public static void main(String[] args) {
    System.out.println(buildCsv(Arrays.asList(new String[] {"one", "two", "three"}).iterator()));
}

private static String buildCsv(Iterator<String> iterator) {
    return iterator.hasNext() ? iterator.next() + (iterator.hasNext() ? "," : "") + buildCsv(iterator) : "";
}
Adriaan Koster
  • 15,870
  • 5
  • 45
  • 60