web-dev-qa-db-ja.com

Stringオブジェクトのリストを連結する最良の方法は?

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アプローチを使用するよりも、これがなんとなくきれいだと思いました。

意見/コメントはありますか?

139
Jagmal

あなたのアプローチは、JavaのArrayList#toString()実装に依存しています。

実装はJava AP​​Iで文書化されており、変更されることはほとんどありませんが、変更される可能性があります。これを自分で実装する方がはるかに信頼性が高くなります(ループ、StringBuilder、再帰、好きなものは何でも)。

確かに、このアプローチは「すてき」または「甘すぎる」または「お金」に見えるかもしれませんが、私の意見では、より悪いアプローチです。

45
Jack Leow

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);
316
Jeff Olson

Java 8+を使用する

String str = list.stream().collect(Collectors.joining())

あるいは

String str = String.join("", list);
139
Andrei N

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

Android向けに開発している場合、SDKによって TextUtils.join が提供されます。

27
Chunliang Lyu

これは私がこれまでに見つけた最もエレガントでクリーンな方法です。

list.stream().collect(Collectors.joining(delimiter));
17

Guava はGoogleの非常に洗練されたライブラリです。

Joiner joiner = Joiner.on(", ");
joiner.join(sList);
16
andras

Java 8のString.join(list)を好みます

6
Manish Mulani

このCoding Horrorのブログエントリを見たことがありますか?

マイクロ最適化シアターの悲しい悲劇

「きれい」であるかどうかはわかりませんが、パフォーマンスの観点からはおそらく重要ではありません。

5
sdfx

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()メソッドに頼るべきではないと言うとき、この質問に答えた他の人たちに同意します。

5
codefin

Java 8を使用している場合、ArrayList.toString()実装に依存するのではなく、ワンライナーを記述できます。

String result = sList.stream()
                     .reduce("", String::concat);

String::concatのランタイムはO(n^2)であるため、Stringの代わりにStringBufferを使用する場合、最初にすべてのStringStringBufferに変換できます。

StringBuffer result = sList.stream()
                           .map(StringBuffer::new)
                           .reduce(new StringBuffer(""), StringBuffer::append);
4
Sreekanth

StringBuilder/StringBufferアプローチを使用するよりも、これがなんとなくきれいだと思いました。

それはあなたがどのアプローチを取ったかにかかっていると思います。

AbstractCollection#toString()メソッドは、すべての要素を単純に反復処理し、それらをStringBuilderに追加します。そのため、メソッドでは数行のコードを保存できますが、余分な文字列操作が必要になります。そのトレードオフが良いものであるかどうかはあなた次第です。

4
Kevin

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

ループターンごとに新しい文字列を初期化しないピーターローリーの答えの次のバリエーション

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

区切り文字を追加するタイミングをループで毎回確認するのではなく、ポインタを移動する/バイトを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(); 
}
2
MonkeeSage

依存関係にjsonがある場合。new JSONArray(list).toString()を使用できます

1
programer8

パフォーマンスの必要性と追加する要素の量に応じて、これは適切なソリューションになる場合があります。要素の量が多い場合、Arraylistsのメモリの再割り当ては、StringBuilderよりも少し遅くなる可能性があります。

1
georg

Java 8では、次のようなレデューサーを使用することもできます。

public static String join(List<String> strings, String joinStr) {
    return strings.stream().reduce("", (prev, cur) -> prev += (cur + joinStr));
}
1

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_()), ", ");
}
1
Apocalisp