Wednesday, April 25, 2012

Java: convert List<String> to a join()d string


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.


Source: Tips4all

11 comments:

  1. All the references to Apache Commons are fine (and that is what most people use) but I think the google-collections 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.

    ReplyDelete
  2. 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.

    ReplyDelete
  3. 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);

    ReplyDelete
  4. Check http://commons.apache.org/lang/api/org/apache/commons/lang/StringUtils.html

    (referenced from this thread)
    http://stackoverflow.com/questions/187676/string-operations-in-java

    ReplyDelete
  5. 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 ");

    ReplyDelete
  6. You might want to try Apache Commons StringUtils join method:

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

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

    ReplyDelete
  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.

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

    ReplyDelete
  9. 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

    ReplyDelete
  10. using Dollar is simple as typing:

    String joined = $(aList).join(" and ");


    check also this answer for the gory details

    ReplyDelete
  11. I put this in the category of functions of "so easy to write I don't bother to look for a library implementation". I can probably write this faster than I can do a Google search for an existing implementation, and then if I need some variation, I have the source, I understand it, etc.

    In many projects I've created a class like this:

    public class Joiner
    {
    String prefix;
    String conj;
    String use;
    StringBuilder sb;
    public Joiner(String prefix, String conj)
    {
    prefix=this.prefix;
    conj=this.conj;

    use=prefix;
    sb=new StringBuilder();
    }
    public Joiner(String conj)
    {
    this("",conj);
    }
    public Joiner append(String s)
    {
    sb.append(use).append(s);
    use=conj;
    }
    public boolean isEmpty()
    {
    return sb.length()==0;
    }
    public String toString()
    {
    return sb.toString();
    }
    }


    Note this allows a prefix to be placed in front of the first item and then some other conjunction used after that.

    This has many uses. Like, to make a comma-separated list:

    Joiner commaList=new Joiner(",");
    commaList.append("Item1").append("Item2").append("Item3");

    Or, to build a SQL WHERE clause with conditions ANDed together:

    Joiner where=new Joiner(" where ", " and ");
    ... bunch of code to find value for foo ...
    where.append("foo="+quote(foo));
    ... bunch of code to find value for bar ...
    where.append("bar="+quote(bar));
    ... etc ...

    (I'm assuming the "quote" function puts quotes around strings and escapes any embedded quotes. You don't stick a user input into a SQL string build without making sure to escape embedded quotes, do you?)

    The prefix is only added if we have at least one entry, which works nicely in many contexts where we string this thing in with other stuff.

    =====

    Edit: Addendum:

    Hmm, I've been downvoted at least twice for suggesting that one write simple routines yourself instead of searching the Internet for code written by someone else. How curious. I would think there's an obvious trade-off here.

    The obvious advantage of getting some open source library is that someone else has already done all the work and hopefully tested and debugged it.

    But open source is not really free. Searching for open source, downloading it, figuring out how to use it, and verifying that it meets your needs surely takes at least a few hours. Depending on the complexity of the requirement, possibly days or weeks. The code may or may not be easy to customize -- and as it's written by someone else, at best you'll have to figure it out. And while one might hope that it has been tested and debugged, it may not be.

    If I know that it would take me significantly longer to write some code I need than to find it pre-written, sure I'm going to look for open source or a commercial package. It would take extraordinarily unusual requirements to justify writing my own database engine or inventing a new programming language.

    But if I need a modest function to concatenate two strings or whatever, something that I know I can write myself in half an hour and test as part of testing the bigger program, I just do it. I can probably write it myself faster than I could find it written by someone else, and when I do, I know that it will do exactly what I want and not have to worry about undesired behavior in special cases.

    ReplyDelete