การใช้ 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()วิธีการจากกรณีในบรรทัดเดียว

List<Integer> list = ...

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

สมมติว่าlistมีจำนวนเต็ม1, 2และ3ผมคาดหวังว่าconcatenatedจะเป็นหรือ"123""1,2,3"


สำเนาซ้ำที่เป็นไปได้ของJava: แปลง List <String> เป็น String
Vadzim

คำตอบ:


369

วิธีง่ายๆในการผนวกรายการในรายการของคุณคือ 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 reductionทำงานใน O (n 2 )

เพื่อประสิทธิภาพที่ดีขึ้นให้ใช้StringBuilderหรือmutable reductionคล้ายกับคำตอบของ F. Böller

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

Ref: Stream Reduction


2
ใช่ไม่ใช่โซลูชั่นแบบอินไลน์ แต่เป็นวิธีแก้ปัญหา
mat_boy

ไม่เข้าใจ คุณคาดหวังอะไร
Shail016

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

194

มีตัวรวบรวมjoiningใน API มันเป็นวิธีการแบบคงที่Collectorsมา

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

ไม่สมบูรณ์เพราะการโทรที่จำเป็นtoStringแต่ใช้ได้ผล ตัวคั่นที่แตกต่างกันเป็นไปได้


7
เพื่อความสมบูรณ์: เพื่อให้ได้รหัสที่แน่นอนในการทำงานคุณต้องimport static java.util.stream.Collectors.joining;
Mahdi

7
ทำไมเราต้องมีการแมปและ `toString 'อย่างชัดเจน? หากนักสะสมคาดหวังว่าองค์ประกอบของสตริงการแปลงควรจะถูกเรียกใช้โดยปริยายไม่ใช่!
Basel Shishani

10

ในกรณีที่ทุกคนพยายามทำโดยไม่ใช้ java 8 มีเคล็ดลับที่ดีงาม List.toString () ส่งคืนคอลเล็กชันที่มีลักษณะดังนี้:

[1,2,3]

ทั้งนี้ขึ้นอยู่กับข้อกำหนดเฉพาะของคุณสิ่งนี้สามารถนำมาประมวลผลภายหลังเป็นสิ่งที่คุณต้องการตราบใดที่รายการของคุณไม่มี [] หรือ,

ตัวอย่างเช่น

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

หรือหากข้อมูลของคุณอาจมีวงเล็บเหลี่ยมอยู่:

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

คุณจะได้รับผลลัพธ์ที่สมเหตุสมผล

รายการอาร์เรย์หนึ่งรายการในแต่ละบรรทัดสามารถสร้างได้ดังนี้:

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

ฉันใช้เทคนิคนี้เพื่อสร้างคำแนะนำเครื่องมือ HTML จากรายการในแอปขนาดเล็กโดยมีลักษณะดังนี้:

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

หากคุณมีอาร์เรย์ให้เริ่มต้นด้วย Arrays.asList (รายการ) .toString () แทน

ฉันจะเป็นเจ้าของความจริงทั้งหมดว่าสิ่งนี้ไม่เหมาะสม แต่ก็ไม่มีประสิทธิภาพเท่าที่คุณคิดและตรงไปตรงมาเพื่ออ่านและทำความเข้าใจ อย่างไรก็ตามมันค่อนข้างยืดหยุ่นได้ - โดยเฉพาะอย่าพยายามแยกองค์ประกอบด้วย replaceAll หากข้อมูลของคุณอาจมีเครื่องหมายจุลภาคและใช้เวอร์ชันย่อยถ้าคุณมีวงเล็บเหลี่ยมในข้อมูลของคุณ แต่สำหรับอาร์เรย์ของตัวเลขมันค่อนข้างสวยมาก สมบูรณ์


ขณะนี้มักจะทำงานก็ไม่ได้รับประกันว่าจะให้ - ไม่บังคับโครงสร้างของ List อย่างไรก็ตามใช้โครงสร้างนี้เป็นค่าเริ่มต้นและฉันคิดว่าการใช้งานทั่วไปทั้งหมดใน Java SE ก็ทำได้เช่นกัน เป็นตัวอย่างของสิ่งที่ไม่ได้เกิดขึ้นในฤดูใบไม้ผลิที่ไม่ได้ใช้งานและจะกลับมาเช่น " " toString()AbstractCollectionListorg.springframework.util.AutoPopulatingListtoString()org.springframework.util.AutoPopulatingList@170a1
M. Justin

Arrays.toString()จะเป็นตัวเลือกที่ดีกว่าArrays.asList(list).toString()เนื่องจากมีการกำหนดให้ส่งคืนสตริงที่เทียบเท่ามันกระชับกว่าและไม่ต้องการการสร้างวัตถุเพิ่มเติม
M. Justin

@ M.Justin ไม่ใช่จุดที่ไม่ดีสำหรับส่วนอาร์เรย์ของคำตอบนี้ แต่สิ่งที่เกี่ยวกับ "รายการ" ธรรมดา? คุณไม่สามารถใช้ Arrays.toString () กับสิ่งนั้นได้ คุณจะแนะนำให้ใช้เทคนิคนี้กับรายการ AutoPopulatingList ที่คุณพูดถึงอย่างไร อาจจะ: ArrayList ใหม่ (autoPopulatingList) .toString () หรือไม่ หรือฉันเดาว่าคุณสามารถแปลงมันเป็นอาร์เรย์ บางทีถ้าคุณวิ่งเข้าไปในปัญหาดังกล่าวคุณควรหวังว่าคุณจะอยู่ใน 1.8 หรือในภายหลังและสามารถใช้เป็นหนึ่งในคำตอบอื่น ๆ ในหัวข้อนี้ ...
บิล K

5

คำตอบอื่น ๆ ก็โอเค อย่างไรก็ตามคุณยังสามารถส่งผ่านCollector.toList ()เป็นพารามิเตอร์ไปที่Stream.collect ()เพื่อส่งคืนองค์ประกอบเป็น ArrayList

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

ถ้าคุณใช้System.out.print(ln) (list)มันจะใช้toString()วิธีการขององค์ประกอบในการพิมพ์รายการ ดังนั้นโค้ดชิ้นนี้เพียงแค่ทำซ้ำสิ่งที่เกิดขึ้นภายในtoStringรายการ
Shirkam

1
ควรเป็น Collector.toList () เว้นแต่ว่าคุณจะนำเข้าแบบคงที่
mwieczorek

ใช่ ... ฉันนำเข้าแบบคงที่สำหรับตัวอย่าง ฉันระบุว่า "Collector.toList ()" ในส่วนของคำตอบ ... ;)
Eddie B


2

มีวิธีใน String API สำหรับการ "เข้าร่วมรายการสตริง" ซึ่งคุณไม่จำเป็นต้องมีแม้แต่สตรีม

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);

ที่นี่ฟังก์ชั่นการลดให้คุณมีค่าเริ่มต้นที่คุณต้องการผนวกสตริงใหม่ตัวอย่าง:

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

1

การทดสอบทั้งสองวิธีที่แนะนำในShail016และbpedroso answer ( 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 dev ของฉัน 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

1
@CalebKleveter มันลด a Listไปเป็นหนึ่งStringและแยกแต่ละองค์ประกอบด้วยเครื่องหมายจุลภาค ( ,)
Joe Almore

2
โซลูชันของคุณใช้งานได้หากรายการคือรายการ <String> OP ไม่ได้ระบุคลาสดังกล่าว ในคำถามของเขาแม้แต่รายการ <Integer> ก็ชี้ไป รหัสของคุณไม่ได้รวบรวมในกรณีนั้น
RubioRic

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);

โซลูชันของคุณใช้งานได้หากรายการคือรายการ <String> OP ไม่ได้ระบุคลาสดังกล่าว ในคำถามของเขาแม้แต่รายการ <Integer> ก็ชี้ไป
RubioRic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.