How to get String values from ArrayList and store them in a single string separated by commas, in Java 8?
Asked Answered
W

8

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

Wordsmith answered 8/10, 2015 at 17:58 Comment(6)
Google Guava's JoinerBencion
See also #206055 -- whose answers are mostly focused on solutions appropriate prior to Java 8. This question is either a duplicate or could focus on Java 8 and following.Fudge
@SteveKuo Java 8 stolen that ;)Effuse
@AndyThomas What ? wait. How do you come to a conclusion that OP is asking for Java 8 solution ?Effuse
by deleting the last comma? :) delete/deleteCharAt/setLengthExtrude
@SureshAtta - It was either that or close this question as a duplicate. I'm open to criticism on the choice.Fudge
M
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.

Marry answered 8/10, 2015 at 18:0 Comment(8)
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(","));Vernettaverneuil
"But the latter is obviously less performant than using an iterator." - why? It's absolutely not obvious. Have you measured the performance?Stenography
@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(",").Stenography
Here's the benchmark (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...Stenography
@TagirValeev Why: because the so called enhanced for statement will be compiled using iterators. 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.Marry
@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).Stenography
@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.Marry
@Tim, I actually meant sbFirst in my previous comment. sbFirst may have more branches. Sorry for confusion.Stenography
E
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

Effuse answered 8/10, 2015 at 17:59 Comment(4)
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?Gentes
@Gentes 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).Marry
@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.Estheresthesia
@Estheresthesia 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.Marry
K
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.

Keg answered 8/10, 2015 at 18:3 Comment(8)
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.Tripos
@Tripos Performance loss by using extra conditional check is very very negligible comparing to other operation (eg: s.append(id)).Keg
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.Estheresthesia
@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.Stenography
@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.Estheresthesia
@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.Stenography
@Holger, see the quick-and-lame benchmark 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).Stenography
@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(…)Estheresthesia
H
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) ? "" : ",");
    }
Hague answered 8/10, 2015 at 18:13 Comment(2)
Or more concise: s.append(e.get(i) + ((i == e.size() - 1) ? "" : ",")); Probably some extra ()'s in there, but this isn't code golf...Duckpin
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(",");Estheresthesia
Y
1
StringBuilder s = new StringBuilder();
for(int i = 0; i < e.size()-1; i++){
    s.append(e.get(i)+",")
}
s.append(e.get(i))
Yogurt answered 8/10, 2015 at 21:32 Comment(1)
#33023322Estheresthesia
G
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
Godding answered 14/10, 2015 at 15:29 Comment(0)
L
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().

Latoya answered 14/10, 2015 at 1:18 Comment(0)
K
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

Kassiekassity answered 31/8, 2018 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.