Java equivalents of C# String.Format() and String.Join()
Asked Answered
G

16

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.

Guay answered 9/10, 2008 at 15:4 Comment(2)
So there is a String.format but I'll have to Roll my own join.Guay
For join(), I like this answer: https://mcmap.net/q/41790/-a-method-to-reverse-effect-of-java-string-split-duplicatePisolite
N
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.

Nogas answered 9/10, 2008 at 15:14 Comment(3)
google collections: google-collections.googlecode.com has a Joiner as well.Digiacomo
FYI on Ron's comment, google-collections was renamed to Guava a while ago.Garish
You should update this answer to reflect that Java 8 introduces a String.join() method.Choir
M
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

Meletius answered 9/10, 2008 at 15:10 Comment(3)
More precisely: StringBuffer for jdk1.4 and below, StringBuilder for jdk1.5 and after, since the latter is not synchronized, hence a little faster.Bagel
Istead of two iter.hasNext() invocations I usually append delimeter and then "return buf.substring(0, buf.length() - delimeter.length())".Tabret
You can streamline it a tiny bit by exiting early to avoid the delimiter: while (true) ( add_iter; if (! iter.hasNext()) break; add_delim; }Gasper
B
38

Guava comes with the Joiner class.

import com.google.common.base.Joiner;

Joiner.on(separator).join(data);
Barbellate answered 28/6, 2011 at 23:10 Comment(0)
M
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"
Moron answered 13/2, 2014 at 14:2 Comment(0)
L
17

TextUtils.join is available on Android

Lennie answered 1/5, 2013 at 22:34 Comment(0)
M
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();
  }
Mayer answered 24/6, 2011 at 3:15 Comment(0)
U
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.

Unionize answered 9/10, 2008 at 16:52 Comment(11)
The for-loop is correct, but you should also make it a Collection<String>. 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.Hierarch
Good point, I'm even more rusty with Generics. I'll edit it in.Unionize
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.Tarazi
@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.Hierarch
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.Chockfull
@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.Anetteaneurin
@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.Unionize
Try it out, write it both ways, see which one allows the larger value of c.size() without falling over.Chockfull
@Chockfull "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?Unionize
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.Chockfull
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.Unionize
K
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();
        }
Ki answered 4/4, 2014 at 20:56 Comment(1)
Very elegant solution, not involving 3rd party libraries!Quincentenary
L
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();
        }
    }
Laris answered 10/3, 2014 at 19:55 Comment(5)
@MrSnowflake Does the standard string builder have a method "removeCharAt(int index)" Its not showing up on the version Im runningInflexed
@Inflexed - 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.Laris
while your at it... this current solution will throw a "index out of bounds exception if you pass in an empty List"Inflexed
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 toInflexed
My apologies, it's not removeCharAt(int index) but deleteCharAt(int). But it has the same functionality.Trustee
K
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.

Kaycekaycee answered 9/10, 2008 at 19:54 Comment(2)
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!Anetteaneurin
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.Kaycekaycee
C
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, ", ")}
Chunky answered 5/4, 2013 at 15:11 Comment(0)
C
0

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

Candidacandidacy answered 25/6, 2009 at 16:56 Comment(1)
Perhaps you didn't notice that the accepted answer, posted 8 months ago, already contains that same information; https://mcmap.net/q/41613/-java-equivalents-of-c-string-format-and-string-join/…Shorn
T
0

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

Trustee answered 1/7, 2014 at 8:5 Comment(0)
H
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();
}
Higdon answered 1/7, 2015 at 20:22 Comment(0)
S
-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.

Squashy answered 18/11, 2013 at 23:21 Comment(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.Guay
C
-8

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

Chayachayote answered 21/10, 2009 at 13:13 Comment(1)
Because it's bad practice and slow.Trustee

© 2022 - 2024 — McMap. All rights reserved.