112

I know this is a bit of a newbie question, but are there equivalents to C#'s string operations in Java?

Specifically, I'm talking about String.Format and String.Join.

Omar Kooheji
  • 54,530
  • 68
  • 182
  • 238
  • So there is a [String.format](http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html#format(java.util.Locale,%20java.lang.String,%20java.lang.Object...)) but I'll have to Roll my own join. – Omar Kooheji Oct 09 '08 at 15:14
  • 1
    For join(), I like this answer: http://stackoverflow.com/a/6116469/562139 – scorpiodawg Mar 08 '12 at 19:38

16 Answers16

91

The Java String object has a format method (as of 1.5), but no join method.

To get a bunch of useful String utility methods not already included you could use org.apache.commons.lang.StringUtils.

Tom
  • 15,798
  • 4
  • 37
  • 48
Grant Wagner
  • 25,263
  • 7
  • 54
  • 64
47

String.format. As for join, you need to write your own:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

The above comes from http://snippets.dzone.com/posts/show/91

Rok Strniša
  • 6,781
  • 6
  • 41
  • 53
Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
  • 6
    More precisely: StringBuffer for jdk1.4 and below, StringBuilder for jdk1.5 and after, since the latter is not synchronized, hence a little faster. – VonC Oct 09 '08 at 15:39
  • 2
    Istead of two iter.hasNext() invocations I usually append delimeter and then "return buf.substring(0, buf.length() - delimeter.length())". – Vilmantas Baranauskas Oct 09 '08 at 15:40
  • 2
    You can streamline it a tiny bit by exiting early to avoid the delimiter: while (true) ( add_iter; if (! iter.hasNext()) break; add_delim; } – 13ren Mar 21 '09 at 12:50
38

Guava comes with the Joiner class.

import com.google.common.base.Joiner;

Joiner.on(separator).join(data);
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Noel Yap
  • 18,822
  • 21
  • 92
  • 144
30

As of Java 8, join() is now available as two class methods on the String class. In both cases the first argument is the delimiter.

You can pass individual CharSequences as additional arguments:

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

Or you can pass an Iterable<? extends CharSequence>:

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 also adds a new class, StringJoiner, which you can use like this:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"
qntm
  • 4,147
  • 4
  • 27
  • 41
17

TextUtils.join is available on Android

Melllvar
  • 2,056
  • 4
  • 24
  • 47
12

You can also use variable arguments for strings as follows:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }
sgsweb
  • 433
  • 1
  • 5
  • 9
4

As for join, I believe this might look a little less complicated:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

I don't get to use Java 5 syntax as much as I'd like (Believe it or not, I've been using 1.0.x lately) so I may be a bit rusty, but I'm sure the concept is correct.

edit addition: String appends can be slowish, but if you are working on GUI code or some short-running routine, it really doesn't matter if you take .005 seconds or .006, so if you had a collection called "joinMe" that you want to append to an existing string "target" it wouldn't be horrific to just inline this:

for(String s : joinMe)
    target += s;

It's quite inefficient (and a bad habit), but not anything you will be able to perceive unless there are either thousands of strings or this is inside a huge loop or your code is really performance critical.

More importantly, it's easy to remember, short, quick and very readable. Performance isn't always the automatic winner in design choices.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • The for-loop is correct, but you should also make it a Collection. If you don't, you'd have to say "for (Object o : c)", because you couldn't guarantee that everything in c was a String. – Michael Myers Oct 09 '08 at 17:00
  • Good point, I'm even more rusty with Generics. I'll edit it in. – Bill K Oct 09 '08 at 17:02
  • When concatting inline: string + string + string, the compiler actually uses StringBuilder to append the values. I wonder if in the for-loop method you have second there, whether the compiler would do the same. – Spencer Kormos Oct 09 '08 at 18:46
  • @Spencer K: It does use a StringBuilder, but it creates a new one for each iteration (hardly the most efficient method, but then how is the compiler supposed to know?). I don't know if the JIT compiler might optimize that at runtime. – Michael Myers Oct 09 '08 at 19:26
  • The 'loop and append' style code is buggy, as it unnecessarily applies an arbitrary limit on the input range of it's arguments (based on overall memory usage and GC implementation). It should be avoided at all costs, as you never know when it will bite you. – soru Oct 21 '09 at 13:22
  • @soru: do you mind explaining the bug? The ordinary way to build up a large string from a sequence of parts is "loop and append". If your claim is that you shouldn't build large strings but use only online algorithms instead, then you have to know that such strictures will add to the complexity of your program. – Ian Apr 27 '11 at 08:04
  • @lan I think he's assuming limits from another language. Using string builder should not cause any problems and unless you run out of memory won't be buggy. If you used string concatenation you might run into some memory problems but probably nothing you'd notice. – Bill K Apr 27 '11 at 21:14
  • Try it out, write it both ways, see which one allows the larger value of c.size() without falling over. – soru May 04 '11 at 17:51
  • @soru "Falling over"? If you mean running out of memory that's not a bug--it is simply that one solution may run out of memory sooner. I'm also not sure what you are proposing as an alternative. If the alternative you mean is another library--do you really think it doesn't do exactly this under the sheets? – Bill K May 04 '11 at 18:34
  • If code written one way (loop with +=) fails when code written another way (explicit StringBuilder) doesn't, then the code written the way that fails has a bug. FindBugs will actually flag it up as such. You seem to be counting performance-related bugs as non-bugs, when in fact they are the only thing worse than threading-related bugs. There is no practical way to even specify the circumstances under which unbounded-memory-usage code will fail. – soru May 05 '11 at 17:43
  • 2
    Oh-wait. Are you talking +=? Yeah, that sucks. The loop and append currently in my answer uses StringBuilder and that's what I'm talking about. Although += has improved much you really don't want to use it inside a loop--not so much that it's buggy but in that it can perform like crap (Bug is not a term generally used for performance issues--at least not in my 20 years of programming). Also the JIT can improve this immensely--but I wouldn't rely on that. – Bill K May 05 '11 at 21:31
4

Here is a pretty simple answer. Use += since it is less code and let the optimizer convert it to a StringBuilder for you. Using this method, you don't have to do any "is last" checks in your loop (performance improvement) and you don't have to worry about stripping off any delimiters at the end.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }
Jess
  • 23,901
  • 21
  • 124
  • 145
2

I didn't want to import an entire Apache library to add a simple join function, so here's my hack.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }
Martin Konecny
  • 57,827
  • 19
  • 139
  • 159
  • @MrSnowflake Does the standard string builder have a method "removeCharAt(int index)" Its not showing up on the version Im running – NSjonas Jul 31 '14 at 16:43
  • @NSjonas - yes his edit to my answer is wrong. The `removeCharAt` doesn't exist, and the entire function no longer returns a String... Will fix this. – Martin Konecny Jul 31 '14 at 16:44
  • while your at it... this current solution will throw a "index out of bounds exception if you pass in an empty List" – NSjonas Jul 31 '14 at 21:04
  • made an edit to get trim the right lenght if the delim is more than 1 character. Also fixed an issue where you were trimming the last character and not the first as you really needed to – NSjonas Jul 31 '14 at 21:35
  • My apologies, it's not `removeCharAt(int index)` but `deleteCharAt(int)`. But it has the same functionality. – MrSnowflake Aug 03 '14 at 19:38
1

I wrote own:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> 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 tag function 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
1

If you wish to join (concatenate) several strings into one, you should use a StringBuilder. It is far better than using

for(String s : joinMe)
    target += s;

There is also a slight performance win over StringBuffer, since StringBuilder does not use synchronization.

For a general purpose utility method like this, it will (eventually) be called many times in many situations, so you should make it efficient and not allocate many transient objects. We've profiled many, many different Java apps and almost always find that string concatenation and string/char[] allocations take up a significant amount of time/memory.

Our reusable collection -> string method first calculates the size of the required result and then creates a StringBuilder with that initial size; this avoids unecessary doubling/copying of the internal char[] used when appending strings.

djb
  • 4,930
  • 1
  • 34
  • 37
  • re: pre-calculating size: When that works, it's great. It's not always possible. I recall reading somewhere that doubling the buffer size each time (or really multiplying by any fixed factor) gives something like n log n performance, which isn't really all that bad. In the general case, the alternative is to make a list of all the strings you plan to concatenate. If you use an ArrayList, you've got copying again (unless you know the length of your sequence in advance) and if you use LinkedList, then it uses more ram and garbage collection with the node objects. Sometimes you can't win. Do try! – Ian Apr 27 '11 at 07:58
  • The above examples/inquiries assumed some ordered collection or array of Strings to join. Also, by precomputing the size, you avoid extra unused chars that internal char[] array growth usually results in. – djb Jun 07 '11 at 15:32
0

StringUtils is a pretty useful class in the Apache Commons Lang library.

  • 8
    Perhaps you didn't notice that the accepted answer, posted 8 months ago, already contains that same information; http://stackoverflow.com/questions/187676/string-operations-in-java/187738#187738 – Jonik Jun 25 '09 at 17:04
0

There is MessageFormat.format() which works like C#'s String.Format().

MrSnowflake
  • 4,724
  • 3
  • 29
  • 32
0

I see a lot of overly complex implementations of String.Join here. If you don't have Java 1.8, and you don't want to import a new library the below implementation should suffice.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}
-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

So basically, the String ntop stores the value of the entire collection with comma separators and brackets.

lost_in_the_source
  • 10,998
  • 9
  • 46
  • 75
  • 1
    I'm not sure what part of the question this answers? It's not a String.format unless you plan on adding the bits of the string bit by bit to the array, and [1,0.92,3] is not as versatile as a generic string .join. – Omar Kooheji Nov 21 '13 at 13:26
-8

I would just use the string concatenation operator "+" to join two strings. s1 += s2;