Best way to build a delimited string from a list in java
I have a list of objects, and each object has a string property. For example, I have a List<Person>
and each Person
has a firstName
property. I want to build a comma-delimited, quoted string that looks like this:
'James', 'Lily', 'Michael'
Considering that java doesn't seem to have a join method (and besides, this is a bit more complicated than a simple delim开发者_StackOverflow社区ited string), what's the most straightforward way to do this? I've been trying to code this for a bit but my code's gotten very messy and I could use some fresh input.
I used this for the same issue (Apache StringUtils)
String joined = "'" + StringUtils.join(arrayOrList,"','") + "'";
If you want to do it simply and manually, you could do something like this:
String mystr = "";
for(int i = 0; i < personlist.size(); i++) {
mystr += "\'" + personlist.get(i).firstName + "\'";
if(i != (personlist.size() - 1)) {
mystr += ", ";
}
}
Now mystr contains your list. Note that the comma is only added if we are not acting on the last element in the list (with index personlist.size() - 1).
Of course, there are more elegant/efficient methods to accomplish this, but this one is, in my opinion, the clearest.
Just use the most straightforward way. Have a StringBuilder
, loop over the List
, surround each item with quotes, append it to builder and if there's more to come, append the comma.
StringBuilder builder = new StringBuilder();
for (int i = 0; i < persons.size(); i++) {
builder.append("'").append(persons.get(i)).append("'");
if (i < persons.size() - 1) {
builder.append(", ");
}
}
String string = builder.toString();
Alternatively, you can also use the Iterator
:
StringBuilder builder = new StringBuilder();
for (Iterator<Person> iter = persons.iterator(); iter.hasNext();) {
builder.append("'").append(iter.next()).append("'");
if (iter.hasNext()) {
builder.append(", ");
}
}
String string = builder.toString();
Note that using a StringBuilder
is preferred over using +=
string concatenation since the latter implicitly creates a new string instance on the heap which might be performance and memory hogging when you've a lot of items.
Jdk8 Lambda's solution:
String quotedAndDelimited = Arrays.stream(values).collect(Collectors.joining("\",\"","\"","\""));
This provides "," as the delimited, and a prefix and suffix of ".
Here is unit test with expectations:
import static java.util.Arrays.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
public class JoinerTest {
private static final Converter<Person, String> PERSON_CONVERTER =
new Converter<Person, String>() {
@Override
public String convert(Person object) {
return object.getFirstName();
}
};
@Test
public void shouldPresentFirstNames() {
// given
Person person1 = new Person();
person1.setFirstName("foo");
Person person2 = new Person();
person2.setFirstName("bar");
Joiner<Person> joiner = new Joiner<Person>(", ", "\'");
// when
String firstNames = joiner.join(PERSON_CONVERTER,
asList(person1, person2));
// then
assertThat(firstNames, is("\'foo\', \'bar\'"));
}
}
Converter is just an interface:
public interface Converter<F, T> {
T convert(F object);
}
And finally Joiner:
public class Joiner<T> {
private final String delimiter;
private final String quote;
public Joiner(String delimiter, String quote) {
this.delimiter = delimiter;
this.quote = quote;
}
public String join(Converter<T, String> converter, Iterable<T> objects) {
StringBuilder builder = new StringBuilder();
for (T object : objects) {
String string = converter.convert(object);
builder.append(quote);
builder.append(string);
builder.append(quote);
builder.append(delimiter);
}
if (builder.length() > 0) {
builder.setLength(builder.length() - delimiter.length());
}
return builder.toString();
}
}
By using different converters it is easy to join properties of different types.
I like
if (list != null) {
Iterator iter = list.iter();
if (iter.hasNext()) {
buffer.append(iter.next());
while (iter.hasNext()) {
buffer.append(", ");
buffer.append(iter.next());
}
}
}
The key advantage of this is that 90% of your execution time will probably be done in the inner while loop, and this solution only requires one comparison to determine if we need to exit the inner while loop.
Other solutions work too, but a solution that looks like:
while (iter.hasNext()) {
buffer.append(iter.next());
if (iter.hasNext()) {
buffer.append(", ");
}
}
embeds an unnecessary if statement within the while loop. This means an extra comparison will have to be made for each element, when the only differing element would be the last one.
By shifting the comparison for checking if the element is the last one to a comparison if there is more than one element, we only need to move the appended ", " to a prepended ", " for each element other than the first
You can loop through each item in your List and use the StringBuilder.append() method to add each Name to the list. This aviods using the string class which will create a new instance of the string each time as string is immutable.
1) initialize your string builder 2) get the number of items in the list by using List.size()
int listSize = personList.size()
3) add the first opening paren and quote to your string
StringBuilderinstance.append("('")
4) use a for loop and add each name, ending quote, comma and opening quote to the stringBuilder instance, before calling the append method check to see if i equals listSize if you are at the last index only append the firstname, quote and closing paren:
for(int i = 0, i<listSize, i++)
{
if(i == listSize - 1)
StringBuilderinstance.append(firstName + "')")
else
StringBuilderinstance.append(firstName + "', '")
}
A simple solution implemented in core Java (no external dependencies) that doesn't make a conditional evaluation inside the loop might look like this:
private static final char QUOTE = '\'';
private static final String SEPARATOR = ", ";
public static String join(List<Person> people) {
if (people.isEmpty()) {
return "";
}
final StringBuilder builder = new StringBuilder();
for (Person person : people) {
builder.append(QUOTE);
builder.append(person.getFirstName());
builder.append(QUOTE);
builder.append(SEPARATOR);
}
builder.setLength(builder.length() - SEPARATOR.length());
return builder.toString();
}
Using StringBuffer avoids lots of String concatenation, which is ill-performant in Java.
I would suggest you first add a method to your Person
class:
Person {
//...
String toListEntry() {
return "\'" + firstName + "\'";
}
}
Now you can use this:
List<Person> list = //... get the list
StringBuilder buf = new StringBuilder(list.get(0).toListEntry());
for(int i = 1; i < list.size(); i++)
buf.append(" , " + list.get(i).toListEntry());
String stringList = buf.toString();
精彩评论