580

See Related .NET question

I'm looking for a quick and easy way to do exactly the opposite of split so that it will cause ["a","b","c"] to become "a,b,c"

Iterating through an array requires either adding a condition (if this is not the last element, add the seperator) or using substring to remove the last separator.

I'm sure there is a certified, efficient way to do it (Apache Commons?)

How do you prefer doing it in your projects?

Community
  • 1
  • 1
Eran Medan
  • 44,555
  • 61
  • 184
  • 276

15 Answers15

917

Using Java 8 you can do this in a very clean way:

String.join(delimiter, elements);

This works in three ways:

1) directly specifying the elements

String joined1 = String.join(",", "a", "b", "c");

2) using arrays

String[] array = new String[] { "a", "b", "c" };
String joined2 = String.join(",", array);

3) using iterables

List<String> list = Arrays.asList(array);
String joined3 = String.join(",", list);
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • 8
    We should mention that this approach works **only** for `List` or `CharSequence[]` elements like list or arrays of `Strings`, `StringBuilder`. – Pshemo Apr 29 '15 at 10:36
  • @Pshemo That's already listed in the comments, it works if you use a varargs argument, arrays or an iterable – skiwi Apr 29 '15 at 10:52
  • 8
    OK emphasis in my previous comment was used incorrectly. My point is that we can use it only for `CharSequence` elements like String (as example in question) but it would be good to add info that this method will not work for elements like `Person`, `Car` where we need to explicitly call `toString`. – Pshemo Apr 29 '15 at 10:57
  • 143
    It took them 8 major versions to implement something this basic and useful? Wish I could down-vote Java for this. – Max Heiber Feb 05 '16 at 20:15
  • 1
    how does it work if one of elements is null or "" ?Just the delimiter will be returned?? It's not so good – svichkar Aug 19 '16 at 07:38
  • 3
    Unfortunatelly `Android Studio` does not fully support 1.8 only 1.7: http://stackoverflow.com/q/31528782/239219 – János Sep 17 '16 at 06:48
  • @svichkar Take a look [here](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#join-java.lang.CharSequence-java.lang.CharSequence...-) – Ati Ranzuglia Aug 16 '18 at 23:40
  • @AtiRanzuglia after 2 years you referenced me to java 8 documentation?? really? have you been learning java 8 all this time? c'mon!! – svichkar Aug 17 '18 at 11:28
  • @svichkar Haha, didn't realise it was a long time ago ... next time do the search yourself ;) – Ati Ranzuglia Aug 17 '18 at 20:54
  • @AtiRanzuglia you are so funny! my question was rhetorical... just to attract authors' attention to specific situations and of course to look at the documentation for these cases :) – svichkar Aug 18 '18 at 10:33
  • 1
    @MaxHeiber That would be like downvoting an answer because you don't like the previous revision while the current revision doesn't have the same problem. I hope you don't do that either. – Jasper Jan 21 '19 at 10:46
  • 2
    for an array or a list of objects you can use streams (Java 8): `Arrays.steam(array).map(String::valueOf).collect(Collectors.joining(delimiter));` – SuperSmartWorld Feb 16 '22 at 15:52
325

If you're on Android you can TextUtils.join(delimiter, tokens)

nmr
  • 16,625
  • 10
  • 53
  • 67
119

I prefer Guava over Apache StringUtils for this particular problem:

Joiner.on(separator).join(array)

Compared to StringUtils, the Joiner API has a fluent design and is a bit more flexible, e.g. null elements may be skipped or replaced by a placeholder. Also, Joiner has a feature for joining maps with a separator between key and value.

laffuste
  • 16,287
  • 8
  • 84
  • 91
nd.
  • 8,699
  • 2
  • 32
  • 42
  • 5
    This, however, accepts only `Iterable>`, so simple array has to be retyped. – Marcel Bro Nov 28 '13 at 18:23
  • 2
    @anoniim Joiner.join in what is now Google Guava is overloaded for Iterable as well as Arrays: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Joiner.html#join(java.lang.Object[]) – nd. Nov 28 '13 at 18:48
  • 2
    Ah, you're right. I accidentally used `com.google.api.client.util.Joiner` instead of `com.google.common.base.Joiner` which accepts both Iterable and Array. – Marcel Bro Dec 05 '13 at 09:30
  • 3
    Must call **skipNulls** `Joiner.on(separator).skipNulls().join(array)` – CelinHC May 22 '14 at 19:11
  • 1
    Changed the accepted answer (just for future visitors, Java 8 obviously didn't exist when you answered it... sorry for taking it away, it was well deserved!) – Eran Medan Sep 18 '15 at 15:19
113

Apache Commons Lang does indeed have a StringUtils.join method which will connect String arrays together with a specified separator.

For example:

String[] s = new String[] {"a", "b", "c"};
String joined = StringUtils.join(s, ",");  // "a,b,c"

However, I suspect that, as you mention, there must be some kind of conditional or substring processing in the actual implementation of the above mentioned method.

If I were to perform the String joining and didn't have any other reasons to use Commons Lang, I would probably roll my own to reduce the number of dependencies to external libraries.

coobird
  • 159,216
  • 35
  • 211
  • 226
  • 1
    in android I wasn't able do this, I had to change some `String fixedHours = StringUtil.join(Arrays.asList(hoursSplit), " | ");` – deadfish Apr 10 '13 at 20:20
  • I tried to use this method with " " as separator (also tried ' '), but that separator is ignored. I don't have time to check if they do a trim or whatever on it but in any case it's not what I needed ;-) – dSebastien Dec 17 '14 at 10:21
  • is this method still better than joining with StringBuilder if the array is large e.g. 100k string items of length 80-150 chars considering how StringUtils work and that the String is immutable? – Zavael Sep 09 '16 at 13:55
  • The problem with this method is the delimiter is last. You can't send in a bunch of strings and then the delimiter; it thinks the delimiter is one of the list. – markthegrea Jan 24 '17 at 21:26
54

A fast and simple solution without any 3rd party includes.

public static String strJoin(String[] aArr, String sSep) {
    StringBuilder sbStr = new StringBuilder();
    for (int i = 0, il = aArr.length; i < il; i++) {
        if (i > 0)
            sbStr.append(sSep);
        sbStr.append(aArr[i]);
    }
    return sbStr.toString();
}
Zedas
  • 557
  • 4
  • 2
  • 1
    This is advertised as fast though it wastes a lot of time. It checks whether i > 0 every time when we know it will only be true the first time. It is easy to avoid this kind of wasted time. Second, it might be faster to test the size of the incoming strings and pass the result into the constructor of the StringBuilder... haven't test that but I suspect it might be a win... if you really care about "fast". – BPS May 08 '14 at 14:07
  • 9
    @BPS actually, if you're considering fast, you can rely on that `i > 0` being optimized out thanks to CPU lookahead. Consider http://stackoverflow.com/q/11227809/330057 – corsiKa Feb 27 '15 at 21:04
  • 3
    The 'i > 0' check is also used in the apache commons implementation. The check is false only in the first iteration. – dARKpRINCE Dec 17 '15 at 15:46
33

With Java 1.8 there is a new StringJoiner class - so no need for Guava or Apache Commons:

String str = new StringJoiner(",").add("a").add("b").add("c").toString();

Or using a collection directly with the new stream api:

String str = Arrays.asList("a", "b", "c").stream().collect(Collectors.joining(","));
Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
eivindw
  • 1,939
  • 17
  • 13
32

"I'm sure there is a certified, efficient way to do it (Apache Commons?)"

yes, apparenty it's

StringUtils.join(array, separator)

http://www.java2s.com/Code/JavaAPI/org.apache.commons.lang/StringUtilsjoinObjectarrayStringseparator.htm

Roland Bouman
  • 31,125
  • 6
  • 66
  • 67
  • is this method still better than joining with StringBuilder if the array is large e.g. 100k string items of length 80-150 chars considering how StringUtils work and that the String is immutable? – Zavael Sep 09 '16 at 13:56
  • @zavael, your remark is so highly specific that I have a feeling you really want to tell me that it's not. You could of course research the issue, gather data and then post your own answer based on your research so that others may learn from it, but that would of course be much more work than posting a comment like you just did. – Roland Bouman Sep 29 '16 at 20:37
  • 2
    oh, sorry, I didnt mean to offend you. I just missed some hint if your sollution has general scope, or if it behaves differently with larger base of data, thats all. I think its better to improve existing answers to provide usefull info than to add 16th answer :) – Zavael Oct 03 '16 at 08:21
  • 1
    This is universal so that the elements doesn't have to be Strings like in case of Java 8 `String.join()`. – Ondrej Bozek Jan 13 '19 at 18:34
22

Even easier you can just use Arrays, so you will get a String with the values of the array separated by a ","

String concat = Arrays.toString(myArray);

so you will end up with this: concat = "[a,b,c]"

Update

You can then get rid of the brackets using a sub-string as suggested by Jeff

concat = concat.substring(1, concat.length() -1);

so you end up with concat = "a,b,c"

if you want to use Kotlin:

 val concat = myArray.joinToString(separator = ",") //"a,b,c"
alexm
  • 1,285
  • 18
  • 36
  • This method does return a comma-delimited String, but it prints array brackets too,(i.e., leading with '[' and ending ']'). so, in your example, concat actually returns "[a,b,c]", not "a,b,c". (This is easily fixed with a call to the String's substring method.) – Jeff Mar 16 '15 at 14:38
  • @Jeff you are right! I missed that, just updated the answer – alexm Mar 16 '15 at 14:57
  • 16
    No no no! The javadoc of `Arrays.toString` does not state that it will return `[a, b, c]`. It states this *Returns a string representation of the contents of the specified array*, it is only meant to be a textual representation. Do not rely on implementation details, as the `Arrays.toString` implementation might theoretically one day change. Never extract data out of a string representation if there are other ways. – skiwi Mar 18 '15 at 21:48
  • 1
    @skiwi this answer is the best answer if the need is to print out array contents for logging purposes. After all, how else do you present a string array content other than the [a,b,c] format? If anything, it's still going to be pretty close to that, which is good enough for logging. – Zoomzoom Aug 07 '15 at 17:48
  • 2
    this is an awful hack that relies in the string representation of one array. – GaRRaPeTa Feb 07 '18 at 15:52
17

You can use replace and replaceAll with regular expressions.

String[] strings = {"a", "b", "c"};

String result = Arrays.asList(strings).toString().replaceAll("(^\\[|\\]$)", "").replace(", ", ",");

Because Arrays.asList().toString() produces: "[a, b, c]", we do a replaceAll to remove the first and last brackets and then (optionally) you can change the ", " sequence for "," (your new separator).

A stripped version (fewer chars):

String[] strings = {"a", "b", "c"};

String result = ("" + Arrays.asList(strings)).replaceAll("(^.|.$)", "").replace(", ", "," );

Regular expressions are very powerful, specially String methods "replaceFirst" and "replaceAll". Give them a try.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
wadjakman
  • 401
  • 5
  • 12
9

All of these other answers include runtime overhead... like using ArrayList.toString().replaceAll(...) which are very wasteful.

I will give you the optimal algorithm with zero overhead; it doesn't look as pretty as the other options, but internally, this is what they are all doing (after piles of other hidden checks, multiple array allocation and other crud).

Since you already know you are dealing with strings, you can save a bunch of array allocations by performing everything manually. This isn't pretty, but if you trace the actual method calls made by the other implementations, you'll see it has the least runtime overhead possible.

public static String join(String separator, String ... values) {
  if (values.length==0)return "";//need at least one element
  //all string operations use a new array, so minimize all calls possible
  char[] sep = separator.toCharArray();

  // determine final size and normalize nulls
  int totalSize = (values.length - 1) * sep.length;// separator size
  for (int i = 0; i < values.length; i++) {
    if (values[i] == null)
      values[i] = "";
    else
      totalSize += values[i].length();
  }

  //exact size; no bounds checks or resizes
  char[] joined = new char[totalSize];
  int pos = 0;
  //note, we are iterating all the elements except the last one
  for (int i = 0, end = values.length-1; i < end; i++) {
    System.arraycopy(values[i].toCharArray(), 0, 
      joined, pos, values[i].length());
    pos += values[i].length();
    System.arraycopy(sep, 0, joined, pos, sep.length);
    pos += sep.length;
  }
  //now, add the last element; 
  //this is why we checked values.length == 0 off the hop
  System.arraycopy(values[values.length-1].toCharArray(), 0,
    joined, pos, values[values.length-1].length());

  return new String(joined);
}
Ajax
  • 2,465
  • 23
  • 20
  • 4
    Interesting. Do you have some benchmarks showing that your implementation is faster than Google collections / Apache commons? if so, both are open source and I encourage you to submit a pull request. – Eran Medan Dec 14 '12 at 15:20
  • Google's method just converts to an array and uses an iterator to append to a StringBuilder. This defaults to size 16, then resizes as it iterates. Simple, but wasteful. Apache is a little better in that they try to estimate the size of the buffer based on the size of the first element, but because it operates on plain Objects, it doesn't risk paying twice to .toString() each one. Both of these methods resort to bounds checks and resizes as they iterate. I would imagine that in the case of small arrays, Google's might be faster, and large arrays of uniform size, apache's would be... – Ajax Sep 02 '14 at 17:19
  • 1
    ...comparable. However, this method gets the same job done using a single precisely sized array, and doesn't even pay for if() checks within the for loop (as apache's does... google uses the Iterator interface to do something similar to what I've done, where the looping logic doesn't require an if on every iteration). – Ajax Sep 02 '14 at 21:07
6

This small function always comes in handy.

public static String join(String[] strings, int startIndex, String separator) {
    StringBuffer sb = new StringBuffer();
    for (int i=startIndex; i < strings.length; i++) {
        if (i != startIndex) sb.append(separator);
        sb.append(strings[i]);
    }
    return sb.toString();
}
SSpoke
  • 5,656
  • 10
  • 72
  • 124
6

This options is fast and clear:

  public static String join(String separator, String... values) {
    StringBuilder sb = new StringBuilder(128);
    int end = 0;
    for (String s : values) {
      if (s != null) {
        sb.append(s);
        end = sb.length();
        sb.append(separator);
      }
    }
    return sb.substring(0, end);
  }
mnesarco
  • 2,619
  • 23
  • 31
3

The approach that I've taken has evolved since Java 1.0 to provide readability and maintain reasonable options for backward-compatibility with older Java versions, while also providing method signatures that are drop-in replacements for those from apache commons-lang. For performance reasons, I can see some possible objections to the use of Arrays.asList but I prefer helper methods that have sensible defaults without duplicating the one method that performs the actual work. This approach provides appropriate entry points to a reliable method that does not require array/list conversions prior to calling.

Possible variations for Java version compatibility include substituting StringBuffer (Java 1.0) for StringBuilder (Java 1.5), switching out the Java 1.5 iterator and removing the generic wildcard (Java 1.5) from the Collection (Java 1.2). If you want to take backward compatibility a step or two further, delete the methods that use Collection and move the logic into the array-based method.

public static String join(String[] values)
{
    return join(values, ',');
}

public static String join(String[] values, char delimiter)
{
    return join(Arrays.asList(values), String.valueOf(delimiter));
}

// To match Apache commons-lang: StringUtils.join(values, delimiter)
public static String join(String[] values, String delimiter)
{
    return join(Arrays.asList(values), delimiter);
}

public static String join(Collection<?> values)
{
    return join(values, ',');
}

public static String join(Collection<?> values, char delimiter)
{
    return join(values, String.valueOf(delimiter));
}

public static String join(Collection<?> values, String delimiter)
{
    if (values == null)
    {
        return new String();
    }

    StringBuffer strbuf = new StringBuffer();

    boolean first = true;

    for (Object value : values)
    {
        if (!first) { strbuf.append(delimiter); } else { first = false; }
        strbuf.append(value.toString());
    }

    return strbuf.toString();
}
Parker
  • 7,244
  • 12
  • 70
  • 92
2
public String join(String[] str, String separator){
    String retval = "";
    for (String s: str){ retval+= separator + s;}
    return retval.replaceFirst(separator, "");
}
IvanP
  • 31
  • 1