从Collection / Array / List创建以逗号分隔的字符串的最复杂方法?

时间:2008-10-15 17:03:21

标签: java sql string

在我处理数据库的过程中,我注意到我编写了查询字符串,在这个字符串中,我必须在list / array / collection的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查询)变得复杂时。

你(更)优雅的方式是什么?

32 个答案:

答案 0 :(得分:87)

使用Google Guava APIjoin方法:

Joiner.on(",").join(collectionOfStrings);

答案 1 :(得分:85)

由于字符串是不可变的,如果要更改代码中的String,可能需要使用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 :(得分:73)

我刚看了今天这样做的代码。这是AviewAnew答案的变体。

collectionOfStrings = /* source string collection */;
String csList = StringUtils.join(collectionOfStrings.toArray(), ",");

我们使用的StringUtils(< - commons.lang 2.x或commons.lang 3.x link)来自Apache Commons

答案 3 :(得分:46)

我写这个循环的方式是:

StringBuilder buff = new StringBuilder();
String sep = "";
for (String str : strs) {
    buff.append(sep);
    buff.append(str);
    sep = ",";
}
return buff.toString();

不要担心sep的性能。分配非常快。无论如何,Hotspot倾向于剥离循环的第一次迭代(因为它经常需要处理诸如null和单/双态内联检查之类的奇怪现象。)

如果您使用它(不止一次),请将其放在共享方法中。

stackoverflow还有另一个问题,涉及如何将一个id列表插入到SQL语句中。

答案 4 :(得分:40)

从Java 8开始,您可以使用:

答案 5 :(得分:11)

我发现迭代器成语优雅,因为它有更多元素的测试(ommited null / empty test for brief):

public static String convert(List<String> list) {
    String res = "";
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
        res += iterator.next() + (iterator.hasNext() ? "," : "");
    }
    return res;
}

答案 6 :(得分:8)

有很多手动解决方案,但我想重申和更新上面的Julie的答案。使用google collections Joiner class

Joiner.on(", ").join(34, 26, ..., 2)

它处理var args,iterables和数组,并正确处理多个char的分隔符(与gimmel的答案不同)。如果需要,它还会处理列表中的空值。

答案 7 :(得分:7)

String.join(", ", collectionOfStrings)

可在Java8 api中找到。

替代(无需添加google guava依赖项):

Joiner.on(",").join(collectionOfStrings);

答案 8 :(得分:7)

这是一个令人难以置信的通用版本,我是根据以前的建议组合而成的:

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 :(得分:5)

你可以尝试

List collections = Arrays.asList(34, 26, "...", 2);
String asString = collection.toString();
// justValues = "34, 26, ..., 2"
String justValues = asString.substring(1, asString.length()-1);

答案 10 :(得分:4)

我认为构建连接where子句值的sql并不是一个好主意:

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

valueX来自字符串列表。

首先,如果您要比较字符串,则必须引用它们,如果字符串内部可以引用,则这并非易事。

其次,如果值来自用户或其他系统,则可能发生SQL注入攻击。

它更冗长,但你应该做的是创建一个像这样的字符串:

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

然后将变量与Statement.setString(nParameter,parameterValue)绑定。

答案 11 :(得分:4)

在Android中你应该使用它:

TextUtils.join(",",collectionOfStrings.toArray());

答案 12 :(得分:4)

如果最近有人偶然发现了这个问题,我使用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, ","));

  }
}

答案 13 :(得分:4)

这是迄今为止最短的解决方案,除了使用Guava或Apache Commons

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

良好的0,1和n元素列表。但是你需要检查空列表。 我在GWT中使用它,所以没有StringBuilder我很好。对于只有几个元素的短名单,其他地方也可以;)

答案 14 :(得分:3)

另一种处理此问题的方法。不是最短的,但效率很高,可以完成任务。

/**
 * 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();
}

答案 15 :(得分:2)

有一些第三方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()以非常特定的格式返回一个字符串,我个人不想依赖它。

答案 16 :(得分:2)

如果你使用Spring,你可以这样做:

StringUtils.arrayToCommaDelimitedString(
    collectionOfStrings.toArray()
)

(包org.springframework.util)

答案 17 :(得分:1)

关于IN表达式的好处是,如果你有重复的值,它不会改变结果。因此,只需复制第一个项目并处理整个列表。这假定列表中至少有一个项目。如果没有项目,我建议先检查,然后再不执行SQL。

这将解决问题,它在做什么并且不依赖于任何外部库是显而易见的:

StringBuffer inString = new StringBuffer(listOfIDs.get(0).toString());
for (Long currentID : listOfIDs) {
  inString.append(",").append(currentID);
}

答案 18 :(得分:1)

虽然我认为你最好的选择是使用Guava的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();
}

答案 19 :(得分:1)

我刚刚为我的图书馆dollar检查了一个测试:

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

使用只有一个静态导入$为列表/数组/字符串/等创建一个流畅的包装器。

<强> NB

使用范围可以将以前的列表重新编写为$(1, 5).join(",")

答案 20 :(得分:1)

如果你有一个数组,你可以这样做:

Arrays.asList(parameters).toString()

答案 21 :(得分:1)

加入'方法'在数组和扩展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数据。

答案 22 :(得分:1)

另一种选择,基于我在这里看到的(稍作修改)。

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();
}

答案 23 :(得分:1)

使代码难看的原因是第一种情况的特殊处理。这个小片段中的大多数行都是专门的,不是为了完成代码的日常工作,而是为了处理这种特殊情况。通过在循环外部移动特殊处理,这就是gimel解决的替代方案。有一个特例(好吧,你可以看到开始和结束都是特殊情况 - 但只需要特别处理其中一个),所以在循环中处理它是不必要的复杂。

答案 24 :(得分:1)

我不确定这是多么“复杂”,但它确实有点短。它适用于各种不同类型的收藏,例如设置&lt;整数&gt;,列出&lt; String&gt;等

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) + ")";
}

为读者练习:修改此方法,使其正确处理空/空集合:)

答案 25 :(得分:1)

List<String> collectionOfStrings = // List of string to concat
String csvStrings = StringUtils.collectionToDelimitedString(collectionOfStrings, ",");

springframeowrk的StringUtils:spring-core

答案 26 :(得分:0)

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 :(得分:0)

String commaSeparatedNames = namesList.toString().replaceAll( "[\\[|\\]| ]", "" );  // replace [ or ] or blank
  

字符串表示由集合列表组成   元素按它们的迭代器返回的顺序,包含在中   方括号 (”[]”)。相邻的元素由   字符“,”(逗号和空格)。

     

AbstractCollection javadoc

答案 28 :(得分:0)

List token = new ArrayList(result); 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 :(得分:0)

您可以使用LINQ(对SQL),并且您可以使用MS的Dynamic Query LINQ示例。 http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

答案 30 :(得分:0)

有一种简单的方法。您可以在一行中获得结果。

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+" ";
    }

答案 31 :(得分:0)

将android转换为逗号分隔字符串的最简单方法是使用android.text.TextUtils

 ArrayList<String>Myli = new ArrayList<String>();
 String ArayCommase=android.text.TextUtils.join(",", Myli);