718

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.

Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 1
    See also this question for [lists](http://stackoverflow.com/questions/63150/whats-the-best-way-to-build-a-string-of-delimited-items-in-java) – Casebash Aug 11 '10 at 05:55
  • 19
    Not strictly related but android has a built in join function as part of their TextUtils class: http://developer.android.com/reference/android/text/TextUtils.html#join(java.lang.CharSequence, java.lang.Iterable) – Xavi May 31 '13 at 19:39
  • 18
    Java 8 has a `String.join()` method. Have a look at this answer if you are using Java 8 (or newer) http://stackoverflow.com/a/22577565/1115554 – micha Mar 22 '14 at 12:26

23 Answers23

939

String.join

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 String.join() method:

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

Collectors.joining

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
micha
  • 47,774
  • 16
  • 73
  • 80
  • 8
    Unfortunately, String.join only accepts CharSequences and not as one would hope Objects. Not even sure if it's nullsafe – Marc Jun 22 '15 at 12:22
  • @Marc `assertThat(String.join(", ", Lists.newArrayList("1", null)), is("1, null"));` – Sanghyun Lee Dec 09 '15 at 05:22
  • StringJoiner is especially useful if one wants to join to something like `[a, b, c]` including the braces. – koppor Feb 17 '16 at 06:26
  • @Marc String.join also accepts `Iterable`; Interface relationship: `Iterable -> Collection -> List` – aff Mar 13 '19 at 07:36
309

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.

Will Beason
  • 3,417
  • 2
  • 28
  • 46
Cowan
  • 37,227
  • 11
  • 66
  • 65
  • 11
    thanks for the plug! Our Joiner also gives you the option to append directly to an Appendable (such as a StringBuilder, or any Writer) without creating intermediate Strings, which the Apache library seems to lack. – Kevin Bourrillion Nov 18 '09 at 08:38
  • 2
    This is great, but can you please add a `.useForLastSeparator()` or some similar method? That way, you could get something like ", and " between only the last two items (with ", " for the rest). – Jeff Evans May 07 '13 at 18:23
  • 2
    Yes, it is thread safe. The only state saved in a joiner is the `separator` (which is `final`). Everything that I've seen in Guava, where I used to use an Apache Commons equivalent has been so much better (read: cleaner, faster, safer and just more robust in general) in Guava with fewer edge case failures and thread safety issues and smaller memory footprint. Their "best way possible" guiding principal seems to hold true so far. – Shadow Man Mar 28 '14 at 00:51
  • If you need to go the opposite direction (i.e., splitting a String into pieces), check out the Guava class Splitter - also very well designed/implemented. – Matt Passell Jun 30 '14 at 20:16
  • In Java 8, there is a `String.join()` method and a `StringJoiner` class. – Sheikh Dec 20 '14 at 12:40
  • Nice, but still reads strange: `useForNull("[unknown]")` should be `forNullUse("[unknown]")`, shouldn't it? – Thomas Weller Apr 20 '15 at 12:41
  • To skip blank values, use `Strings.emptyToNull` on your values: `final String a = Strings.emptyToNull( nameA.trim() ); return Joiner.on(" and ").skipNulls().join(a);` – pyb Jul 30 '15 at 14:46
  • Yup reads strange, the reverse would be more readable (since it is operating on the array) Joiner.on(names).skipNulls().join(" and "); – holdfenytolvaj Apr 14 '16 at 04:07
147

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);
Arne Burmeister
  • 20,046
  • 8
  • 53
  • 94
138

On Android you could use TextUtils class.

TextUtils.join(" and ", names);
Rafal Enden
  • 3,028
  • 1
  • 21
  • 16
51

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.

sinuhepop
  • 20,010
  • 17
  • 72
  • 107
Bart Kiers
  • 166,582
  • 36
  • 299
  • 288
  • 11
    It's always bugged me that String has a split but not a join. In a way it makes sense that it does, but it's just annoying that there isn't at least a static method for it in String. – Powerlord Nov 17 '09 at 21:29
  • 4
    I'm with you Bemrose. At least they gave us an `isEmpty()` method instead of a (static) `join()` method in `String`... :rollseyes: :) – Bart Kiers Nov 17 '09 at 21:44
48

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("");
amra
  • 16,125
  • 7
  • 50
  • 47
30

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"));
aifa
  • 524
  • 5
  • 6
25

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, ", ")}
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
20

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 ");
Juha Syrjälä
  • 33,425
  • 31
  • 131
  • 183
17

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 ~,~

Daniel De León
  • 13,196
  • 5
  • 87
  • 72
  • 5
    giving a method parameter the same name as the method itself is probably not the best idea. `separator` would a lot more suggestive. – ccpizza Dec 09 '15 at 20:55
10

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);
spongebob
  • 8,370
  • 15
  • 50
  • 83
klingt.net
  • 2,019
  • 3
  • 18
  • 19
  • You don't need to write a bit of boilerplate if you use the more convenient method [String.join()](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#join(java.lang.CharSequence,java.lang.CharSequence...)). – Andy Thomas Oct 19 '18 at 20:37
9

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

Community
  • 1
  • 1
dcp
  • 54,410
  • 22
  • 144
  • 164
7

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.

NawaMan
  • 25,129
  • 10
  • 51
  • 77
7

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 "));
George
  • 6,886
  • 3
  • 44
  • 56
Saurabh
  • 7,525
  • 4
  • 45
  • 46
6

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 ][

Daniel De León
  • 13,196
  • 5
  • 87
  • 72
  • Try it with "[Bill]", "1,2,3", and "Apple ][". Creative, but it has cases whwen it is incorrect. – Jason S Jul 22 '13 at 04:13
  • 1
    Now try it with "~,~". You can't make a program with this kind of architecture completely bulletproof. – Jason S Jul 22 '13 at 14:05
  • Yeah, I know... I've removed the downvote. But you should think a little more carefully about posting answers here, especially for questions like this one that are several years old. Not only are the answers immediately useful to the original poster, but they also show up on Google searches. (In fact, for questions that are several years old, new answers may have *no* value to the original poster.) Solutions that have drawbacks or errors can be harmful to people who find it later out of context. – Jason S Jul 22 '13 at 18:40
  • 1
    I learn a lot here from others post like mine, with not perfect solution, but really rich of knowledge, and side thinking. Relax for others, each one must understand what each line of their code do. Remember programing is like an art, and even each line of code, means something about the programmer personality. And yes, I will not use this code in production! – Daniel De León Jul 22 '13 at 19:06
4

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

Upgradingdave
  • 12,916
  • 10
  • 62
  • 72
4

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

djm.im
  • 3,295
  • 4
  • 30
  • 45
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • This will work now, but you can't be sure how List.toString() will behave in the future. I would never trust a toString() operation other than printing some informational string about the object in question. In your defense, you're not the only one that proposes this solution. – Hans Doggen Nov 17 '09 at 21:49
  • What if the contents of an element in the list contains the substring `", "`? – Bart Kiers Nov 17 '09 at 21:58
  • @Hans & @Bart: You're right. I thought nobody else will notice :P I'm changing my answer. – OscarRyz Nov 17 '09 at 22:28
  • It depends on use case. For debugging purposes toString() is just fine IMO... provided you don't rely on particular formatting. And even so, if you write a tests for your code, any future changes will be caught. – akostadinov Oct 28 '12 at 18:40
4

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.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44
Craig P. Motlin
  • 26,452
  • 17
  • 99
  • 126
2

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

Dean J
  • 39,360
  • 16
  • 67
  • 93
2

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.

thg
  • 1,209
  • 1
  • 14
  • 26
0

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();
    }
}
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
0

Another solution, it is a variation of another answer

public static String concatStringsWSep(Iterable<String> strings, String separator) {
    Iterator<String> it = strings.iterator();
    if( !it.hasNext() ) return "";
    StringBuilder sb = new StringBuilder(it.next());
    while( it.hasNext()) {
        sb.append(separator).append(it.next());
    }
    return sb.toString();                           
}
yas
  • 3,520
  • 4
  • 25
  • 38
-3

Try this:

java.util.Arrays.toString(anArray).replaceAll(", ", ",")
                .replaceFirst("^\\[","").replaceFirst("\\]$","");
Nunser
  • 4,512
  • 8
  • 25
  • 37
jorge
  • 15