Stringオブジェクトのリストを連結する最良の方法は何ですか?私はこのようにすることを考えています:
List<String> sList = new ArrayList<String>();
// add elements
if (sList != null)
{
String listString = sList.toString();
listString = listString.subString(1, listString.length() - 1);
}
StringBuilder/StringBufferアプローチを使用するよりも、これがなんとなくきれいだと思いました。
意見/コメントはありますか?
あなたのアプローチは、JavaのArrayList#toString()実装に依存しています。
実装はJava APIで文書化されており、変更されることはほとんどありませんが、変更される可能性があります。これを自分で実装する方がはるかに信頼性が高くなります(ループ、StringBuilder、再帰、好きなものは何でも)。
確かに、このアプローチは「すてき」または「甘すぎる」または「お金」に見えるかもしれませんが、私の意見では、より悪いアプローチです。
Apache Commons Langの StringUtils.join メソッドのいずれかを使用します。
import org.Apache.commons.lang3.StringUtils;
String result = StringUtils.join(list, ", ");
Java 8を使用できるほど幸運であれば、それはさらに簡単です... String.join を使用するだけです
String result = String.join(", ", list);
Java 8+を使用する
String str = list.stream().collect(Collectors.joining())
あるいは
String str = String.join("", list);
Codefinの答えのバリエーション
public static String concatStringsWSep(Iterable<String> strings, String separator) {
StringBuilder sb = new StringBuilder();
String sep = "";
for(String s: strings) {
sb.append(sep).append(s);
sep = separator;
}
return sb.toString();
}
Android向けに開発している場合、SDKによって TextUtils.join
が提供されます。
これは私がこれまでに見つけた最もエレガントでクリーンな方法です。
list.stream().collect(Collectors.joining(delimiter));
Guava はGoogleの非常に洗練されたライブラリです。
Joiner joiner = Joiner.on(", ");
joiner.join(sList);
Java 8のString.join(list)を好みます
このCoding Horrorのブログエントリを見たことがありますか?
「きれい」であるかどうかはわかりませんが、パフォーマンスの観点からはおそらく重要ではありません。
StringBuilderが迅速かつ効率的になるように思えます。
基本的なフォームは次のようになります。
public static String concatStrings(List<String> strings)
{
StringBuilder sb = new StringBuilder();
for(String s: strings)
{
sb.append(s);
}
return sb.toString();
}
それがあまりにも単純すぎる(そしておそらくそうである)場合は、同様のアプローチを使用して、次のようなセパレーターを追加できます。
public static String concatStringsWSep(List<String> strings, String separator)
{
StringBuilder sb = new StringBuilder();
for(int i = 0; i < strings.size(); i++)
{
sb.append(strings.get(i));
if(i < strings.size() - 1)
sb.append(separator);
}
return sb.toString();
}
JavaのArrayListのtoString()メソッドに頼るべきではないと言うとき、この質問に答えた他の人たちに同意します。
Java 8を使用している場合、ArrayList.toString()
実装に依存するのではなく、ワンライナーを記述できます。
String result = sList.stream()
.reduce("", String::concat);
String::concat
のランタイムはO(n^2)
であるため、Stringの代わりにStringBuffer
を使用する場合、最初にすべてのString
をStringBuffer
に変換できます。
StringBuffer result = sList.stream()
.map(StringBuffer::new)
.reduce(new StringBuffer(""), StringBuffer::append);
StringBuilder/StringBufferアプローチを使用するよりも、これがなんとなくきれいだと思いました。
それはあなたがどのアプローチを取ったかにかかっていると思います。
AbstractCollection#toString()メソッドは、すべての要素を単純に反復処理し、それらをStringBuilderに追加します。そのため、メソッドでは数行のコードを保存できますが、余分な文字列操作が必要になります。そのトレードオフが良いものであるかどうかはあなた次第です。
ArrayList
はtoString()
- methodを AbstractCollection
から継承します。つまり:
public String toString() {
Iterator<E> i = iterator();
if (! i.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = i.next();
sb.append(e == this ? "(this Collection)" : e);
if (! i.hasNext())
return sb.append(']').toString();
sb.append(", ");
}
}
自分で文字列を構築する方がはるかに効率的です。
何らかの種類のリストで文字列を事前に集約したい場合は、独自のメソッドを提供してそれらを効率的に結合する必要があります。このような:
static String join(Collection<?> items, String sep) {
if(items.size() == 0)
return "";
String[] strings = new String[items.size()];
int length = sep.length() * (items.size() - 1);
int idx = 0;
for(Object item : items) {
String str = item.toString();
strings[idx++] = str;
length += str.length();
}
char[] chars = new char[length];
int pos = 0;
for(String str : strings) {
str.getChars(0, str.length(), chars, pos);
pos += str.length();
if(pos < length) {
sep.getChars(0, sep.length(), chars, pos);
pos += sep.length();
}
}
return new String(chars);
}
ループターンごとに新しい文字列を初期化しないピーターローリーの答えの次のバリエーション
String concatList(List<String> sList, String separator)
{
Iterator<String> iter = sList.iterator();
StringBuilder sb = new StringBuilder();
while (iter.hasNext())
{
sb.append(iter.next()).append( iter.hasNext() ? separator : "");
}
return sb.toString();
}
区切り文字を追加するタイミングをループで毎回確認するのではなく、ポインタを移動する/バイトをnullに設定する(またはJavaがStringBuilder#setLengthを実装する)方が速いと仮定すると、この方法を使用します。
public static String Intersperse(Collection <?> collection、String delimiter) { StringBuilder sb = new StringBuilder(); for(Object item:collection) { if(item == null)continue; sb.append(item).append(delimiter); } sb.setLength(sb.length ()-delimiter.length()); return sb.toString(); }
依存関係にjsonがある場合。new JSONArray(list).toString()
を使用できます
パフォーマンスの必要性と追加する要素の量に応じて、これは適切なソリューションになる場合があります。要素の量が多い場合、Arraylist
sのメモリの再割り当ては、StringBuilder
よりも少し遅くなる可能性があります。
Java 8では、次のようなレデューサーを使用することもできます。
public static String join(List<String> strings, String joinStr) {
return strings.stream().reduce("", (prev, cur) -> prev += (cur + joinStr));
}
Functional Java ライブラリを使用して、これらをインポートします。
import static fj.pre.Monoid.stringMonoid;
import static fj.data.List.list;
import fj.data.List;
...その後、これを行うことができます:
List<String> ss = list("foo", "bar", "baz");
String s = stringMonoid.join(ss, ", ");
または、一般的な方法、文字列のリストがない場合:
public static <A> String showList(List<A> l, Show<A> s) {
return stringMonoid.join(l.map(s.showS_()), ", ");
}