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

Во время моей работы с базами данных я заметил, что я пишу строки запросов, и в эти строки я должен наложить несколько ограничений в предложении where из списка / массива / коллекции. Должно выглядеть так:

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-запрос) усложняется.

Каков ваш (более) элегантный способ?

Ответы [ 31 ]

4 голосов
/ 15 октября 2008

Я думаю, что это не очень хорошая идея - создавать sql-конкатенацию значений предложений where, как вы делаете:

SELECT.... FROM.... WHERE ID IN( value1, value2,....valueN)

Где valueX берется из списка строк.

Во-первых, если вы сравниваете строки, они должны быть заключены в кавычки, и это не тривиально, если внутри строк может быть кавычка.

Во-вторых, если значения поступают от пользователя или другой системы, тогда возможна атака SQL-инъекцией.

Это намного более многословно, но вам нужно создать строку вроде этой:

SELECT.... FROM.... WHERE ID IN( ?, ?,....?)

и затем связать переменные с помощью Statement.setString(nParameter,parameterValue).

4 голосов
/ 06 марта 2013

Это будет самое короткое решение, за исключением использования Guava или Apache Commons

String res = "";
for (String i : values) {
    res += res.isEmpty() ? i : ","+i;
}

Хорошо с 0,1 и n списком элементов. Но вам нужно проверить нулевой список. Я использую это в GWT, так что мне хорошо без StringBuilder. А для коротких списков, состоящих из пары элементов, это нормально, в другом месте;)

4 голосов
/ 21 апреля 2015

В Android вы должны использовать это:

TextUtils.join(",",collectionOfStrings.toArray());
4 голосов
/ 24 марта 2015

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

  }
}
3 голосов
/ 15 октября 2008

Просто еще один способ справиться с этой проблемой. Не самый короткий, но он эффективен и выполняет свою работу.

/**
 * 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();
}
2 голосов
/ 26 января 2012

Если вы используете Spring, вы можете сделать:

StringUtils.arrayToCommaDelimitedString(
    collectionOfStrings.toArray()
)

(пакет org.springframework.util)

2 голосов
/ 15 октября 2008

Есть некоторые сторонние библиотеки 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 (), возвращающего строку в очень специфическом формате, на который я лично не хотел бы полагаться.

1 голос
/ 20 октября 2012

Хотя я думаю, что вам лучше всего использовать Joiner из Гуавы, но если бы мне пришлось кодировать его вручную, я нахожу этот подход более элегантным, чем флаг 'first' или отключение последней запятой.

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();
}
1 голос
/ 25 января 2010

Я только что зарегистрировал тест для своей библиотеки доллар :

@Test
public void join() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
    String string = $(list).join(",");
}

создает свободную оболочку вокруг списков / массивов / строк / и т. Д., Используя только один статический импорт : $.

NB

используя диапазоны, предыдущий список можно переписать как $(1, 5).join(",")

1 голос
/ 25 января 2010

Соединение 'методов' доступно в массивах и классах, которые расширяют AbstractCollections, но не переопределяют метод toString() (как практически все коллекции в java.util).

Например:

String s= java.util.Arrays.toString(collectionOfStrings.toArray());
s = s.substing(1, s.length()-1);// [] are guaranteed to be there

Это довольно странный способ, поскольку он работает только для чисел, аналогично данным SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...