The Source for Java Technology Collaboration
User: Password:



Bruce Chapman

Bruce Chapman's Blog

List Separators

Posted by brucechapman on March 25, 2008 at 05:58 PM | Comments (12)

In his Disturbing Thoughts from a Developing Mind blog, fellow kiwi Mark Derricutt discusses a situation where new for loops don't provide enough power for a particular case. (And yes this blog has been sitting drafted but unpublished for ages :( )

The case in point is building a String from the concatenation of a List of Strings with some separator between them, in this case a semi-colon.

The problem is expressed as "append the separator after each element, except the last one". What seems to have evaded Mark and his commenters is that the problem can be expressed differently.

If you restate the problem as "append the separator before each item except the first one" then you are one step away from what seems to me to be an elegant solution, at least compared to the others offered.

That final step is to realise that "except the first one" can be rephrased as except the first one in which case you prepend nothing".

Applying this to the relevant section of Mark's code yields

StringBuilder sb = new StringBuilder();
String separator = "";
for (String listenerClassName : data.TEST_LISTENERS) {
    if (listenerClassName != null && !"".equals(listenerClassName)) {
        sb.append(separator).append(listenerClassName);
        separator = ";";
    }
}

There is a more OOish solution, which is to have a (tiny) class thus

public Class Separator {
    private String next = "";
    private String separator;
    public Separator(String separator) {
        this.separator = separator;
    }

    public String get() {
        String result = next;
        next = separator;
        return result;
    }
}

which returns an empty string the first time get() is called, then the configured value thereafter.

With this we can code the loop as

StringBuilder sb = new StringBuilder();
Separator sep = new Separator(";");
for (String listenerClassName : data.TEST_LISTENERS) {
    if (listenerClassName != null && !"".equals(listenerClassName)) {
        sb.append(sep.get()).append(listenerClassName);
    }
}

So how do you tackle this problem?


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Slightly cleaner than using fFirst - use StringBuilder as the flag:

    StringBuilder sb = new StringBuilder();
    
    for (String listenerClassName: data.TEST_LISTENERS)
    {
        if (sb.length() > 0)
            sb.append(";");
        sb.append(listenerClassName);
    }
    

    Posted by: kennardconsulting on March 30, 2008 at 08:26 PM

  • Iterable<String> of course - forgotten to escape my characters :-/

    Posted by: alexlamsl on March 29, 2008 at 08:44 AM

  • Then for the core of the problem:

    public static String join(final String separator, final Iterable elements) {
        final StringBuilder sb = new StringBuilder();
        for (final String element : elements) {
            sb.append(separator).append(element);
        }
        return sb.substring(separator.length()).toString();
    }
    

    Using the boolean flag to skip the first element is probably a better overall solution to the generalised form of this problem, especially if the join operation is expensive or not quite undo-able.

    But for the case of Strings I'd prefer the illustrated solution more :-)

    Posted by: alexlamsl on March 29, 2008 at 08:42 AM

  • If we think out-of-the-box a little bit:
    public static final String join(final String separator, final String firstElement, final String ... elements) {
        final StringBuilder sb = new StringBuilder(firstElement);
        if (elements != null) {
            for (final String element : elements) {
                sb.append(separator).append(element);
            }
        }
        return sb.toString();
    }
    

    Posted by: alexlamsl on March 29, 2008 at 08:24 AM

  • I encounter this quite a bit, so this is how I handle it:

    StringBuilder sb = new StringBuilder();
    boolean fFirst = true;
    
    for (String listenerClassName: data.TEST_LISTENERS)
    {
        if (!fFirst)
            sb.append(";");
        sb.append(listenerClassName);
        fFirst = false;
    }
    

    Posted by: jkeatley on March 28, 2008 at 12:18 PM

  • String separator = ...;
    List strings = ...;
    
    StringBuilder builder = new StringBuilder();
    for (Iterator iter = strings.iterator(); iter.hasNext(); ) {
      builder.append(iter.next());
      if (iter.hasNext()) builder.append(separator);
    }
    Greetings, Daniel

    Posted by: danielrohe on March 28, 2008 at 10:51 AM

  • data.TEST_LISTENERS = Collections.emptyList();
    sb.delete(sb.length()-separator.length(), sb.length());

    Ugly.

    Also note that 1.5 has String.isEmpty.

    Posted by: tackline on March 28, 2008 at 10:20 AM

  • data.TEST_LISTENERS = Collections.emptyList();
    sb.delete(sb.length()-separator.length(), sb.length());

    Ugly.

    Also note that 1.5 has String.isEmpty.

    Posted by: tackline on March 28, 2008 at 10:19 AM

  • Or, in Scala, data.TEST_LISTENERS.mkString(";")

    Posted by: cayhorstmann on March 28, 2008 at 08:12 AM

  • I've done this more times then I can count. I ended up writing StringUtil.join(Collection, String seperator). This seems like the natural counterpart to split(). Any reason this isn't in String?

    Posted by: aberrant on March 26, 2008 at 05:21 PM

  • I do it the same way you do, though I hadn't thought of using the OOish way. This comes up fairly often, so it's idiomatic for me.

    Posted by: trcorbin on March 26, 2008 at 01:08 PM

  • StringBuilder sb = new StringBuilder();
    String separator = ";";
    for (String listenerClassName : data.TEST_LISTENERS) {
        if (listenerClassName != null && !"".equals(listenerClassName)) {
            sb.append(listenerClassName).append(separator);
        }
    }
    sb.deleteCharAt(sb.length() - 1);
    

    Posted by: gwvantreese on March 25, 2008 at 08:46 PM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds