[java] A quick and easy way to join array elements with a separator (the opposite of split) in Java

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?

This question is related to java arrays string

The answer is


"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


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.


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);
  }

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

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

I prefer Google Collections 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.



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

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

public String join(String[] str, String separator){
    String retval = "";
    for (String s: str){ retval+= separator + s;}
    return retval.replaceFirst(separator, "");
}

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"

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


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.


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);
}

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to string

How to split a string in two and store it in a field String method cannot be found in a main class method Kotlin - How to correctly concatenate a String Replacing a character from a certain index Remove quotes from String in Python Detect whether a Python string is a number or a letter How does String substring work in Swift How does String.Index work in Swift swift 3.0 Data to String? How to parse JSON string in Typescript