I\'ve run into a common situation where i have a list of objects and need to generate a comma separated string with a single property, which are then each surrounded by sing
As shown in this answer, Collectors.joining
can be used to produce a comma separator list. But you can also use it to enclose the elements in single quotes in one go, which is more efficient than doing this in a separate String operation per element before joining them.
The basic idea is as follows:
public static <T> String objectPropertyToString(
Collection<? extends T> list, Function<T,String> f) {
return list.stream().map(f).collect(Collectors.joining("', '", "'", "'"));
}
Instead of just separating the elements with just a comma, we separate with a closing single quote, followed by comma and an opening single quote. Further, we use an opening single quote as starter before the first element and a closing single quote after the last element.
This works smoothly, with only one exception: if the list is empty, we get a sole ''
as result because the initial opening quote and trailing closing quote is always produced. To solve this, we have to retrace what Collectors.joining
does internally, to get hands on the StringJoiner
used for the process, so we can configure it in a way not offered by the built-in collector:
public static <T> String objectPropertyToString(
Collection<? extends T> list, Function<T,? extends CharSequence> f) {
return list.stream().map(f).collect(
()->new StringJoiner("', '", "'", "'").setEmptyValue(""),
StringJoiner::add, StringJoiner::merge).toString();
}
This basically does the same as the previous attempt with the notable exception that we now can use setEmptyValue
to specify the different result for the case that there were no elements. As a bonus we can now relax the generic type signature, allowing arbitrary CharSequence
instances to be joined instead of just String
s.
The usage is as before:
List<Company> companies = getCompaniesList();//not important
String result = objectPropertyToString(companies , Company::getId);
List<Part> parts= getPartsList();//not important
String result = objectPropertyToString(parts, Part::getPartNumber);
Stream.map() and Collectors.joining() are your friends here.
companies.stream()
.map(Company::getId)
.map(s -> "'" + s + "'")
.collect(joining(","));
You can create a helper method, but in my judgement the above is succinct enough that it isn't worthwhile:
static <T> String mapAndJoin(Collection<T> c, Function<T,String> f){
return c.stream()
.map(f)
.map(s -> "'" + s + "'")
.collect(joining(","));
}
mapAndJoin(companies, Company::getId);