Skip to main content

List Separators

Posted by brucechapman on March 25, 2008 at 5:58 PM PDT

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?

Related Topics >>

Comments

List Separators

On a general overview though articles on Javapedia are written from a neutral point of view, the prominent differences between Wikipedia and Javapedia include feature restrictions (for example, editing is open to registered users only), software used (TWiki), links (CamelCase is used), and content licensing (Creative Commons 1.0 Attribution license).Office Space

I use Commons Lang's StringUtils.join() No need to write this over and over again.

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

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

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

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?

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.

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

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

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

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

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

Ugly.

Also note that 1.5 has String.isEmpty.

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

Ugly.

Also note that 1.5 has String.isEmpty.

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