web-dev-qa-db-ja.com

Javaで区切られた項目の文字列を作成するための最良の方法は何ですか?

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にはjoinコマンドがないので、これに相当するものは何も見つけられませんでした。

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

280
Sean McMains

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"
490
Martin Gladdish

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, ",");
52
Rob Dickerson

Androidの場合、commonsのStringUtilsクラスは利用できないので、このために私は使いました

Android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)

http://developer.Android.com/reference/Android/text/TextUtils.html

46
Kevin

GoogleのGuavaライブラリ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の文字列ユーティリティに関する記事 です。

31
Alex K

Java 8では String.join() を使うことができます。

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

Stream APIの例については、 この回答 もご覧ください。

25
micha

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

This がうまくいくかもしれません。

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

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

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

StringBuilder の代わりにStringBufferが使われるのはなぜですか? StringBuilder javadocから:

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

15
Stu Thompson

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

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

listにnullがある場合は、次のものを使用できます。

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

私はGoogle Collectionsを使うでしょう。 Nice Join機能があります。
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);
    }
}

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

10
Eric Normand

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

8
Alejandro

Java 8

stringCollection.stream().collect(Collectors.joining(", "));
4
gstackoverflow

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

/**
 *
 * @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();
}
3
Thamme Gowda

StringBuilderとクラスSeparatorを使う

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

区切り文字は区切り文字を折り返します。最初の呼び出しで空の文字列を返す場合を除き、区切り文字はSeparatorのtoStringメソッドによって返されます。

クラス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;
    }

}
3
akuhn

これには、Javaの StringBuilder タイプを使用できます。 StringBufferもありますが、これは不要な余分なスレッドセーフティロジックを含みます。

3
Kent Boogaart

Eclipse Collections を使用している場合は、makeString()またはappendString()を使用できます。

makeString()は、toString()と同様にString表現を返します。

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です。追加の最初の引数であるAppendableを使って、同じ3つの形式を取ります。

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

自分のコレクションをEclipseのCollections型に変換できない場合は、適切なアダプタでそれを調整してください。

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

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

2
Craig P. Motlin

もしあなたが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
Ankit Lalan

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

2
Dave Costa

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
Mark B

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
Luis Aguilar

Springのコンテキストにいる人には、 StringUtils クラスも便利です。

以下のような便利なショートカットがたくさんあります。

  • collectionToCommaDelimitedString(Collection coll)
  • collectionToDelimitedString(Collection coll、String delim)
  • arrayToDelimitedString(Object [] arr、String delim)

そして他の多くの。

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

私はこれをより簡単にするCollectionサポートのために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);
1
рüффп

おそらくStringBuilderappendメソッドと一緒に使用して結果を構築する必要がありますが、それ以外の場合はJavaが提供するのと同じくらい良い解決策です。

1
newdayrising

Rubyでやっているのと同じこと、つまりすべての要素を配列に追加した後で初めて区切り文字で区切られた文字列を作成することをJavaでやらないでください。

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を使います...

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

1
agnul

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

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

これが本当に優れているかどうかはわかりませんが、少なくとも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
Mikezx6r

あなたのアプローチはそれほど悪くはありませんが、+記号を使う代わりに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()を呼び出すだけです。

0
Yaba
//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
MetroidFan2002

ドル を使うのは簡単です:

String joined = $(aCollection).join(",");

注意:これはArrayや他のデータ型でも動作します

実装

内部的にそれは非常にきちんとしたトリックを使います:

@Override
public String join(String separator) {
    Separator sep = new Separator(separator);
    StringBuilder sb = new StringBuilder();

    for (T item : iterable) {
        sb.append(sep).append(item);
    }

    return sb.toString();
}

クラスSeparatorは、最初に呼び出されたときにだけ空の文字列を返し、次にセパレータを返します。

class Separator {

    private final String separator;
    private boolean wasCalled;

    public Separator(String separator) {
        this.separator = separator;
        this.wasCalled = false;
    }

    @Override
    public String toString() {
        if (!wasCalled) {
            wasCalled = true;
            return "";
        } else {
            return separator;
        }
    }
}
0
dfa

回答を修正するRob Dickerson。

使い方は簡単です。

public static String join(String delimiter, String... values)
{
    StringBuilder stringBuilder = new StringBuilder();

    for (String value : values)
    {
        stringBuilder.append(value);
        stringBuilder.append(delimiter);
    }

    String result = stringBuilder.toString();

    return result.isEmpty() ? result : result.substring(0, result.length() - 1);
}
0
maXp

文字列の連結を使用する代わりに、コードがスレッド化されていない場合はStringBuilderを、そうでない場合はStringBufferを使用してください。

0
Aaron

それで、あなたが探しているように見えるという感覚を得るためにあなたがするかもしれない2、3の事柄:

1)リストクラスを拡張 - そしてそれにjoinメソッドを追加します。 joinメソッドは単にデリミタを連結して追加する作業をするだけです(これはjoinメソッドのパラメータかもしれません)。

2)Java 7はJavaに拡張メソッドを追加しようとしているように見えます - それはあなたがクラスに特定のメソッドをアタッチすることを可能にします:それであなたはそのjoinメソッドを書きそしてListに拡張メソッドとして追加できますコレクション。

Java 7はまだ出ていないので、今のところ、解決策1はおそらく唯一の現実的な解決策です:)しかし、それはうまく機能するはずです。

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

0
user13276

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

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
cjw2600

Izbからのバージョンのわずかな改善[速度]:

public static String join(String[] strings, char del)
{
    StringBuilder sb = new StringBuilder();
    int len = strings.length;

    if(len > 1) 
    {
       len -= 1;
    }else
    {
       return strings[0];
    }

    for (int i = 0; i < len; i++)
    {
       sb.append(strings[i]).append(del);
    }

    sb.append(strings[i]);

    return sb.toString();
}
0
java al

私は個人的には、ロギングの目的で次のような簡単な解決策を頻繁に使用します。

List lst = Arrays.asList("ab", "bc", "cd");
String str = lst.toString().replaceAll("[\\[\\]]", "");
0

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

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