Самый сложный способ создания разделенных запятыми строк из коллекции/массива/списка?
Во время моей работы с базами данных я заметил, что я пишу строки запроса, и в этих строках я должен поместить несколько ограничений в where-предложение из списка /array/collection. Должно выглядеть так:
select * from customer
where customer.id in (34, 26, ..., 2);
Вы можете упростить это, уменьшив это до вопроса о том, что у вас есть коллекция строк, и хотите создать список этих строк с разделителями-запятыми только в одной строке.
Мой подход, который я использовал до сих пор, выглядит примерно так:
String result = "";
boolean first = true;
for(String string : collectionOfStrings) {
if(first) {
result+=string;
first=false;
} else {
result+=","+string;
}
}
Но это так, как вы можете видеть очень уродливо. Вы не можете видеть, что там происходит при первом взгляде, особенно когда сложная строка (как и каждый SQL-запрос) усложняется.
Каков ваш (более) элегантный способ?
Ответы
Ответ 1
Поскольку строки неизменяемы, вы можете захотеть использовать класс StringBuilder, если вы собираетесь изменить строку в коде.
Класс StringBuilder можно рассматривать как изменяемый объект String, который выделяет больше памяти при изменении содержимого.
Исходное предложение в вопросе может быть написано еще более четко и эффективно, если учесть резервную запятую:
StringBuilder result = new StringBuilder();
for(String string : collectionOfStrings) {
result.append(string);
result.append(",");
}
return result.length() > 0 ? result.substring(0, result.length() - 1): "";
Ответ 2
Используйте API Google Guava join
метод
Joiner.on(",").join(collectionOfStrings);
Ответ 3
Я просто посмотрел на код, который сделал это сегодня. Это вариант ответа AviewAnew.
collectionOfStrings = /* source string collection */;
String csList = StringUtils.join(collectionOfStrings.toArray(), ",");
StringUtils (< - commons.lang 2.x или commons.lang 3.x link) мы использовали от Apache Commons.
Ответ 4
Как я пишу этот цикл:
StringBuilder buff = new StringBuilder();
String sep = "";
for (String str : strs) {
buff.append(sep);
buff.append(str);
sep = ",";
}
return buff.toString();
Не беспокойтесь о производительности sep. Назначение выполняется очень быстро. Hotspot имеет тенденцию удалять первую итерацию цикла в любом случае (так как это часто приходится иметь дело с такими странностями, как null и mono/bimorphic inlining check).
Если вы используете его много (более одного раза), поместите его в общий метод.
В stackoverflow есть еще один вопрос о том, как вставить список идентификаторов в инструкцию SQL.
Ответ 5
Начиная с Java 8, вы можете использовать:
Ответ 6
Я нашел итератор итератор элегантным, потому что у него есть тест для большего количества элементов (пропущенный нуль/пустой тест для краткости):
public static String convert(List<String> list) {
String res = "";
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
res += iterator.next() + (iterator.hasNext() ? "," : "");
}
return res;
}
Ответ 7
Там много ручных решений, но я хотел повторить и обновить ответ Джулии выше. Используйте сбор google класса Столяр.
Joiner.on(", ").join(34, 26, ..., 2)
Он обрабатывает var args, iterables и массивы и правильно обрабатывает разделители более чем одного char (в отличие от ответа на gimmel). Он также будет обрабатывать нулевые значения в вашем списке, если вам это нужно.
Ответ 8
Здесь невероятно общая версия, которую я создал из комбинации предыдущих предложений:
public static <T> String buildCommaSeparatedString(Collection<T> values) {
if (values==null || values.isEmpty()) return "";
StringBuilder result = new StringBuilder();
for (T val : values) {
result.append(val);
result.append(",");
}
return result.substring(0, result.length() - 1);
}
Ответ 9
String.join(", ", collectionOfStrings)
доступен в Java8 api.
альтернатива (без необходимости добавления зависимости goava goava):
Joiner.on(",").join(collectionOfStrings);
Ответ 10
Вы можете попробовать
List collections = Arrays.asList(34, 26, "...", 2);
String asString = collection.toString();
// justValues = "34, 26, ..., 2"
String justValues = asString.substring(1, asString.length()-1);
Ответ 11
Я думаю, что неважно, что sql объединяет значения where where, которые вы делаете:
SELECT.... FROM.... WHERE ID IN (значение1, значение2,.... значениеN)
Где значениеX происходит из списка строк.
Во-первых, если вы сравниваете строки, они должны быть процитированы, это не тривиально, если строки могут иметь цитату внутри.
Во-вторых, если значения исходят от пользователя или другой системы, тогда возможен атака SQL-инъекций.
Это намного более подробный, но что вам нужно сделать, это создать строку, такую как:
SELECT.... FROM.... WHERE ID IN (?,?,....?)
а затем привяжите переменные с помощью Statement.setString(nParameter, parameterValue)
Ответ 12
Это будет кратчайшее решение, за исключением использования Guava или Apache Commons
String res = "";
for (String i : values) {
res += res.isEmpty() ? i : ","+i;
}
Хорошо с списком элементов 0,1 и n. Но вам нужно проверить нулевой список.
Я использую это в GWT, поэтому я хорошо разбираюсь в StringBuilder. И для коротких списков с двумя парами элементов в порядке и в другом месте;)
Ответ 13
В случае, если кто-то наткнулся на это в последнее время, я добавил простой вариант с использованием Java 8 reduce()
. Он также включает некоторые из уже упомянутых решений другими:
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import com.google.common.base.Joiner;
public class Dummy {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "de", "fg");
String commaSeparated = strings
.stream()
.reduce((s1, s2) -> {return s1 + "," + s2; })
.get();
System.out.println(commaSeparated);
System.out.println(Joiner.on(',').join(strings));
System.out.println(StringUtils.join(strings, ","));
}
}
Ответ 14
В Android вы должны использовать это:
TextUtils.join(",",collectionOfStrings.toArray());
Ответ 15
Еще один метод решения этой проблемы. Не самый короткий, но он эффективен и выполняет свою работу.
/**
* Creates a comma-separated list of values from given collection.
*
* @param <T> Value type.
* @param values Value collection.
* @return Comma-separated String of values.
*/
public <T> String toParameterList(Collection<T> values) {
if (values == null || values.isEmpty()) {
return ""; // Depending on how you want to deal with this case...
}
StringBuilder result = new StringBuilder();
Iterator<T> i = values.iterator();
result.append(i.next().toString());
while (i.hasNext()) {
result.append(",").append(i.next().toString());
}
return result.toString();
}
Ответ 16
Есть некоторые сторонние библиотеки Java, которые предоставляют метод объединения строк, но вы, вероятно, не хотите использовать библиотеку только для чего-то простого. Я бы просто создал вспомогательный метод вроде этого, который, я думаю, немного лучше, чем ваша версия. Он использует StringBuffer, который будет более эффективен, если вам нужно присоединиться к множеству строк, и он работает с коллекцией любого типа.
public static <T> String join(Collection<T> values)
{
StringBuffer ret = new StringBuffer();
for (T value : values)
{
if (ret.length() > 0) ret.append(",");
ret.append(value);
}
return ret.toString();
}
Другое предложение с использованием Collection.toString() короче, но оно полагается на Collection.toString(), возвращающее строку в очень специфическом формате, на который я лично не хочу полагаться.
Ответ 17
Если вы используете Spring, вы можете сделать:
StringUtils.arrayToCommaDelimitedString(
collectionOfStrings.toArray()
)
(пакет org.springframework.util)
Ответ 18
Я не уверен, что это "сложный", но это, конечно, немного короче. Он будет работать с различными типами коллекций, например. Set <Integer> , List <String> , и т.д.
public static final String toSqlList(Collection<?> values) {
String collectionString = values.toString();
// Convert the square brackets produced by Collection.toString() to round brackets used by SQL
return "(" + collectionString.substring(1, collectionString.length() - 1) + ")";
}
Упражнение для чтения: измените этот метод так, чтобы он корректно обрабатывал пустую/пустую коллекцию:)
Ответ 19
Что делает код уродливым - это специальная обработка для первого случая. Большинство строк в этом маленьком фрагменте посвящены не выполнению обычной работы с кодом, а обработке этого особого случая. И то, что альтернативы, подобные гимелю, решают, перемещая специальную обработку вне цикла. Есть один специальный случай (ну, вы можете видеть как начало, так и конец как особые случаи, но только один из них нужно обрабатывать специально), поэтому обработка его внутри цикла излишне сложна.
Ответ 20
Присоединение методов доступно в массивах и классах, расширяющих AbstractCollections, но не переопределяет метод toString() (например, практически все коллекции в java.util)
например:
Строка s = java.util.Arrays.toString(collectionOfStrings.toArray());
s = s.substing(1, s.length() - 1);//[] гарантированно существует
что довольно странно, поскольку он работает только для чисел, как и для SQL.
Ответ 21
Я только что проверил тест для своей библиотеки dollar:
@Test
public void join() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
String string = $(list).join(",");
}
он создает свободную оболочку вокруг списков/массивов/строк/etc, используя только один статический импорт: $
.
NB
используя диапазоны, предыдущий список можно переписать как $(1, 5).join(",")
Ответ 22
Хорошая вещь в выражении IN заключается в том, что если вы повторяете значения, это не изменяет результат. Итак, просто продублируйте первый элемент и обработайте весь список. Это предполагает, что в списке есть хотя бы один элемент. Если нет элементов, я бы предложил сначала проверить это, а потом вообще не выполнять SQL.
Это сделает трюк, очевидно в том, что он делает, и не полагается на внешние библиотеки:
StringBuffer inString = new StringBuffer(listOfIDs.get(0).toString());
for (Long currentID : listOfIDs) {
inString.append(",").append(currentID);
}
Ответ 23
В то время как я думаю, что лучше всего использовать Joiner из Guava, если я должен был его закодировать вручную, я нахожу этот подход более элегантным, чем флаг "первый" или измельчение последней запятой.
private String commas(Iterable<String> strings) {
StringBuilder buffer = new StringBuilder();
Iterator<String> it = strings.iterator();
if (it.hasNext()) {
buffer.append(it.next());
while (it.hasNext()) {
buffer.append(',');
buffer.append(it.next());
}
}
return buffer.toString();
}
Ответ 24
если у вас есть массив, который вы можете сделать:
Arrays.asList(parameters).toString()
Ответ 25
Возможно, вы сможете использовать LINQ (для SQL), и вы можете использовать образец LINQ Dynamic Query из MS. http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Ответ 26
java.util.List<String> lista = new java.util.ArrayList<String>();
lista.add("Hola");
lista.add("Julio");
System.out.println(lista.toString().replace('[','(').replace(']',')'));
$~(Hola, Julio)
Ответ 27
String commaSeparatedNames = namesList.toString().replaceAll( "[\\[|\\]| ]", "" ); // replace [ or ] or blank
Строковое представление состоит из списка коллекции элементов в том порядке, в котором они возвращаются своим итератором, заключенным в квадратные скобки ( "[]" ). Смежные элементы разделены символы "," (запятая и пробел).
AbstractCollection javadoc
Ответ 28
Список токенов = новый ArrayList (результат);
final StringBuilder builder = new StringBuilder();
for (int i =0; i < tokens.size(); i++){
builder.append(tokens.get(i));
if(i != tokens.size()-1){
builder.append(TOKEN_DELIMITER);
}
}
builder.toString();
Ответ 29
Другой вариант, основанный на том, что я вижу здесь (с небольшими изменениями).
public static String toString(int[] numbers) {
StringBuilder res = new StringBuilder();
for (int number : numbers) {
if (res.length() != 0) {
res.append(',');
}
res.append(number);
}
return res.toString();
}
Ответ 30
Есть простой способ. Вы можете получить результат в одной строке.
String memberIdsModifiedForQuery = memberIds.toString().replace("[", "(").replace("]", ")");
Чтобы получить полную идею, проверьте код ниже
public static void main(String[] args) {
List<Integer>memberIds=new ArrayList<Integer>(); //This contain member ids we want to process
//adding some sample values for example
memberIds.add(3);
memberIds.add(4);
memberIds.add(2);
String memberIdsModifiedForQuery = memberIds.toString().replace("[", "(").replace("]", ")"); //here you will get (3,4,5) That you can directly use in query
System.out.println(memberIdsModifiedForQuery);
String exampleQuery="select * from customer where customer.id in "+memberIdsModifiedForQuery+" ";
}