Java 8を使用してオブジェクトのリストをtoString()メソッドから取得した文字列に変換する


206

Java 8には多くの便利な新機能があります。たとえば、オブジェクトのリストに対してストリームを反復処理して、Objectのインスタンスの特定のフィールドの値を合計できます。例えば

public class AClass {
  private int value;
  public int getValue() { return value; }
}

Integer sum = list.stream().mapToInt(AClass::getValue).sum();

したがって、インスタンスからのメソッドStringの出力をtoString()1行で連結するを構築する方法があるかどうかを尋ねています。

List<Integer> list = ...

String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class

その仮定listの整数を含む123私は期待して、concatenatedあります"123""1,2,3"


回答:


369

簡単な方法の1つは、リストアイテムを StringBuilder

   List<Integer> list = new ArrayList<>();
   list.add(1);
   list.add(2);
   list.add(3);

   StringBuilder b = new StringBuilder();
   list.forEach(b::append);

   System.out.println(b);

あなたも試すことができます:

String s = list.stream().map(e -> e.toString()).reduce("", String::concat);

説明:マップは、整数ストリームをストリングストリームに変換し、その後、すべての要素の連結として縮小されます。

注:これはnormal reductionO(n 2)で実行されます

より良い性能の使用aのStringBuilderまたはmutable reductionF.ボラーの回答に似ています。

String s = list.stream().map(Object::toString).collect(Collectors.joining(","));

参照:ストリーム削減


2
ええ、インラインソリューションではなく、ソリューションです。
mat_boy 14

聞き取れませんでした。何を期待していますか?
Shail016 14

2
なぜ「O(n2)で実行される通常の還元」なのか分かりません。私にはO(n)のように「見える」...
datahaki

2
@datahaki私はJavaの男ではありませんが、反復文字列連結(縮小)では、反復ごとに配列(再)割り当てが必要になるため、O(n ^ 2)になると思います。join反対側では、最後の文字列を格納するのに十分な大きさの単一の配列を事前に割り当ててから、O(n)にします。
Eli Korvigo、

2
非常に良いヒント。たぶん "::"表記を使うと簡単にできるでしょう。だから、list.stream().map(Object::toString).reduce("", String::concat)。使用map e-> e.toString()は少し冗長です。
e2a 2018

194

joiningAPIにはコレクターがあります。これはの静的メソッドですCollectors

list.stream().map(Object::toString).collect(Collectors.joining(","))

の呼び出しが必要なため完璧ではありませんtoStringが、動作します。さまざまな区切り文字が可能です。


7
完全を期すために:仕事への正確なコードを取得するには、必要にimport static java.util.stream.Collectors.joining;
マハディ

7
なぜマッピングと明示的な「toString」が必要なのですか?コレクターがString要素を期待している場合、変換は暗黙的に呼び出される必要があります。
バーゼルシシャニ2016

10

誰かがJava 8なしでこれをやろうとしている場合に備えて、かなり良いトリックがあります。List.toString()はすでに次のようなコレクションを返します。

[1、2、3]

特定の要件に応じて、リストアイテムに[]または、が含まれていない限り、これを必要に応じて後処理できます。

例えば:

list.toString().replace("[","").replace("]","") 

または、データに角括弧が含まれている可能性がある場合は、次のようにします。

String s=list.toString();
s = s.substring(1,s.length()-1) 

かなり合理的な出力が得られます。

各行に1つの配列項目を次のように作成できます。

list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")

このテクニックを使用して、小さなアプリのリストからhtmlツールチップを作成しました。

list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")

配列がある場合は、代わりにArrays.asList(list).toString()から始めます

これは最適ではないという事実を私は完全に所有しますが、あなたが考えるほど非効率的ではなく、読んで理解するのはかなり簡単です。ただし、非常に柔軟性がありません。特に、データにコンマが含まれている可能性がある場合はreplaceAllで要素を分離しようとせず、データに角かっこがある場合は部分文字列バージョンを使用しますが、数値の配列の場合はかなり完璧。


これは通常は機能しますが、保証はされListません— の構造を強制しませんtoString()AbstractCollectionただし、デフォルトではこの構造を使用Listしており、Java SEのすべての汎用実装でも同様に使用できると思います。org.springframework.util.AutoPopulatingList実装しない例として、Springでは実装されていないためtoString()、たとえば " org.springframework.util.AutoPopulatingList@170a1" が返されます。
M.ジャスティン

Arrays.toString()Arrays.asList(list).toString()同等の文字列を返すように定義されており、より簡潔であり、追加のオブジェクトを作成する必要がないため、よりも良い選択です。
M.ジャスティン

@ M.Justinこの回答の配列部分の悪い点ではありませんが、単純な「リスト」はどうですか?その上でArrays.toString()を実際に使用することはできません。あなたが言及したAutoPopulatingListのようなものにこのテクニックを使うことをどのように提案しますか?たぶん:new ArrayList(autoPopulatingList).toString()?または、配列に変換できると思います。おそらく、そのような問題に遭遇した場合は、1.8以降であり、このスレッドで他の回答の1つを使用できると思います...
Bill K

5

他の答えは結構です。ただし、Collectors.toList()をパラメーターとしてStream.collect()渡して、要素をArrayListとして返すこともできます。

System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );

あなたが使用している場合はSystem.out.print(LN)を(list)それが使用するtoString()リストを印刷する要素の方法を。したがって、このコードはtoString、リストの内部で発生することを繰り返すだけです。
Shirkam、2017年

1
静的をインポートする場合を除き、Collectors.toList()である必要があります。
mwieczorek

はい...私は例のために静的をインポートしました。私は答えの本文に「Collectors.toList()」と述べました...;)
エディB

2

StringListName = ObjectListName.stream()。map(m-> m.toString()).collect(Collectors.toList());


2

これらの「文字列の結合リスト」ユースケース用のメソッドがString APIにあり、Streamも必要ありません。

List<String> myStringIterable = Arrays.asList("baguette", "bonjour");

String myReducedString = String.join(",", myStringIterable);

// And here you obtain "baguette,bonjour" in your myReducedString variable

1
List<String> list = Arrays.asList("One", "Two", "Three");
    list.stream()
            .reduce("", org.apache.commons.lang3.StringUtils::join);

または

List<String> list = Arrays.asList("One", "Two", "Three");
        list.stream()
                .reduce("", (s1,s2)->s1+s2);

このアプローチでは、オブジェクトのリストから文字列結果を作成することもできます。

List<Wrapper> list = Arrays.asList(w1, w2, w2);
        list.stream()
                .map(w->w.getStringValue)
                .reduce("", org.apache.commons.lang3.StringUtils::join);

ここで、reduce関数を使用すると、新しい文字列を追加する初期値を設定できます。例:

 List<String> errors = Arrays.asList("er1", "er2", "er3");
            list.stream()
                    .reduce("Found next errors:", (s1,s2)->s1+s2);

1

Shail016bpedrosoの回答(https://stackoverflow.com/a/24883180/2832140)で提案されている両方のアプローチをテストすると、ループ内の単純なStringBuilder+ append(String)は、forよりもはるかに速く実行されるようですlist.stream().map([...]

例:このコードは、次のコードMap<Long, List<Long>>を使用して、json文字列をビルドしますlist.stream().map([...]

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");
        sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

私の開発VMでは、junitは通常、テストの実行に0.35〜1.2秒かかります。次のコードを使用すると、0.15〜0.33秒かかります。

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");

        for (Long tid : entry.getValue()) {
            sb.append(tid.toString() + ", ");
        }
        sb.delete(sb.length()-2, sb.length());
        sb.append("]}, ");
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

0

これを試すことができますか?

public static void main(String []args){
        List<String> stringList = new ArrayList<>();
        for(int i=0;i< 10;i++){
            stringList.add(""+i);
        }
        String stringConcated = String.join(",", stringList);
        System.out.println(stringConcated);

    }

0
String actual = list.stream().reduce((t, u) -> t + "," + u).get();

7
通常は、コードがどのように機能するかを説明する説明を投稿に追加することをお勧めします。これにより、新しい開発者はコードの機能を理解できます。
Caleb Kleveter 2017年

1
@CalebKleveterはListを1つに減らし、String各要素をコンマ(,)で区切ります。
Joe Almore 2017年

2
ソリューションが機能するのは、listがList <String>の場合のみです。OPはそのようなクラスを指定しませんでした。彼の質問では、List <Integer>も指摘されています。その場合、コードはコンパイルされません。
RubioRic 2017年

0

私はストリームAPIを使用して、整数のストリームを単一の文字列に変換します。提供された回答のいくつかの問題は、文字列の構築のためにO(n ^ 2)ランタイムが生成されることです。より良い解決策は、StringBuilderを使用して、最後のステップとして文字列を結合することです。

//              Create a stream of integers 
    String result = Arrays.stream(new int[]{1,2,3,4,5,6 })                
            // collect into a single StringBuilder
            .collect(StringBuilder::new, // supplier function
                    // accumulator - converts cur integer into a string and appends it to the string builder
                    (builder, cur) -> builder.append(Integer.toString(cur)),
                    // combiner - combines two string builders if running in parallel
                    StringBuilder::append) 
            // convert StringBuilder into a single string
            .toString();

オブジェクトのコレクションを単一の文字列に変換することにより、このプロセスをさらに一歩進めることができます。

// Start with a class definition
public static class AClass {
    private int value;
    public int getValue() { return value; }
    public AClass(int value) { this.value = value; }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

// Create a stream of AClass objects
        String resultTwo = Arrays.stream(new AClass[]{
                new AClass(1),
                new AClass(2),
                new AClass(3),
                new AClass(4)
        })
                // transform stream of objects into a single string
                .collect(StringBuilder::new,
                        (builder, curObj) -> builder.append(curObj.toString()),
                        StringBuilder::append
                )
            // finally transform string builder into a single string
            .toString();

-1

Java 8以降

String s = Arrays.toString(list.stream().toArray(AClass[]::new));

最も効率的ではありませんが、コードが少ないソリューションです。


-2

また、このようにすることができます。

    List<String> list = Arrays.asList("One", "Two", "Three");
    String result = String.join(", ", list);
    System.out.println(result);

ソリューションが機能するのは、listがList <String>の場合のみです。OPはそのようなクラスを指定しませんでした。彼の質問では、List <Integer>も指摘されています。
RubioRic 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.