Javaで区切られたアイテムの文字列を作成する最良の方法は何ですか?


317

最近、Javaアプリで作業しているときに、コンマで区切られた値のリストを組み立てて、事前に存在する要素の数を知らずに別のWebサービスに渡す必要がありました。頭のてっぺんから思いつくことができる最高のものは、次のようなものでした。

public String appendWithDelimiter( String original, String addition, String delimiter ) {
    if ( original.equals( "" ) ) {
        return addition;
    } else {
        return original + delimiter + addition;
    }
}

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

あちこちに文字列が作成されているため、これは特に効率的ではないことに気付きましたが、最適化よりも明確にするために行っていました。

Rubyでは、代わりに次のようなことを行うことができます。

parameterArray = [];
parameterArray << "elementName" if condition;
parameterArray << "anotherElementName" if anotherCondition;
parameterString = parameterArray.join(",");

しかし、Javaには結合コマンドがないため、同等のものを見つけることができませんでした。

それで、これをJavaで行うための最良の方法は何ですか?


StringbBilderは、java.lang.StringBuilderを使用する方法です。
Yusubov

Java 8の場合は、この回答を確認してください:stackoverflow.com/a/22577623/1115554
micha

回答:


542

Java 8より前:

Apacheのcommons langはここでの友です-Rubyで参照する方法と非常によく似たjoinメソッドを提供します。

StringUtils.join(java.lang.Iterable,char)


Java 8:

Java 8ではStringJoiner、とを介してすぐに参加できますString.join()。以下のスニペットは、それらの使用方法を示しています。

StringJoiner

StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"

String.join(CharSequence delimiter, CharSequence... elements))

String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"

String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)

List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"

2
疑問に思っています-コレクション内のオブジェクトの文字列表現に区切り文字自体が含まれている場合、これは考慮されますか?
GreenieMeanie 2009

4
まさに私が探していたもの:パッケージorg.apache.commons.lang3.StringUtilsからのStringUtils.join(java.util.Collection、String)、jarファイルはcommons-lang3-3.0.1.jar
Umar

108
Androidでは、TextUtils.join()も使用できます。
James Wald

3
それは最善の方法ではありません。コレクション内のオブジェクトがnull /空の場合でも、常に文字が追加されます。見た目もすっきりしていますが、二重の区切り文字が出力されることがあります。
Stephan Schielke 2012

52

java.util.Listsで機能する小さな結合スタイルのユーティリティメソッドを書くことができます。

public static String join(List<String> list, String delim) {

    StringBuilder sb = new StringBuilder();

    String loopDelim = "";

    for(String s : list) {

        sb.append(loopDelim);
        sb.append(s);            

        loopDelim = delim;
    }

    return sb.toString();
}

次に、次のように使用します。

    List<String> list = new ArrayList<String>();

    if( condition )        list.add("elementName");
    if( anotherCondition ) list.add("anotherElementName");

    join(list, ",");

2
少なくとも2つの実装(apacheとguava)が既に存在する場合、なぜ独自のメソッドを作成する必要があるのですか?
Timofey 2012年

29
これは、たとえば、外部依存関係を減らしたい場合に役立ちます。
Thomas Zumbrunn 2013

2
条件の代わりにloopDelimがどのように使用されるかが好きです。これは一種のハックですが、サイクルから条件付きステートメントを削除します。イテレータを使用して、サイクルの前に最初の要素を追加することを好みますが、これは確かに素晴らしいハックです。
Vlasec、2015年

List<String>初期化子をに変更しIterator<?>て、同じ効果を得ることができませんか?
k_g

@Tim Eg apacheは空の文字列をスキップしません。
banterCZ


31

Googleのグアバライブラリがありcom.google.common.base.Joinerのようなタスクを解決するのに役立ちますクラスを。

サンプル:

"My pets are: " + Joiner.on(", ").join(Arrays.asList("rabbit", "parrot", "dog")); 
// returns "My pets are: rabbit, parrot, dog"

Joiner.on(" AND ").join(Arrays.asList("field1=1" , "field2=2", "field3=3"));
// returns "field1=1 AND field2=2 AND field3=3"

Joiner.on(",").skipNulls().join(Arrays.asList("London", "Moscow", null, "New York", null, "Paris"));
// returns "London,Moscow,New York,Paris"

Joiner.on(", ").useForNull("Team held a draw").join(Arrays.asList("FC Barcelona", "FC Bayern", null, null, "Chelsea FC", "AC Milan"));
// returns "FC Barcelona, FC Bayern, Team held a draw, Team held a draw, Chelsea FC, AC Milan"

これはGuavaの文字列ユーティリティに関する記事です。


26

Java 8では次を使用できますString.join()

List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"

また、見ていこの回答ストリームAPIの例を。


20

あなたはそれを一般化することができますが、よく言われるように、Javaには結合はありません。

これでうまくいくかもしれません。

public static String join(Iterable<? extends CharSequence> s, String delimiter) {
    Iterator<? extends CharSequence> iter = s.iterator();
    if (!iter.hasNext()) return "";
    StringBuilder buffer = new StringBuilder(iter.next());
    while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
    return buffer.toString();
}

この回答には同意しますが、AbstractCollection <String>ではなくCollection <String>を受け入れるように誰かが署名を編集できますか?コードの残りの部分は同じでなければなりませんが、AbstractCollectionは、ここでは重要ではない実装の詳細だと思います。
無法者プログラマ

さらに良いのは、 Iterable<String>ことに、それを反復できるという事実を使用してください。あなたの例では、コレクション内のアイテムの数は必要ないので、これはさらに一般的です。
ジェイソンコーエン

1
またはさらに良いことに、Iterable <?Charsequence>を拡張し、StringBuildersとStringsのコレクション、およびストリームとその他の文字列のようなものを受け入れることができます。:)
ジェイソンコーエン

2
StringBuilder代わりに使用したい。StringBuffer不必要なスレッドセーフを提供することを除いて、それらは同じです。誰かが編集してください!
Casebash 2010

1
このコードにはいくつかのエラーがあります。1. CharSequenceには大文字のsがあります。2. s.iterator()はIterator <?を返します CharSequence>を拡張します。3. IterableにはisEmpty()メソッドがありません。next()代わりにメソッドを使用してください
Casebash

17

Java 8では、次のようにできます。

list.stream().map(Object::toString)
        .collect(Collectors.joining(delimiter));

リストにnullがある場合は、次を使用できます。

list.stream().map(String::valueOf)
        .collect(Collectors.joining(delimiter))

また、プレフィックスとサフィックスもサポートしています。

list.stream().map(String::valueOf)
        .collect(Collectors.joining(delimiter, prefix, suffix));

15

に基づくアプローチを使用してくださいjava.lang.StringBuilder!(「変更可能な文字シーケンス。」)

あなたが言ったように、それらの文字列連結はすべて文字列を作成しています。 StringBuilderそれはしません。

なぜStringBuilder代わりにStringBufferStringBuilderjavadoc から:

ほとんどの実装では高速になるため、可能であれば、このクラスをStringBufferよりも優先して使用することをお勧めします。


4
うん。また、StringBufferはスレッドセーフですが、StringBuilderはスレッドセーフではありません。
Jon Onstott 2013年

10

Googleコレクションを使用します。素敵な参加機能があります。
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

でも自分で書こうと思ったら

package util;

import java.util.ArrayList;
import java.util.Iterable;
import java.util.Collections;
import java.util.Iterator;

public class Utils {
    // accept a collection of objects, since all objects have toString()
    public static String join(String delimiter, Iterable<? extends Object> objs) {
        if (objs.isEmpty()) {
            return "";
        }
        Iterator<? extends Object> iter = objs.iterator();
        StringBuilder buffer = new StringBuilder();
        buffer.append(iter.next());
        while (iter.hasNext()) {
            buffer.append(delimiter).append(iter.next());
        }
        return buffer.toString();
    }

    // for convenience
    public static String join(String delimiter, Object... objs) {
        ArrayList<Object> list = new ArrayList<Object>();
        Collections.addAll(list, objs);
        return join(delimiter, list);
    }
}

結合する前にオブジェクトを文字列に変換する必要がないので、オブジェクトコレクションを使用するほうがうまくいくと思います。


8

Apache commons StringUtilsクラスにはjoinメソッドがあります。



3

StringBuilderこれにはJavaのタイプを使用できます。もありますがStringBuffer、多くの場合不要な追加のスレッドセーフティロジックが含まれています。


3

そして、最小限のもの(Apache CommonsまたはGauvaをプロジェクトの依存関係に含めたくない場合は、文字列を結合するためだけに)

/**
 *
 * @param delim : String that should be kept in between the parts
 * @param parts : parts that needs to be joined
 * @return  a String that's formed by joining the parts
 */
private static final String join(String delim, String... parts) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < parts.length - 1; i++) {
        builder.append(parts[i]).append(delim);
    }
    if(parts.length > 0){
        builder.append(parts[parts.length - 1]);
    }
    return builder.toString();
}

File.separatorの代わりにdelimを使用してください。
Pradeep Kumar 2015

3

StringBuilderとクラスを使用する Separator

StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list) {
    buf.append(sep).append(each);
}

区切り文字は区切り文字をラップします。toString空の文字列を返す最初の呼び出しでない限り、区切り文字はSeparatorのメソッドによって返されます。

クラスのソースコード Separator

public class Separator {

    private boolean skipFirst;
    private final String value;

    public Separator() {
        this(", ");
    }

    public Separator(String value) {
        this.value = value;
        this.skipFirst = true;
    }

    public void reset() {
        skipFirst = true;
    }

    public String toString() {
        String sep = skipFirst ? "" : value;
        skipFirst = false;
        return sep;
    }

}

連結でSeparatorStringBuilderなくを使用するように変更するとどうなりStringsますか?
Mohamed Nuur

@Mohamed Separatorは区切り文字を返すだけで、文字列自体を連結しません。
akuhn

このクラスは何ですかSeparator?単純な文字列を使用しないでください。
maxxyme 2016

2

独自のjoin()メソッドを作成してみませんか?これは、文字列のコレクションと区切り文字列をパラメーターとして受け取ります。メソッド内でコレクションを反復処理し、結果をStringBufferに構築します。


2

Spring MVCを使用している場合は、次の手順を試すことができます。

import org.springframework.util.StringUtils;

List<String> groupIds = new List<String>;   
groupIds.add("a");    
groupIds.add("b");    
groupIds.add("c");

String csv = StringUtils.arrayToCommaDelimitedString(groupIds.toArray());

それは結果になります a,b,c


2

あなたが使用している場合はEclipseのコレクションを、あなたが使用することができますmakeString()appendString()

makeString()Stringと同様の表現を返しますtoString()

3つの形があります

  • makeString(start, separator, end)
  • makeString(separator) デフォルトは空の文字列で始まり、終わります
  • makeString()区切り文字のデフォルトは", "(カンマとスペース)

コード例:

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
assertEquals("[1/2/3]", list.makeString("[", "/", "]"));
assertEquals("1/2/3", list.makeString("/"));
assertEquals("1, 2, 3", list.makeString());
assertEquals(list.toString(), list.makeString("[", ", ", "]"));

appendString()はに似てmakeString()いますが、Appendable(のようにStringBuilder)に追加され、ですvoid。これは同じ3つの形式で、最初の引数Appendableが追加されています。

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
Appendable appendable = new StringBuilder();
list.appendString(appendable, "[", "/", "]");
assertEquals("[1/2/3]", appendable.toString());

コレクションをEclipseコレクションタイプに変換できない場合は、関連するアダプターを使用して適合させてください。

List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");

注:私はEclipseコレクションのコミッターです。


2

Java 8ネイティブタイプ

List<Integer> example;
example.add(1);
example.add(2);
example.add(3);
...
example.stream().collect(Collectors.joining(","));

Java 8カスタムオブジェクト:

List<Person> person;
...
person.stream().map(Person::getAge).collect(Collectors.joining(","));

1

おそらくメソッドでStringBuilderwithを使用しappendて結果を構築する必要がありますが、それ以外の場合は、Javaが提供するのと同じくらい優れたソリューションです。


1

Javaで、Rubyで行っているのと同じことをしませんか。つまり、すべての断片を配列に追加した後でのみ、区切り文字で区切られた文字列を作成しますか。

ArrayList<String> parms = new ArrayList<String>();
if (someCondition) parms.add("someString");
if (anotherCondition) parms.add("someOtherString");
// ...
String sep = ""; StringBuffer b = new StringBuffer();
for (String p: parms) {
    b.append(sep);
    b.append(p);
    sep = "yourDelimiter";
}

別のヘルパーメソッドでforループを移動し、StringBufferの代わりにStringBuilderを使用することもできます...

編集:追加の順序を修正しました。


はい、(Java 1.5以降を使用しているため)StringBuilderの代わりにStringBufferを使用する理由は何ですか?また、間違った方法でアペンドを使用しています。
トム・ホーティン-タックライン

1

Java 5変数の引数を使用するため、すべての文字列をコレクションまたは配列に明示的に詰め込む必要はありません。

import junit.framework.Assert;
import org.junit.Test;

public class StringUtil
{
    public static String join(String delim, String... strings)
    {
        StringBuilder builder = new StringBuilder();

        if (strings != null)
        {
            for (String str : strings)
            {
                if (builder.length() > 0)
                {
                    builder.append(delim).append(" ");
                }
                builder.append(str);
            }
        }           
        return builder.toString();
    }
    @Test
    public void joinTest()
    {
        Assert.assertEquals("", StringUtil.join(",", null));
        Assert.assertEquals("", StringUtil.join(",", ""));
        Assert.assertEquals("", StringUtil.join(",", new String[0]));
        Assert.assertEquals("test", StringUtil.join(",", "test"));
        Assert.assertEquals("foo, bar", StringUtil.join(",", "foo", "bar"));
        Assert.assertEquals("foo, bar, x", StringUtil.join(",", "foo", "bar", "x"));
    }
}

1

Springコンテキストにいる人のために、StringUtilsクラスも役立ちます。

次のような多くの便利なショートカットがあります。

  • collectionToCommaDelimitedString(コレクションcoll)
  • collectionToDelimitedString(Collection coll、String delim)
  • arrayToDelimitedString(Object [] arr、String delim)

その他多数。

これは、Java 8をまだ使用しておらず、すでにSpringコンテキストにいる場合に役立ちます。

私は、次のように簡単なコレクションサポートのために、Apache Commons(非常に良いものの)に対してそれを好みます。

// Encoding Set<String> to String delimited 
String asString = org.springframework.util.StringUtils.collectionToDelimitedString(codes, ";");

// Decoding String delimited to Set
Set<String> collection = org.springframework.util.StringUtils.commaDelimitedListToSet(asString);

0

あなたはこのようなことを試すことができます:

StringBuilder sb = new StringBuilder();
if (condition) { sb.append("elementName").append(","); }
if (anotherCondition) { sb.append("anotherElementName").append(","); }
String parameterString = sb.toString();

これは、文字列の末尾にカンマが残るように見えるため、回避したいと考えていました。(もちろん、そこにあることがわかっているので、それをトリミングすることもできますが、これも少しエレガントではありません。)
Sean McMains

0

だから基本的には次のようなもの:

public static String appendWithDelimiter(String original, String addition, String delimiter) {

if (original.equals("")) {
    return addition;
} else {
    StringBuilder sb = new StringBuilder(original.length() + addition.length() + delimiter.length());
        sb.append(original);
        sb.append(delimiter);
        sb.append(addition);
        return sb.toString();
    }
}

ここでの問題は、彼がappendWithDelimiter()allotを呼び出しているように見えることです。ソリューションは、1つのStringBufferのみをインスタンス化し、その単一のインスタンスで機能する必要があります。
Stu Thompson、

0

これが本当に優れているかどうかはわかりませんが、少なくともStringBuilderを使用しているため、多少効率が良くなる可能性があります。

以下は、パラメーターの区切りを行う前にパラメーターのリストを作成できる場合のより一般的な方法です。

// Answers real question
public String appendWithDelimiters(String delimiter, String original, String addition) {
    StringBuilder sb = new StringBuilder(original);
    if(sb.length()!=0) {
        sb.append(delimiter).append(addition);
    } else {
        sb.append(addition);
    }
    return sb.toString();
}


// A more generic case.
// ... means a list of indeterminate length of Strings.
public String appendWithDelimitersGeneric(String delimiter, String... strings) {
    StringBuilder sb = new StringBuilder();
    for (String string : strings) {
        if(sb.length()!=0) {
            sb.append(delimiter).append(string);
        } else {
            sb.append(string);
        }
    }

    return sb.toString();
}

public void testAppendWithDelimiters() {
    String string = appendWithDelimitersGeneric(",", "string1", "string2", "string3");
}

0

あなたのアプローチはそれほど悪くはありませんが、+記号を使用する代わりにStringBufferを使用する必要があります。+には、単一の操作ごとに新しいStringインスタンスが作成されるという大きな欠点があります。文字列が長くなるほど、オーバーヘッドが大きくなります。したがって、StringBufferを使用するのが最も速い方法です。

public StringBuffer appendWithDelimiter( StringBuffer original, String addition, String delimiter ) {
        if ( original == null ) {
                StringBuffer buffer = new StringBuffer();
                buffer.append(addition);
                return buffer;
        } else {
                buffer.append(delimiter);
                buffer.append(addition);
                return original;
        }
}

文字列の作成が完了したら、返されたStringBufferでtoString()を呼び出すだけです。


1
ここでStringBufferを使用すると、正当な理由なく速度が遅くなるだけです。+演算子を使用すると、内部で高速のStringBuilderが使用されるため、代わりにStringBufferを使用することで、必要のないスレッドセーフだけを獲得できます。
フレドリク

また...「+には、単一の操作ごとに新しいStringインスタンスが作成されるという大きな欠点がある」という文は誤りです。コンパイラーはそれらからStringBuilder.append()呼び出しを生成します。
フレドリク

0

文字列連結を使用する代わりに、コードがスレッド化されていない場合はStringBuilderを使用し、スレッド化されている場合はStringBufferを使用する必要があります。


0

これを必要以上に複雑にしています。あなたの例の終わりから始めましょう:

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Stringの代わりにStringBuilderを使用する小さな変更により、次のようになります。

StringBuilder parameterString = new StringBuilder();
if (condition) parameterString.append("elementName").append(",");
if (anotherCondition) parameterString.append("anotherElementName").append(",");
...

完了したら(他のいくつかの条件も確認する必要があると思います)、次のようなコマンドを使用して、末尾のコンマを必ず削除してください。

if (parameterString.length() > 0) 
    parameterString.deleteCharAt(parameterString.length() - 1);

そして最後に、必要な文字列を取得します

parameterString.toString();

追加する2回目の呼び出しで "、"を置き換えて、任意に設定できる汎用の区切り文字列を追加することもできます。(無条件に)追加する必要があることがわかっているもののリストがある場合、このコードを文字列のリストを取得するメソッド内に配置できます。


0
//Note: if you have access to Java5+, 
//use StringBuilder in preference to StringBuffer.  
//All that has to be replaced is the class name.  
//StringBuffer will work in Java 1.4, though.

appendWithDelimiter( StringBuffer buffer, String addition, 
    String delimiter ) {
    if ( buffer.length() == 0) {
        buffer.append(addition);
    } else {
        buffer.append(delimiter);
        buffer.append(addition);
    }
}


StringBuffer parameterBuffer = new StringBuffer();
if ( condition ) { 
    appendWithDelimiter(parameterBuffer, "elementName", "," );
}
if ( anotherCondition ) {
    appendWithDelimiter(parameterBuffer, "anotherElementName", "," );
}

//Finally, to return a string representation, call toString() when returning.
return parameterBuffer.toString(); 

0

それで、あなたが探しているように感じるためにあなたがするかもしれないいくつかのこと:

1)リストクラスを拡張し、それに結合メソッドを追加します。joinメソッドは、区切り文字を連結して追加するだけの作業を行います(これは、joinメソッドのパラメーターになる場合があります)。

2)Java 7が拡張メソッドをjavaに追加するようです-これにより、特定のメソッドをクラスにアタッチできるようになります。そのため、その結合メソッドを記述し、拡張メソッドとしてリストに追加したり、コレクション。

ソリューション1はおそらく唯一の現実的なものですが、Java 7はまだリリースされていないためです:)しかし、問題なく動作するはずです。

これらの両方を使用するには、通常どおりすべてのアイテムをリストまたはコレクションに追加し、新しいカスタムメソッドを呼び出してそれらを「結合」します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.