Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

is there a more elegant way to solve this problem?

List<String> paramList = new ArrayList<String>( );
paramList.add( "param1" );
paramList.add( "param2" );

StringBuilder result = new StringBuilder();
for ( String p : paramList )
{
  result.append( p ).append( ", " );
}

String withoutLastComma = result.substring( 0, result.length( ) - ", ".length( ) );
System.err.println( withoutLastComma );
share|improve this question

We're looking for long answers that provide some explanation and context. Don't just give a one-line answer; explain why your answer is right, ideally with citations. Answers that don't include explanations may be removed.

4  
How about instead of adding comma and then removing it just not add comma to the last item? using the index based approach would have been fine. for (int i = 0; i < paramList.size()-1; i++) { result.append( p ).append( ", " ); } –  Arjang May 19 '11 at 20:01
    
Swapping the order of appends--result.append(", ").append(p)--allows you to use the simpler extraction: result.substring(", ".length()) –  David Harkness May 18 at 21:07

15 Answers 15

up vote 61 down vote accepted

One may use string utility methods such as StringUtil.join to concatenate elements in an array or a collection object. Consult the StringUtil API's StringUtil.join entry.

For example:

StringUtils.join(["a", "b", "c"], "--")  // => "a--b--c"
share|improve this answer
    
I will add a tool library. First i have to check the differences between apache commons and google guave. –  Jonas Apr 19 '11 at 11:23
6  
I don't see how something this simple can justify adding a library. –  Athas Apr 24 '11 at 23:04
3  
@Wes: If you are working with critical production code and do not want to take on unnecessary dependencies (or have a policy requirement not to use external libraries) and have the time to write/test/debug/optimize your own methods, then by all means, roll your own. I made the assumption that OP is looking for a simple solution for a non-critical task. –  Adeel Zafar Soomro Apr 25 '11 at 13:04
    
Guava has Joiner: Joiner.on(", ").join(paramList). It provides helpful options such as skipping null values, too. Oops, already an answer. –  David Harkness May 18 at 21:00
3  
Java 8 now also has a String.join() method, see my answer below. –  Kolargol00 Jul 31 at 3:18
for ( String p : paramList )
{
  if (result.length() > 0) result.append( ", " );
  result.append( p );
}
share|improve this answer
    
I've always used this method. It's clear. It looks nearly the same in any language. I've never seen where the extra comparison would ever have enough weight per iteration to make a difference. –  hometoast Apr 19 '11 at 12:44
1  
I used this solution in .NET, until I discovered that string has a static Join method. I'm surprised Java doesn't have something similar built-in. –  Kyralessa May 20 '11 at 3:40
    
I like how this answer actually shows how to solve the problem, not just avoid it by using a library method. Not that using the library method is wrong though, its just you still don't see how it is actually solved. –  Andrew Hagner Mar 20 '13 at 17:45
    
It is exactly what StringUtils.join seems to be doing. –  would_like_to_be_anon Jul 22 at 16:49

I believe it's better to know how to write it and then use a library. I usually prefer to make a check before the loop, thus avoiding to have to check every time in the loop:

int size = paramList.size();
if (size > 0) {
    result.append(paramList.get(0));
    for (int i = 1; i < size; ++i) {
        result.append(", ").append(paramList.get(i));
    }
}
share|improve this answer
    
+1 -- this is the best possible solution that does not call String.Join. –  Billy ONeal Apr 19 '11 at 21:35

One of possible ways is a using Joiner from Google Guava library:

result = Joiner.on(", ").join(paramList);
share|improve this answer

A couple of alternate options here - any of such can simply only resolve to making the intentions clear, although it's hardly an issue.

It might be a little clearer, albeit not much, using lastIndexOf:

String withoutLastComma = result.substring(0, result.lastIndexOf(","));

Or just refactor a little, which could be more explanatory:

StringBuilder result = new StringBuilder();
for (int i = 0; i < paramList.size(); i++)
{           
    result.append(paramList.get(i));
    if (i + 1 != paramList.size())
        result.append(", ");
}
System.err.println(result);

Or, lastly, use a string utility library as linked in other answers provided; though, the thought of doing so brings the term 'sledgehammer to crack a nut' to mind, but it might well be justified depending on what other operations you require to carry out.

share|improve this answer
    
This makes the code more complex. Replacing the foreach with a for statement makes it a bit more complex. However your solution works and is correct, but you have to make sure not bringing in extra complexity. –  Gertjan Apr 19 '11 at 12:23
    
I would argue that complexity in such a trivial construct is far, far from being reached (though understand where you're coming from in terms of 'more complex') - it is clear and self-explanatory for the everyday / professional programmer (IMO). Even just the following of the original warrants such changes, I think: ( 0, result.length( ) - ", ".length( ) ); And, thanks for the input. :) –  Grant Thomas Apr 19 '11 at 12:27
    
for this sample you are right. But imagine a scenario with a couple of nested loops. In that case you get something like result[i].result[j].result[k]. In those cases a foreach might be a more readable solution because it is easy to make a small mistake like using 1 instead of i or mixup the i,j & k variable. –  Gertjan Apr 20 '11 at 8:08
    
@Gertjan: I agree, luckily we're not in that situation here. ;) –  Grant Thomas Apr 20 '11 at 8:36

That's strange that nobody has mentioned iterator-wise approach so far.

So here it goes:

public static <E> String join(Iterable<E> iterable, String delim) {
    Iterator<E> iterator = iterable.iterator();
    if (!iterator.hasNext()) {
        return "";
    }

    StringBuilder builder = new StringBuilder(iterator.next().toString());
    while (iterator.hasNext()) {
        builder.append(delim).append(iterator.next().toString());
    }

    return builder.toString();
}

No messing with indexes, substringing, etc, etc.

And let's use it:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.println(join(list, ", "));
share|improve this answer
3  
There's no need for the toString() calls on the elements since StringBuilder does that automatically. Plus you're risking an NPE. –  David Harkness May 18 at 21:10

What I use is a variable that I initialize as empty and then set inside the loop.

List<String> paramList = new ArrayList<String>( );
paramList.add("param1");
paramList.add("param2");

String separator = "";

StringBuilder result = new StringBuilder();
for (String p : paramList)
{
    result.append(separator)
    result.append(p);
    separator = ", ";
}

System.err.println(result.toString());
share|improve this answer
    
I often use this for languages without "join" functions, like PL/SQL... –  AsKaiser Dec 4 '13 at 15:16

Java 8 provides a String.join() method, so you can do it without depending on an external library.

List<String> paramList = new ArrayList<String>();
paramList.add("param1");
paramList.add("param2");

String withoutLastComma = String.join(", ", paramList);
share|improve this answer
    
This answer is being discussed on meta –  rolfl Jul 31 at 3:40
    
Thanks for the explanation @rolfl. Sorry, I didn't notice the question was so old. :( –  Kolargol00 Jul 31 at 9:09
String listString = Arrays.toString(paramList.toArray());
System.err.println( listString );

Will return:

[param1, param2]

This has the added benefit of using the String.valueOf(object) method which will print 'null' in the case of null objects. The Arrays.toString() method is also pretty straight forward if you just want to reimplement it. Removing the brackets:

    int iMax = paramList.size() - 1;
    if (iMax == -1) {
      return "";
    }
    String[] params = paramList.toArray();
    StringBuilder b = new StringBuilder();
    for (int i = 0; ; i++) {
      String param = params[i];
      b.append(param);
      if (i == iMax) {
        return b.toString();
      }
      b.append(", ");
    }
share|improve this answer

Surprised no one has contributed a unit test specification:

  • a useful result should contain max(0, paramList.length() - 1) commas.
  • a robust solution should not throw IndexOutOfBoundsException if the list is empty.
  • an efficient solution would provide a realistic StringBuilder capacity estimate.

The result can be misleading or useless if any parameter contains a comma. Java8 String.join should be redesigned to flag this "delimiter collision" possibility at compile time, and only accept strings to be joined that can be split again afterwards because they have already been escaped or quoted or do not or cannot contain the delimiter.

share|improve this answer

I like this technique:

private String join(Iterable<?> items, String sep) {
    Iterator<?> iter = items.iterator();
    if (!iter.hasNext()) {
        return "";
    }

    StringBuilder builder = new StringBuilder();
    builder.append(iter.next());
    while (iter.hasNext()) {
        builder.append(sep).append(iter.next());
    }

    return builder.toString();
}

What I like about it is there is no wasted if condition inside the loop.

Here are some unit tests to go with it:

@Test
public void testEmptyCollection() {
    Assert.assertTrue(join(Collections.emptyList(), ", ").isEmpty());
}

@Test
public void testJoinSingleItem() {
    String item = "hello";
    Assert.assertEquals(item, join(Collections.singletonList(item), ", "));
}

@Test
public void testJoinTwoItems() {
    Integer item1 = 4;
    Integer item2 = 9;
    String sep = ", ";
    String expected = item1 + sep + item2;
    Assert.assertEquals(expected, join(Arrays.asList(item1, item2), sep));
}
share|improve this answer
    
Then you'll love Alexey's answer above. ;) –  David Harkness May 18 at 21:12
    
Ah, too many answers, I overlooked that one. Normally I would have just left some suggestions in comments, instead of a full-blown answer. Too late now, I'll just keep this anyway, for the few small extras I added. –  janos May 18 at 21:46
    
Dear downvoter, what is it you don't like about this answer (and apparently Alexey's too, I see he got a downvote today too, coincidence?) Why do you think this answer is not useful? –  janos Sep 23 at 7:12

With java.util.stream, we can simply do this:

List<String> paramList = new ArrayList<String>( );
paramList.add( "param1" );
paramList.add( "param2" );

System.err.println(
        paramList.stream()
                 .collect(Collectors.joining(", "))
);//"param1, param2"
share|improve this answer
2  
This answer is being discussed on meta –  rolfl Jul 31 at 3:40

Less intuitive but a bit neater

for (String p : paramList) {
    result.append(" ,").append(p);
}

String withoutComma = paramList.isEmpty() ? "" : result.substring(2)
System.err.println(withoutComma);

*Edited for robustness. Now less pretty, but avoids the IndexOutOfBoundsException (which is quite ugly itself).

share|improve this answer
    
How does this behave for empty lists? –  dainichi Aug 6 at 4:20
    
Good question. I didn't even think of that. Luckily, it works –  Eva Aug 6 at 21:45
    
It works if you're happy to get an IndexOutOfBoundsException. –  dainichi Aug 8 at 4:51
    
@dainichi Whoops. I was thinking the comma would be appended so it would be at least 2 characters, but I forgot that it's only appended if the list has a value. Also, that was kinda sassy. j/s –  Eva Aug 8 at 15:33

As an alternative of manually going through the loop to construct the comma-separated contents of the List, you can take advantage of the List's toString() method alongside with substring method of String.

String contents = paramList.toString(); //returns [param 1, param2]

//remove `[` and `]`
System.out.println(contents.substring(1, contents.length()-1));
share|improve this answer

The following approach is general - it does not assume that you can look back at the results or check how many items are remaining.

List<String> paramList = new ArrayList<String>( );
paramList.add("param1");
paramList.add("param2");

String[] prefixes = {"", ", "};
int prIndex = 0;
StringBuilder result = new StringBuilder();
for (String p : paramList)
{
    result.append(prefixes[prIndex]).append(p);
    prIndex = 1; // Overwriting this is very cheap.
}

System.err.println(result.toString());
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.