654
votes

JavaScript has Array.join()

js>["Bill","Bob","Steve"].join(" and ")
Bill and Bob and Steve

Does Java have anything like this? I know I can cobble something up myself with StringBuilder:

static public String join(List<String> list, String conjunction)
{
   StringBuilder sb = new StringBuilder();
   boolean first = true;
   for (String item : list)
   {
      if (first)
         first = false;
      else
         sb.append(conjunction);
      sb.append(item);
   }
   return sb.toString();
}

...but there's no point in doing this if something like it is already part of the JDK.

22
See also this question for listsCasebash
Not strictly related but android has a built in join function as part of their TextUtils class: developer.android.com/reference/android/text/…, java.lang.Iterable)Xavi
Java 8 has a String.join() method. Have a look at this answer if you are using Java 8 (or newer) stackoverflow.com/a/22577565/1115554micha

22 Answers

844
votes

With Java 8 you can do this without any third party library.

If you want to join a Collection of Strings you can use the new String.join() method:

List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"

If you have a Collection with another type than String you can use the Stream API with the joining Collector:

List<Person> list = Arrays.asList(
  new Person("John", "Smith"),
  new Person("Anna", "Martinez"),
  new Person("Paul", "Watson ")
);

String joinedFirstNames = list.stream()
  .map(Person::getFirstName)
  .collect(Collectors.joining(", ")); // "John, Anna, Paul"

The StringJoiner class may also be useful.

308
votes

All the references to Apache Commons are fine (and that is what most people use) but I think the Guava equivalent, Joiner, has a much nicer API.

You can do the simple join case with

Joiner.on(" and ").join(names)

but also easily deal with nulls:

Joiner.on(" and ").skipNulls().join(names);

or

Joiner.on(" and ").useForNull("[unknown]").join(names);

and (useful enough as far as I'm concerned to use it in preference to commons-lang), the ability to deal with Maps:

Map<String, Integer> ages = .....;
String foo = Joiner.on(", ").withKeyValueSeparator(" is ").join(ages);
// Outputs:
// Bill is 25, Joe is 30, Betty is 35

which is extremely useful for debugging etc.

142
votes

Not out of the box, but many libraries have similar:

Commons Lang:

org.apache.commons.lang.StringUtils.join(list, conjunction);

Spring:

org.springframework.util.StringUtils.collectionToDelimitedString(list, conjunction);
131
votes

On Android you could use TextUtils class.

TextUtils.join(" and ", names);
49
votes

No, there's no such convenience method in the standard Java API.

Not surprisingly, Apache Commons provides such a thing in their StringUtils class in case you don't want to write it yourself.

43
votes

Three possibilities in Java 8:

List<String> list = Arrays.asList("Alice", "Bob", "Charlie")

String result = String.join(" and ", list);

result = list.stream().collect(Collectors.joining(" and "));

result = list.stream().reduce((t, u) -> t + " and " + u).orElse("");
29
votes

With a java 8 collector, this can be done with the following code:

Arrays.asList("Bill", "Bob", "Steve").stream()
.collect(Collectors.joining(" and "));

Also, the simplest solution in java 8:

String.join(" and ", "Bill", "Bob", "Steve");

or

String.join(" and ", Arrays.asList("Bill", "Bob", "Steve"));
24
votes

I wrote this one (I use it for beans and exploit toString, so don't write Collection<String>):

public static String join(Collection<?> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<?> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

but Collection isn't supported by JSP, so for TLD I wrote:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

and put to .tld file:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

and use it in JSP files as:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}
19
votes

Code you have is right way to do it if you want to do using JDK without any external libraries. There is no simple "one-liner" that you could use in JDK.

If you can use external libs, I recommend that you look into org.apache.commons.lang.StringUtils class in Apache Commons library.

An example of usage:

List<String> list = Arrays.asList("Bill", "Bob", "Steve");
String joinedResult = StringUtils.join(list, " and ");
17
votes

An orthodox way to achieve it, is by defining a new function:

public static String join(String joinStr, String... strings) {
    if (strings == null || strings.length == 0) {
        return "";
    } else if (strings.length == 1) {
        return strings[0];
    } else {
        StringBuilder sb = new StringBuilder(strings.length * 1 + strings[0].length());
        sb.append(strings[0]);
        for (int i = 1; i < strings.length; i++) {
            sb.append(joinStr).append(strings[i]);
        }
        return sb.toString();
    }
}

Sample:

String[] array = new String[] { "7, 7, 7", "Bill", "Bob", "Steve",
        "[Bill]", "1,2,3", "Apple ][","~,~" };

String joined;
joined = join(" and ","7, 7, 7", "Bill", "Bob", "Steve", "[Bill]", "1,2,3", "Apple ][","~,~");
joined = join(" and ", array); // same result

System.out.println(joined);

Output:

7, 7, 7 and Bill and Bob and Steve and [Bill] and 1,2,3 and Apple ][ and ~,~

10
votes

Java 8 solution with java.util.StringJoiner

Java 8 has got a StringJoiner class. But you still need to write a bit of boilerplate, because it's Java.

StringJoiner sj = new StringJoiner(" and ", "" , "");
String[] names = {"Bill", "Bob", "Steve"};
for (String name : names) {
   sj.add(name);
}
System.out.println(sj);
9
votes

You can use the apache commons library which has a StringUtils class and a join method.

Check this link: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Note that the link above may become obsolete over time, in which case you can just search the web for "apache commons StringUtils", which should allow you to find the latest reference.

(referenced from this thread) Java equivalents of C# String.Format() and String.Join()

7
votes

You can do this:

String aToString = java.util.Arrays.toString(anArray);
// Do not need to do this if you are OK with '[' and ']'
aToString = aToString.substring(1, aToString.length() - 1);

Or a one-liner (only when you do not want '[' and ']')

String aToString = java.util.Arrays.toString(anArray).substring(1).replaceAll("\\]$", "");

Hope this helps.

6
votes

A fun way to do it with pure JDK, in one duty line:

String[] array = new String[] { "Bill", "Bob", "Steve","[Bill]","1,2,3","Apple ][" };
String join = " and ";

String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "");

System.out.println(joined);

Output:

Bill and Bob and Steve and [Bill] and 1,2,3 and Apple ][


A not too perfect & not too fun way!

String[] array = new String[] { "7, 7, 7","Bill", "Bob", "Steve", "[Bill]",
        "1,2,3", "Apple ][" };
String join = " and ";

for (int i = 0; i < array.length; i++) array[i] = array[i].replaceAll(", ", "~,~");
String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "").replaceAll("~,~", ", ");

System.out.println(joined);

Output:

7, 7, 7 and Bill and Bob and Steve and [Bill] and 1,2,3 and Apple ][

4
votes

You might want to try Apache Commons StringUtils join method:

http://commons.apache.org/lang/api/org/apache/commons/lang/StringUtils.html#join(java.util.Iterator, java.lang.String)

I've found that Apache StringUtils picks up jdk's slack ;-)

4
votes

If you're using Eclipse Collections (formerly GS Collections), you can use the makeString() method.

List<String> list = Arrays.asList("Bill", "Bob", "Steve");

String string = ListAdapter.adapt(list).makeString(" and ");

Assert.assertEquals("Bill and Bob and Steve", string);

If you can convert your List to an Eclipse Collections type, then you can get rid of the adapter.

MutableList<String> list = Lists.mutable.with("Bill", "Bob", "Steve");
String string = list.makeString(" and ");

If you just want a comma separated string, you can use the version of makeString() that takes no parameters.

Assert.assertEquals(
    "Bill, Bob, Steve", 
    Lists.mutable.with("Bill", "Bob", "Steve").makeString());

Note: I am a committer for Eclipse Collections.

4
votes

EDIT

I also notice the toString() underlying implementation issue, and about the element containing the separator but I thought I was being paranoid.

Since I've got two comments on that regard, I'm changing my answer to:

static String join( List<String> list , String replacement  ) {
    StringBuilder b = new StringBuilder();
    for( String item: list ) { 
        b.append( replacement ).append( item );
    }
    return b.toString().substring( replacement.length() );
}

Which looks pretty similar to the original question.

So if you don't feel like adding the whole jar to your project you may use this.

I think there's nothing wrong with your original code. Actually, the alternative that everyone's is suggesting looks almost the same ( although it does a number of additional validations )

Here it is, along with the Apache 2.0 license.

public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer
    if (iterator == null) {
        return null;
    }
    if (!iterator.hasNext()) {
        return EMPTY;
    }
    Object first = iterator.next();
    if (!iterator.hasNext()) {
        return ObjectUtils.toString(first);
    }

    // two or more elements
    StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
    if (first != null) {
        buf.append(first);
    }

    while (iterator.hasNext()) {
        if (separator != null) {
            buf.append(separator);
        }
        Object obj = iterator.next();
        if (obj != null) {
            buf.append(obj);
        }
    }
    return buf.toString();
}

Now we know, thank you open source

3
votes

With java 1.8 stream can be used ,

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<String> list = Arrays.asList("Bill","Bob","Steve").
String str = list.stream().collect(Collectors.joining(" and "));
2
votes

Google's Guava API also has .join(), although (as should be obvious with the other replies), Apache Commons is pretty much the standard here.

1
votes

Java 8 does bring the

Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

method, that is nullsafe by using prefix + suffix for null values.

It can be used in the following manner:

String s = stringList.stream().collect(Collectors.joining(" and ", "prefix_", "_suffix"))

The Collectors.joining(CharSequence delimiter) method just calls joining(delimiter, "", "") internally.

0
votes

You can use this from Spring Framework's StringUtils. I know it's already been mentioned, but you can actually just take this code and it works immediately, without needing Spring for it.

// from https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/StringUtils.java

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class StringUtils {
    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
        if(coll == null || coll.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            sb.append(prefix).append(it.next()).append(suffix);
            if (it.hasNext()) {
                sb.append(delim);
            }
        }
        return sb.toString();
    }
}
-3
votes

Try this:

java.util.Arrays.toString(anArray).replaceAll(", ", ",")
                .replaceFirst("^\\[","").replaceFirst("\\]$","");