7

I have an ArrayList with some Strings. I want to store that list of numbers from the ArrayList in a single string separated by a comma like the following.

String s = "350000000000050287,392156486833253181,350000000000060764"

This is my list:

   List<String> e = new ArrayList<String>();

    e.add("350000000000050287");
    e.add("392156486833253181");
    e.add("350000000000060764");

I have been trying to do it the following way:

     StringBuilder s = new StringBuilder();
    for (String id : e){

        s.append(id+",");

    }

The only problem with this is that this adds a comma to the end and I do not want that.What would be the best way to this?

Thanks

Radu Murzea
  • 10,724
  • 10
  • 47
  • 69
danilo
  • 834
  • 9
  • 25

8 Answers8

19

The easiest solution is to use String.join:

List<String> list = new ArrayList<String>();

list.add("11");
list.add("22");
list.add("33");

String joined = String.join(",", list);

System.out.println(joined);
//prints "11,22,33"

Note that this requires Java 8.


However if you want to support older versions of Java, you could fix your code using an iterator:

StringBuilder sb = new StringBuilder();

Iterator<String> iterator = list.iterator();

// First time (no delimiter):
if (iterator.hasNext()) {
    sb.append(iterator.next());

    // Other times (with delimiter):
    while (iterator.hasNext()) {
        sb.append(",");
        sb.append(iterator.next());
    }
}

Or simply use a boolean to determine the first time:

StringBuilder sb = new StringBuilder();

boolean firstTime = true;

for (String str : list) {

    if (firstTime) {
        firstTime = false;
    } else {
        sb.append(",");
    }

    sb.append(str);
}

But the latter should obviously be less performant than using an iterator comparing the generated bytecode per method. However, this might not be true as Tagir Valeev pointed out: this benchmark shows us that using a flag is more performant with a number of iterations starting from 10.

If anyone could explain why this is the case, I'd be glad to know.

Community
  • 1
  • 1
Tim
  • 5,521
  • 8
  • 36
  • 69
  • 5
    And just in case you don't have a list of CharSequence or one of its subclasses, you can grab a stream, map each object to its `toString` representation and use `collect(joining(","))` like this: `String s = myList.stream().map(Object::toString).collect(Collectors.joining(","));` – Alexis C. Oct 08 '15 at 22:01
  • "But the latter is obviously less performant than using an iterator." - why? It's absolutely not obvious. Have you measured the performance? – Tagir Valeev Oct 14 '15 at 02:02
  • @AlexisC., actually I don't know why they hesitated to add automatic `Object.toString()` or `String.valueOf` inside the joining collector. In my library I did this step, so you can use `StreamEx.of(myList).joining(",")`. – Tagir Valeev Oct 14 '15 at 02:07
  • Here's the [benchmark](https://gist.github.com/amaembo/54a3ac86691975466ce3) (8u60, x64). The flag version is slower only for single element, then it becomes faster. `String.join` is the slowest as expected (it will be optimized in Java-9). Holger's `setLength()` solution is surprisingly slower than `flag` solution... – Tagir Valeev Oct 14 '15 at 09:31
  • @TagirValeev Why: because the so called `enhanced for statement` [will be compiled using iterators](http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.14.2). This will result in exactly the same code, except for the additional branch. The reason why the `sbFlag` performs better than `sbFirst` on both your and my computer, is probably due compiler optimizations or how the test is setup. I can't really tell. But you might want to look into the generated bytecode for those methods to see what I mean. – Tim Oct 14 '15 at 18:27
  • @Tim, of course I know how the generated bytecode looks like. Bytecode is nothing, JIT-compiler generated assembler is what's really executed and it can be drastically different. The `sbFlag` may have more branches as you call `hasNext` and `next` twice, these calls are inlined twice (at least in my test as the call is monomorphic) and there are more branches inside the hasNext/next implementation. More branches in hot code = harder work for CPU branch predictor, more chances that branch predictor table entries coincide. (that's only my guess, I haven't checked what's going on exactly). – Tagir Valeev Oct 14 '15 at 21:38
  • @TagirValeev Seems like a good guess. But that doesn't tell us that `sbFlag` is more performant than `sbFirst`. It just tells us that CPU branch predictor seems to choose to optimize `sbFlag`. If it choose to optimize `sbFirst` also, I still think it will perform better. Therefor it isn't a fair test by my opinion. However, I've edited this answer, which now includes your effort. – Tim Oct 15 '15 at 12:05
  • @Tim, I actually meant `sbFirst` in my previous comment. `sbFirst` may have more branches. Sorry for confusion. – Tagir Valeev Oct 15 '15 at 12:08
8

Upvote for Tim for that Java 8 solution ;)

If you are not using JDK 8

   StringBuilder s = new StringBuilder();
    for (String id : e){

        s.append(id).append(",");

    }
   String result =s.toString().replaceAll(",$", "");

The regex I used ",$" is to detect the last comma.

And also if you see, I'm replaced s.append(id +","); with s.append(id).append(","); for better performance

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
  • I was going to add "...and pray to god that your last item does not end in a comma", but then I realized that wouldn't affect anything anyway. Are there any cases where this would not work? – dberm22 Oct 08 '15 at 19:55
  • @dberm22 It will always match the last `,` because of the `$` which denotes the end of the string. However, it seems pretty redundant to me to search for a `,` at exactly the last index, while we all know for sure it contains a `,` if `e.length() > 0 `. So `String.substring` would be a better fit (by my opinion). – Tim Oct 08 '15 at 20:27
  • 1
    @Tim: no, the most efficient solution is to perform an `if(s.length()>0) s.setLength(s.length()-1);` right *before* calling `s.toString()`, thus you don’t need a `substring` operation. – Holger Oct 09 '15 at 08:35
  • @Holger You are absolutely right, that's a much more efficient way. However, I think, the most performant solution would be using iterators and not appending the last `","` in the first place. – Tim Oct 09 '15 at 19:44
2

You can take a boolean flag to check first iteration, if not first iteration append "," before append id.

This may solve your problem.

boolean first = true;
for (String id : e){
    if(!first)
        s.append(",");
    else
        first = false;
    s.append(id);

}

NOTE

If you use Java8 then follow the solution of Tim.

ashiquzzaman33
  • 5,781
  • 5
  • 31
  • 42
  • This will add an extra conditional check every iteration the OP might be concerned about performance but it would be a lot quicker to just add the comma and delete it after the loop is done. – draksia Oct 08 '15 at 20:33
  • 1
    @draksia Performance loss by using extra conditional check is very very negligible comparing to other operation (eg: s.append(id)). – ashiquzzaman33 Oct 08 '15 at 21:18
  • Since the whole operation may only become performance relevant if we are talking about lots of iterations, you should not underestimate the cost of conditionals. However, you can expect Hotspot to be smart enough to predict the result of the conditional in this specific case and remove it entirely. Thus, it’s just a matter of taste. I, personally, don’t like making a loop body more complicated and prefer a simple loop and removal of the one extra `,`. But that’s just me. – Holger Oct 09 '15 at 08:40
  • @Holger, even if HotSpot fails to remove this check, the CPU branch predictor would work ideally after several iterations here. Much more overhead would I expect from creating heap objects, reallocating and copying `StringBuilder` internal buffer. – Tagir Valeev Oct 14 '15 at 02:10
  • @Tagir Valeev: even the reallocation and copying operations are more than often optimized away by Hotspot. However, removing the last comma wouldn’t require that if you implement it by just decrementing the `Stringbuilder`’s length by one right *before* creating a `String`. – Holger Oct 14 '15 at 08:15
  • 1
    @Holger, I would not say that reallocation/copying is optimized away. I know no evidence about that. Only some simple very specific StringBuilder patterns are optimized currently (like `new StringBuilder().append(str).append(intNum).toString()` can be translated to direct preallocation of target `char[]` buffer and creating a String on it). I'm not criticizing your solution at all, it's nice (though I personally prefer `first` flag), I'm just saying that compared to the whole loop operation the cost of extra predictable branch is negligible, thus performance does not matter here, only style. – Tagir Valeev Oct 14 '15 at 09:08
  • @Holger, see the quick-and-lame [benchmark](https://gist.github.com/amaembo/54a3ac86691975466ce3) here. The `setLength` is a little bit slower (probably due to some additional checks inside), though the difference is not that much (or there's some problem in my benchmark). – Tagir Valeev Oct 14 '15 at 09:35
  • @Tagir Valeev: maybe you should try with different string elements and add one test with a higher number of elements, but generally, the result seems to say that there is no difference and the shorter code wins. Btw., it doesn’t matter whether reallocations are optimized away as they are the same in all variants. It’s very unlikely that the extra comma at the end exhausts the capacity. And I already said in my first comment, that there are no relevant performance differences. I also didn’t suggest any solution. I just said once, use `setLength` rather than `toString().substring(…)`… – Holger Oct 14 '15 at 09:57
1

Try iterating through your list by checking if the current index is last value of list (e.size-1), if it is not the last value, then concatenate the string as normal with ",", if it is, then concatenate without ",".

    List<String> e = new ArrayList<>(Arrays.asList("3333", "4444", "3333"));
    StringBuilder s = new StringBuilder();

    for (int i = 0; i < e.size(); i++) {
        s.append(e.get(i)).append((i == e.size() - 1) ? "" : ",");
    }
  • Or more concise: `s.append(e.get(i) + ((i == e.size() - 1) ? "" : ","));` Probably some extra ()'s in there, but this isn't code golf... – Darrel Hoffman Oct 08 '15 at 20:55
  • It’s weird to use a string concatenation via `+` when you already have a `StringBuilder`. That’s inconsistent. If you don’t care about performance (that’s ok, the difference is not dramatic), then use `+` all over the code, otherwise, don’t use `s.append(e.get(i)+",");`, use `s.append(e.get(i)).append(",");` – Holger Oct 09 '15 at 08:43
1
StringBuilder s = new StringBuilder();
for(int i = 0; i < e.size()-1; i++){
    s.append(e.get(i)+",")
}
s.append(e.get(i))
Jibin Mathew
  • 4,816
  • 4
  • 40
  • 68
  • http://stackoverflow.com/questions/33022822/how-to-get-string-values-from-arraylist-and-store-them-in-a-single-string-separa#comment53888953_33023087 – Holger Oct 09 '15 at 08:44
1

Collectors.joining(","), work well in your case, example implementation:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Solution {
    public static void main(String[] args) {
        List<String> e = new ArrayList<>();
        e.add("350000000000050287");
        e.add("392156486833253181");
        e.add("350000000000060764");
        System.out.println(e.stream().collect(Collectors.joining(",")));
    }
}

Output:

350000000000050287,392156486833253181,350000000000060764
Arpit Aggarwal
  • 27,626
  • 16
  • 90
  • 108
0

If we're talking about Java 8, how about:

Collection<String> e = asList("a", "b", "c");

String result = e.stream().collect(Collectors.joining(",")); // a,b,c

this method could be applied to any Collection of strings, as well as String.join().

Denis Bazhenov
  • 9,680
  • 8
  • 43
  • 65
0
List<String> e = new ArrayList<String>();
    e.add("350000000000050287");
    e.add("392156486833253181");
    e.add("350000000000060764");
    Iterator iterator = e.iterator();
    String s="";
    int i = 1;
    while (iterator.hasNext()) {
        if (i == e.size()) {
            s =s+iterator.next();
        } else {
            s =s+iterator.next()+",";
        }
        i++;
    }
    System.out.println(s);

350000000000050287,392156486833253181,350000000000060764