Tương đương với Java của C # String.Format () và String.Join ()


111

Tôi biết đây là một câu hỏi hơi thắc mắc của người mới, nhưng có tương đương với các hoạt động chuỗi của C # trong Java không?

Cụ thể, tôi đang nói về String.FormatString.Join.


Vì vậy, có một String.format nhưng tôi sẽ phải Roll tham gia của riêng mình.
Omar Kooheji 09/10/08

1
Đối với join (), tôi giống như câu trả lời này: stackoverflow.com/a/6116469/562139
scorpiodawg

Câu trả lời:


92

Đối tượng Chuỗi Java có một formatphương thức (kể từ 1.5), nhưng không có joinphương thức nào .

Để có được một loạt các phương thức tiện ích chuỗi hữu ích chưa được bao gồm, bạn có thể sử dụng org.apache.commons.lang.StringUtils .


13
bộ sưu tập của google: google-collections.googlecode.com cũng có Trình kết hợp.
Ron

10
FYI trên bình luận của Ron, google-collection đã được đổi tên thành Guava một thời gian trước.
Kevin Bourrillion

3
Bạn nên cập nhật câu trả lời này để phản ánh rằng Java 8 giới thiệu một String.join()phương pháp.
Duncan Jones

46

String.format . Đối với tham gia, bạn cần viết của riêng bạn:

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

Ở trên đến từ http://snippets.dzone.com/posts/show/91


6
Chính xác hơn: StringBuffer cho jdk1.4 trở xuống, StringBuilder cho jdk1.5 trở lên, vì sau này không được đồng bộ hóa, do đó nhanh hơn một chút.
VonC

2
Trước hai lệnh gọi iter.hasNext (), tôi thường nối thêm lệnh mê sảng và sau đó "trả về buf.substring (0, buf.length () - Delimeter.length ())".
Vilmantas Baranauskas 9/10/08

2
Bạn có thể hợp lý hóa nó một chút bằng cách thoát sớm để tránh dấu phân cách: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren


29

Kể từ Java 8, join() hiện có sẵn dưới dạng hai phương thức lớp trên lớp String. Trong cả hai trường hợp, đối số đầu tiên là dấu phân cách.

Bạn có thể chuyển từng CharSequences làm đối số bổ sung :

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

Hoặc bạn có thể vượt quaIterable<? 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 cũng thêm một lớp mới StringJoiner, bạn có thể sử dụng lớp này như sau:

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

Bạn cũng có thể sử dụng các đối số biến cho các chuỗi như sau:

  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

Đối với tham gia, tôi tin rằng điều này có thể trông ít phức tạp hơn một chút:

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

Tôi không thể sử dụng cú pháp Java 5 nhiều như tôi muốn (Tin hay không, tôi đang sử dụng 1.0.x gần đây) vì vậy tôi có thể hơi thiếu hiểu biết, nhưng tôi chắc chắn khái niệm này là đúng .

chỉnh sửa bổ sung: Phần bổ sung chuỗi có thể hơi chậm, nhưng nếu bạn đang làm việc trên mã GUI hoặc một số quy trình chạy ngắn, sẽ thực sự không thành vấn đề nếu bạn mất .005 giây hoặc .006, vì vậy nếu bạn có một bộ sưu tập có tên "joinMe" mà bạn muốn nối thêm vào một chuỗi hiện có "target" thì sẽ không kinh khủng nếu chỉ nội dòng thế này:

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

Nó khá kém hiệu quả (và là một thói quen xấu), nhưng không phải bất cứ thứ gì bạn có thể cảm nhận được trừ khi có hàng nghìn chuỗi hoặc điều này nằm trong một vòng lặp lớn hoặc mã của bạn thực sự quan trọng về hiệu suất.

Quan trọng hơn, nó dễ nhớ, ngắn gọn, nhanh chóng và rất dễ đọc. Hiệu suất không phải lúc nào cũng tự động chiến thắng trong các lựa chọn thiết kế.


Vòng lặp for là đúng, nhưng bạn cũng nên đặt nó thành Bộ sưu tập <Chuỗi>. Nếu không, bạn phải nói "for (Object o: c)", bởi vì bạn không thể đảm bảo rằng mọi thứ trong c là một Chuỗi.
Michael Myers

Điểm tốt, tôi thậm chí còn say mê với Generics. Tôi sẽ chỉnh sửa nó trong.
Bill K

Khi nối nội tuyến: string + string + string, trình biên dịch thực sự sử dụng StringBuilder để nối các giá trị. Tôi tự hỏi nếu trong phương thức vòng lặp for bạn có thứ hai ở đó, liệu trình biên dịch có làm như vậy không.
Spencer Kormos 9/10/08

@Spencer K: Nó sử dụng một StringBuilder, nhưng nó tạo ra một cái mới cho mỗi lần lặp (hầu như không phải là phương pháp hiệu quả nhất, nhưng sau đó làm thế nào để trình biên dịch biết được?). Tôi không biết liệu trình biên dịch JIT có thể tối ưu hóa điều đó trong thời gian chạy hay không.
Michael Myers

2
Chờ đã. Bạn đang nói + =? Phải, thật tệ. Vòng lặp và phần nối hiện trong câu trả lời của tôi sử dụng StringBuilder và đó là những gì tôi đang nói đến. Mặc dù + = đã được cải thiện nhiều nhưng bạn thực sự không muốn sử dụng nó trong một vòng lặp - không quá nhiều lỗi nhưng ở chỗ nó có thể hoạt động như tào lao (Lỗi không phải là một thuật ngữ thường được sử dụng cho các vấn đề về hiệu suất - ít nhất là không trong 20 năm lập trình của tôi). Ngoài ra, JIT có thể cải thiện điều này rất nhiều - nhưng tôi sẽ không dựa vào điều đó.
Bill K

4

Đây là một câu trả lời khá đơn giản. Sử dụng +=vì nó là mã ít hơn và để trình tối ưu hóa chuyển đổi nó thành a StringBuildercho bạn. Sử dụng phương pháp này, bạn không phải thực hiện bất kỳ kiểm tra "là cuối cùng" nào trong vòng lặp của mình (cải thiện hiệu suất) và bạn không phải lo lắng về việc loại bỏ bất kỳ dấu phân cách nào ở cuối.

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

1
Giải pháp rất thanh lịch, không liên quan đến thư viện của bên thứ 3!
Denis Itskovich

2

Tôi không muốn nhập toàn bộ thư viện Apache để thêm một chức năng tham gia đơn giản, vì vậy đây là bản hack của tôi.

    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 Liệu chuỗi tiêu chuẩn xây dựng có một phương pháp "removeCharAt (int index)" của nó không hiển thị trên phiên bản Im chạy
NSjonas

@NSjonas - vâng, chỉnh sửa của anh ấy đối với câu trả lời của tôi là sai. Các removeCharAtkhông tồn tại, và toàn bộ chức năng không còn trả về một String ... sẽ sửa lỗi này.
Martin Konecny,

trong khi tại nó ... giải pháp hiện tại này sẽ ném một "chỉ số nằm ngoài giới hạn ngoại lệ nếu bạn vượt qua trong một danh sách rỗng"
NSjonas

đã thực hiện chỉnh sửa để cắt bớt độ dài phù hợp nếu dấu phân cách có nhiều hơn 1 ký tự. Cũng cố định một vấn đề mà bạn đã được cắt tỉa ký tự cuối cùng và không phải là lần đầu tiên khi bạn thực sự cần thiết để
NSjonas

Thx cho các phản hồi. Đã cập nhật.
Martin Konecny

1

Nếu bạn muốn nối (nối) nhiều chuỗi thành một, bạn nên sử dụng StringBuilder. Nó tốt hơn nhiều so với việc sử dụng

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

Cũng có một chút hiệu suất thắng StringBuffer, vì StringBuilder không sử dụng đồng bộ hóa.

Đối với một phương thức tiện ích có mục đích chung như thế này, nó sẽ (cuối cùng) được gọi nhiều lần trong nhiều tình huống, vì vậy bạn nên làm cho nó hiệu quả và không phân bổ nhiều đối tượng tạm thời. Chúng tôi đã lập hồ sơ rất nhiều ứng dụng Java khác nhau và hầu như luôn nhận thấy rằng việc nối chuỗi và phân bổ chuỗi / char [] chiếm một lượng thời gian / bộ nhớ đáng kể.

Phương thức collection -> string có thể tái sử dụng của chúng tôi trước tiên sẽ tính toán kích thước của kết quả được yêu cầu và sau đó tạo một StringBuilder với kích thước ban đầu đó; điều này tránh nhân đôi / sao chép không cần thiết của char nội bộ [] được sử dụng khi nối các chuỗi.


re: tính toán trước kích thước: Khi điều đó hoạt động, thật tuyệt. Nó không phải lúc nào cũng có thể. Tôi nhớ đã đọc ở đâu đó rằng việc tăng gấp đôi kích thước bộ đệm mỗi lần (hoặc thực sự nhân với bất kỳ hệ số cố định nào) mang lại hiệu suất như n log n, điều này không thực sự tệ như vậy. Trong trường hợp chung, giải pháp thay thế là tạo danh sách tất cả các chuỗi mà bạn định nối. Nếu bạn sử dụng ArrayList, bạn sẽ phải sao chép lại (trừ khi bạn biết trước độ dài của chuỗi của mình) và nếu bạn sử dụng LinkedList, thì nó sẽ sử dụng nhiều ram hơn và thu thập rác với các đối tượng nút. Đôi khi bạn không thể chiến thắng. Hãy cố gắng!
Ian

Các ví dụ / câu hỏi ở trên giả định một số tập hợp có thứ tự hoặc mảng các Chuỗi tham gia. Ngoài ra, bằng precomputing kích thước, bạn tránh được thêm ký tự không sử dụng mà char nội bộ [] mảng tăng trưởng thường dẫn đến.
DJB

1

Tôi đã viết riêng:

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

nhưng Collectionkhông được hỗ trợ bởi JSP, vì vậy đối với chức năng thẻ, tôi đã viết:

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

và đưa vào .tldtệp:

<?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>

và sử dụng nó trong các tệp JSP như:

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



0

Tôi thấy rất nhiều triển khai quá phức tạp của String.Join ở đây. Nếu bạn không có Java 1.8 và bạn không muốn nhập thư viện mới, thì việc triển khai bên dưới là đủ.

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]" 

Vì vậy, về cơ bản, chuỗi ntop lưu trữ giá trị của toàn bộ tập hợp bằng dấu phẩy và dấu ngoặc.


1
Tôi không chắc phần nào của câu hỏi này trả lời? Nó không phải là String.format trừ khi bạn có kế hoạch thêm các bit của chuỗi từng bit vào mảng và [1,0,92,3] không linh hoạt như một chuỗi chung .join.
Omar Kooheji

-8

Tôi sẽ chỉ sử dụng toán tử nối chuỗi "+" để nối hai chuỗi. s1 += s2;


Vì nó thực hành không tốt và chậm.
MrSnowflake
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.