データベースでの作業中に、クエリ文字列を記述することに気付きました。この文字列では、リスト/配列/コレクションのwhere句にいくつかの制限を設定する必要があります。次のようになります。
select * from customer
where customer.id in (34, 26, ..., 2);
文字列のコレクションがあり、この文字列のコンマ区切りリストを1つの文字列で作成するという質問にこれを減らすことで、これを単純化できます。
私がこれまで使用してきたアプローチは次のようなものです。
String result = "";
boolean first = true;
for(String string : collectionOfStrings) {
if(first) {
result+=string;
first=false;
} else {
result+=","+string;
}
}
しかし、これは非常にいものです。特に構築された文字列(すべてのSQLクエリなど)が複雑になっている場合、最初の外観では何が起こるかわかりません。
あなたの(より)エレガントな方法は何ですか?
文字列は不変であるため、コード内の文字列を変更する場合は、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): "";
Google Guava API の join
メソッドを使用します。
Joiner.on(",").join(collectionOfStrings);
今日これを実行したコードを見ました。これは、AviewAnewの回答のバリエーションです。
collectionOfStrings = /* source string collection */;
String csList = StringUtils.join(collectionOfStrings.toArray(), ",");
StringUtils (<-commons.lang 2.x、または commons.lang 3.x link )使用したものは Apache Commons です。
そのループを記述する方法は次のとおりです。
StringBuilder buff = new StringBuilder();
String sep = "";
for (String str : strs) {
buff.append(sep);
buff.append(str);
sep = ",";
}
return buff.toString();
9月のパフォーマンスについては心配しないでください。割り当てはvery fastです。とにかく、ホットスポットはループの最初の反復を剥がす傾向があります(多くの場合、ヌルやモノ/バイモルフィックインラインチェックなどの奇妙なことに対処する必要があるため)。
何度も(複数回)使用する場合は、共有メソッドに入れてください。
IDのリストをSQLステートメントに挿入する方法を扱うstackoverflowには別の質問があります。
Java 8以降、次を使用できます。
String String.join(CharSequence delimiter, CharSequence... elements)
String String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
非String
sを取得してString
に結合する場合は、 Collectors.joining(CharSequence delimiter)
を使用できます。例:
String joined = anyCollection.stream().map(Object::toString).collect(Collectors.joining(","));
イテレータのイディオムは、より多くの要素のテスト(簡潔さのためにnull /空のテストを省略)があるため、エレガントであることがわかりました。
public static String convert(List<String> list) {
String res = "";
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
res += iterator.next() + (iterator.hasNext() ? "," : "");
}
return res;
}
これには多くの手動による解決策がありますが、私は上記のジュリーの答えを繰り返して更新したかったのです。 google collections Joiner class を使用します。
Joiner.on(", ").join(34, 26, ..., 2)
Var args、iterables、arraysを処理し、複数の文字のセパレータを適切に処理します(gimmelの答えとは異なります)。必要に応じて、リスト内のnull値も処理します。
以下は、以前の提案を組み合わせて作成した非常に汎用的なバージョンです。
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);
}
String.join(", ", collectionOfStrings)
java8 APIで利用可能です。
(Google guava依存関係を追加する必要なし)の代替:
Joiner.on(",").join(collectionOfStrings);
試すことができます
List collections = Arrays.asList(34, 26, "...", 2);
String asString = collection.toString();
// justValues = "34, 26, ..., 2"
String justValues = asString.substring(1, asString.length()-1);
これは、GuavaまたはApache Commonsを使用することを除いて、これまでのところ最短のソリューションになります。
String res = "";
for (String i : values) {
res += res.isEmpty() ? i : ","+i;
}
0、1、およびn要素リストで良好です。ただし、nullリストを確認する必要があります。私はこれをGWTで使用しているため、StringBuilderがなくても大丈夫です。そして、いくつかの要素がある短いリストの場合は、他の場所でも問題ありません;)
Androidでは、これを使用する必要があります。
TextUtils.join(",",collectionOfStrings.toArray());
最近誰かがこれに遭遇した場合に備えて、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, ","));
}
}
私はあなたがしているようなwhere句の値を連結するSQLを構築することは良い考えではないと思います:
SELECT.... FROM.... WHERE ID IN( value1, value2,....valueN)
valueX
は文字列のリストに由来します。
まず、文字列を比較する場合、引用符で囲む必要があります。これは、文字列に引用符を含めることができれば簡単ではありません。
次に、値がユーザーまたは他のシステムからのものである場合、SQLインジェクション攻撃が可能です。
もっと冗長ですが、次のようなStringを作成する必要があります。
SELECT.... FROM.... WHERE ID IN( ?, ?,....?)
そして、Statement.setString(nParameter,parameterValue)
で変数をバインドします。
この問題に対処する別の方法。最も短いものではありませんが、効率的であり、仕事を終わらせます。
/**
* 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();
}
Springを使用する場合、次のことができます。
StringUtils.arrayToCommaDelimitedString(
collectionOfStrings.toArray()
)
(パッケージorg.springframework.util)
文字列結合メソッドを提供するサードパーティの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()の使用に関するもう1つの提案は短くなりますが、Collection.toString()が非常に特定の形式の文字列を返すことに依存しているため、個人的には依存したくないでしょう。
配列がある場合、次のことができます。
Arrays.asList(parameters).toString()
これがどの程度「洗練されている」かはわかりませんが、確かに少し短くなっています。さまざまなタイプのコレクションで動作します。 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) + ")";
}
読者のための運動:null /空のコレクションを正しく処理するようにこのメソッドを変更します:)
私がここで見るものに基づいた別のオプション(わずかな変更を加えた)。
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();
}
ライブラリのテストをチェックインしました ドル :
@Test
public void join() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
String string = $(list).join(",");
}
1つの静的インポートのみを使用して、lists/arrays/strings/etcの流なラッパーを作成します:$
。
NB:
範囲を使用して、前のリストを$(1, 5).join(",")
として書き換えることができます
コードが見苦しいのは、最初のケースの特別な処理です。この小さなスニペットのほとんどの行は、コードのルーチンジョブを実行するのではなく、その特殊なケースを処理することに専念しています。そして、それが、特殊な処理をループの外側に移動することにより、ギメルのような代替案が解決するものです。特別なケースが1つあります(まあ、開始と終了の両方を特別なケースとして見ることができます-ただし、そのうちの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のようにデータが似たデータに対してのみ機能するため、非常に奇妙な方法です。
私はあなたの最善の策はグアバのジョイナーを使用することだと思いますが、手作業でコーディングする場合、このアプローチは「最初の」フラグまたは最後のコンマを切るよりもエレガントです。
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();
}
IN式の良いところは、値を繰り返しても結果が変わらないことです。したがって、最初のアイテムを複製し、リスト全体を処理します。これは、リストに少なくとも1つのアイテムがあることを前提としています。項目がない場合は、まずその項目を確認してから、SQLをまったく実行しないことをお勧めします。
これはトリックを行い、それが何をしているのか明らかであり、外部ライブラリに依存しません:
StringBuffer inString = new StringBuffer(listOfIDs.get(0).toString());
for (Long currentID : listOfIDs) {
inString.append(",").append(currentID);
}
LINQ(to 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
List token = new ArrayList(result);最終的な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();
リストをカンマ区切り文字列に変換するためのAndroidで最も簡単な方法は、Android.text.TextUtils
を使用することです
ArrayList<String>Myli = new ArrayList<String>();
String ArayCommase=Android.text.TextUtils.join(",", Myli);
簡単な方法があります。結果は1行で取得できます。
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+" ";
}
String commaSeparatedNames = namesList.toString().replaceAll( "[\\[|\\]| ]", "" ); // replace [ or ] or blank
文字列表現は、イテレータによって返される順序でのコレクションの要素のリストで構成され、角括弧( "[]")で囲まれています。隣接する要素は、文字 "、"(コンマとスペース)で区切られます。
AbstractCollection javadoc
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)