Самый сложный способ создания разделенных запятыми строк из коллекции/массива/списка?

Во время моей работы с базами данных я заметил, что я пишу строки запроса, и в этих строках я должен поместить несколько ограничений в 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()

Ответ 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+" ";
    }