Java เทียบเท่ากับ C # String.Format () และ String.Join ()


111

ฉันรู้ว่านี่เป็นคำถามสำหรับมือใหม่ แต่จะเทียบเท่ากับการดำเนินการสตริงของ C # ใน Java หรือไม่

โดยเฉพาะผมกำลังพูดถึงและString.FormatString.Join


ดังนั้นจึงมีString.formatแต่ฉันจะต้องม้วนการเข้าร่วมของตัวเอง
Omar Kooheji

1
สำหรับ join () ฉันชอบคำตอบนี้: stackoverflow.com/a/6116469/562139
scorpiodawg

คำตอบ:


92

อ็อบเจ็กต์ Java String มีformatเมธอด (ณ 1.5) แต่ไม่มีjoinเมธอด

ที่จะได้รับพวงของวิธีการสาธารณูปโภค String ประโยชน์ไม่รวมอยู่แล้วคุณสามารถใช้org.apache.commons.lang.StringUtils


13
คอลเลกชันของ Google: google-collections.googlecode.com มี Joiner ด้วย
รอน

10
FYI ในความคิดเห็นของ Ron Google คอลเลคชันถูกเปลี่ยนชื่อเป็น Guava เมื่อไม่นานมานี้
Kevin Bourrillion

3
คุณควรอัปเดตคำตอบนี้เพื่อแสดงว่า Java 8 แนะนำString.join()วิธีการ
Duncan Jones

46

สตริงรูปแบบ สำหรับการเข้าร่วมคุณต้องเขียนของคุณเอง:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

ข้างต้นมาจากhttp://snippets.dzone.com/posts/show/91


6
อย่างแม่นยำยิ่งขึ้น: StringBuffer สำหรับ jdk1.4 และต่ำกว่า StringBuilder สำหรับ jdk1.5 และหลังจากนั้นเนื่องจากไม่ซิงโครไนซ์หลังจึงเร็วขึ้นเล็กน้อย
VonC

2
แทนที่จะเรียกใช้ iter.hasNext () สองรายการฉันมักจะต่อท้ายเส้นคั่นแล้วส่งกลับ "buf.substring (0, buf.length () - delimeter.length ())"
Vilmantas Baranauskas

2
คุณสามารถปรับปรุงมันได้เล็กน้อยโดยการออกก่อนเวลาเพื่อหลีกเลี่ยงตัวคั่น: while (true) (add_iter; if (! iter.hasNext ()) break; add_delim;}
13


29

สำหรับ Java 8 join()ตอนนี้มีให้ใช้งานเป็นสองคลาสเมธอดในคลาส String ในทั้งสองกรณีอาร์กิวเมนต์แรกคือตัวคั่น

คุณสามารถส่งผ่านแต่ละรายการCharSequenceเป็นอาร์กิวเมนต์เพิ่มเติม :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

หรือคุณสามารถส่งIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 ยังเพิ่มคลาสใหม่StringJoinerซึ่งคุณสามารถใช้ได้ดังนี้:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"


12

คุณยังสามารถใช้อาร์กิวเมนต์ตัวแปรสำหรับสตริงได้ดังนี้:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }

4

สำหรับการเข้าร่วมฉันเชื่อว่าสิ่งนี้อาจดูซับซ้อนน้อยลงเล็กน้อย:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

ฉันไม่ได้ใช้ไวยากรณ์ Java 5 มากเท่าที่ฉันต้องการ (เชื่อหรือไม่ว่าฉันใช้ 1.0.x เมื่อเร็ว ๆ นี้) ดังนั้นฉันอาจจะค่อนข้างเป็นสนิม แต่ฉันแน่ใจว่าแนวคิดนั้นถูกต้อง .

การแก้ไขเพิ่มเติม: การต่อท้ายสตริงอาจช้า แต่ถ้าคุณกำลังทำงานกับโค้ด GUI หรือรูทีนที่ทำงานสั้น ๆ ไม่สำคัญว่าคุณจะใช้เวลา. 005 วินาทีหรือ. 006 ดังนั้นหากคุณมีคอลเล็กชันชื่อ "joinMe" ที่คุณต้องการต่อท้ายสตริง "เป้าหมาย" ที่มีอยู่มันจะไม่น่ากลัวที่จะแทรกในบรรทัดนี้:

for(String s : joinMe)
    target += s;

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

ที่สำคัญจำง่ายสั้นเร็วและน่าอ่านมาก ประสิทธิภาพไม่ใช่สิ่งที่ชนะโดยอัตโนมัติในตัวเลือกการออกแบบเสมอไป


for-loop นั้นถูกต้อง แต่คุณควรทำให้เป็น Collection <String> หากไม่เป็นเช่นนั้นคุณจะต้องพูดว่า "for (Object o: c)" เพราะคุณไม่สามารถรับประกันได้ว่าทุกอย่างใน c เป็นสตริง
Michael Myers

จุดดีฉันยิ่งยุ่งกับ Generics ฉันจะแก้ไขให้ค่ะ
Bill K

เมื่อเชื่อมต่อแบบอินไลน์: string + string + string คอมไพเลอร์จะใช้ StringBuilder เพื่อต่อท้ายค่า ฉันสงสัยว่าในวิธีการ for-loop คุณมีที่สองหรือไม่คอมไพเลอร์จะทำเช่นเดียวกันหรือไม่
Spencer Kormos

@Spencer K: มันใช้ StringBuilder แต่มันสร้างอันใหม่สำหรับการวนซ้ำแต่ละครั้ง (แทบจะเป็นวิธีที่มีประสิทธิภาพมากที่สุด แต่คอมไพลเลอร์ควรจะรู้ได้อย่างไร?) ฉันไม่รู้ว่าคอมไพเลอร์ JIT อาจปรับให้เหมาะสมที่สุดในรันไทม์หรือไม่
Michael Myers

2
โอ้รอ คุณกำลังพูด + =? ใช่มันห่วย การวนซ้ำและต่อท้ายในคำตอบของฉันใช้ StringBuilder และนั่นคือสิ่งที่ฉันกำลังพูดถึง แม้ว่า + = จะได้รับการปรับปรุงให้ดีขึ้นมาก แต่คุณไม่ต้องการใช้มันในวง - ไม่มากจนเป็นบั๊ก แต่มันสามารถทำงานได้เหมือนอึ (Bug ไม่ใช่คำที่ใช้โดยทั่วไปสำหรับปัญหาด้านประสิทธิภาพ - อย่างน้อยก็ไม่ใช่ ใน 20 ปีของการเขียนโปรแกรม) นอกจากนี้ JIT ยังสามารถปรับปรุงสิ่งนี้ได้อย่างมหาศาล - แต่ฉันจะไม่พึ่งพาสิ่งนั้น
Bill K

4

นี่คือคำตอบที่ค่อนข้างง่าย ใช้+=เนื่องจากเป็นรหัสน้อยและให้เครื่องมือเพิ่มประสิทธิภาพแปลงเป็นรหัสStringBuilderให้คุณ เมื่อใช้วิธีนี้คุณไม่ต้องทำการตรวจสอบ "สุดท้าย" ใด ๆ ในลูปของคุณ (การปรับปรุงประสิทธิภาพ) และคุณไม่ต้องกังวลว่าจะตัดตัวคั่นใด ๆ ออกในตอนท้าย

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }

1
โซลูชันที่สวยงามมากไม่เกี่ยวข้องกับไลบรารีของบุคคลที่สาม!
Denis Itskovich

2

ฉันไม่ต้องการนำเข้าไลบรารี Apache ทั้งหมดเพื่อเพิ่มฟังก์ชันการเข้าร่วมแบบธรรมดานี่คือแฮ็คของฉัน

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }

@MrSnowflake ตัวสร้างสตริงมาตรฐานมีเมธอด "removeCharAt (int index)" มันไม่ปรากฏในเวอร์ชันที่ฉันกำลังทำงานอยู่
NSjonas

@NSjonas - ใช่เขาแก้ไขคำตอบของฉันผิด removeCharAtไม่อยู่และฟังก์ชั่นทั้งไม่มีผลตอบแทนที่ไม่เป็น String ... จะแก้ไขปัญหานี้
Martin Konecny

ในขณะที่คุณทำ ... โซลูชันปัจจุบันนี้จะทำให้ "ดัชนีออกนอกขอบเขตหากคุณผ่านในรายการที่ว่างเปล่า"
NSjonas

ทำการแก้ไขเพื่อตัดทอนความยาวที่เหมาะสมหากเส้นคั่นมีความยาวมากกว่า 1 อักขระ แก้ไขปัญหาที่คุณตัด
ทอน

ขอบคุณสำหรับข้อเสนอแนะ Updated
Martin Konecny

1

หากคุณต้องการรวม (ต่อ) หลาย ๆ สตริงเข้าด้วยกันคุณควรใช้ StringBuilder มันดีกว่าการใช้

for(String s : joinMe)
    target += s;

นอกจากนี้ยังมีประสิทธิภาพเล็กน้อยที่ชนะ StringBuffer เนื่องจาก StringBuilder ไม่ใช้การซิงโครไนซ์

สำหรับวิธีอรรถประโยชน์วัตถุประสงค์ทั่วไปเช่นนี้ (ในที่สุด) จะถูกเรียกหลายครั้งในหลาย ๆ สถานการณ์ดังนั้นคุณควรทำให้มีประสิทธิภาพและไม่จัดสรรวัตถุชั่วคราวจำนวนมาก เราได้จัดทำโปรไฟล์แอป Java ที่แตกต่างกันจำนวนมากและเกือบตลอดเวลาพบว่าการต่อสตริงและการจัดสรรสตริง / อักขระ [] ใช้เวลา / หน่วยความจำจำนวนมาก

คอลเลกชันที่ใช้ซ้ำได้ของเรา -> วิธีการสตริงจะคำนวณขนาดของผลลัพธ์ที่ต้องการก่อนจากนั้นจึงสร้าง StringBuilder ด้วยขนาดเริ่มต้นนั้น วิธีนี้จะหลีกเลี่ยงการเพิ่ม / คัดลอก char [] ภายในโดยไม่จำเป็นที่ใช้ในการต่อท้ายสตริง


เรื่องการคำนวณขนาดล่วงหน้า: เมื่อได้ผลดีมาก มันเป็นไปไม่ได้เสมอไป ฉันจำได้ว่าอ่านที่ไหนสักแห่งที่การเพิ่มขนาดบัฟเฟอร์เป็นสองเท่าในแต่ละครั้ง (หรือการคูณด้วยปัจจัยคงที่จริงๆ) ให้บางอย่างเช่น n log n performance ซึ่งก็ไม่ได้แย่ขนาดนั้น ในกรณีทั่วไปทางเลือกคือการสร้างรายการสตริงทั้งหมดที่คุณวางแผนจะเชื่อมต่อกัน หากคุณใช้ ArrayList คุณจะต้องคัดลอกอีกครั้ง (เว้นแต่คุณจะทราบความยาวของลำดับของคุณล่วงหน้า) และหากคุณใช้ LinkedList ระบบจะใช้หน่วยความจำและการรวบรวมขยะกับวัตถุโหนดมากขึ้น บางครั้งคุณไม่สามารถชนะได้ ลองดู!
Ian

ตัวอย่าง / คำถามข้างต้นถือว่าคอลเลกชันที่สั่งซื้อหรืออาร์เรย์ของสตริงที่จะเข้าร่วม นอกจากนี้ด้วยการคำนวณขนาดล่วงหน้าคุณจะหลีกเลี่ยงตัวอักษรที่ไม่ได้ใช้เพิ่มเติมซึ่งมักจะส่งผลให้การเติบโตของอาร์เรย์ char [] ภายใน
djb

1

ฉันเขียนเอง:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

แต่Collectionไม่ได้รับการสนับสนุนโดย JSP ดังนั้นสำหรับฟังก์ชันแท็กฉันเขียน:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

และใส่ลงใน.tldไฟล์:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

และใช้ในไฟล์ JSP เป็น:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}

0

StringUtilsเป็นคลาสที่มีประโยชน์มากในไลบรารี Apache Commons Lang


8
บางทีคุณอาจไม่สังเกตว่าคำตอบที่ยอมรับซึ่งโพสต์เมื่อ 8 เดือนที่แล้วมีข้อมูลเดียวกันนั้นอยู่แล้ว stackoverflow.com/questions/187676/string-operations-in-java/…
Jonik


0

ฉันเห็นการใช้งาน String ที่ซับซ้อนเกินไปเข้าร่วมที่นี่ หากคุณไม่มี Java 1.8 และคุณไม่ต้องการนำเข้าไลบรารีใหม่การใช้งานด้านล่างนี้ก็เพียงพอแล้ว

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}

-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

โดยพื้นฐานแล้ว String ntop จะเก็บค่าของคอลเลคชันทั้งหมดด้วยตัวคั่นและวงเล็บจุลภาค


1
ฉันไม่แน่ใจว่าส่วนไหนของคำถามนี้ตอบ? ไม่ใช่ String.format เว้นแต่คุณจะวางแผนที่จะเพิ่มบิตของสตริงทีละบิตให้กับอาร์เรย์และ [1,0.92,3] นั้นไม่หลากหลายเท่ากับสตริงทั่วไป .join
Omar Kooheji

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