Java: chuyển đổi Danh sách <Chuỗi> thành Chuỗi


595

JavaScript có Array.join()

js>["Bill","Bob","Steve"].join(" and ")
Bill and Bob and Steve

Java có gì như thế này không? Tôi biết tôi có thể tự khắc phục điều gì đó với StringBuilder:

static public String join(List<String> list, String conjunction)
{
   StringBuilder sb = new StringBuilder();
   boolean first = true;
   for (String item : list)
   {
      if (first)
         first = false;
      else
         sb.append(conjunction);
      sb.append(item);
   }
   return sb.toString();
}

... nhưng không có lý do gì để làm điều này nếu một cái gì đó giống như nó đã là một phần của JDK.


1
Xem thêm câu hỏi này để biết danh sách
Casebash

18
Không liên quan chặt chẽ nhưng android có chức năng tham gia tích hợp như một phần của lớp TextUtils của họ: developer.android.com/reference/android/text/iêu , java.lang.Iterable)
Xavi

16
Java 8 có một String.join()phương thức. Hãy xem câu trả lời này nếu bạn đang sử dụng Java 8 (hoặc mới hơn) stackoverflow.com/a/22577565/1115554
micha

Câu trả lời:


763

Với Java 8, bạn có thể làm điều này mà không cần bất kỳ thư viện bên thứ ba nào.

Nếu bạn muốn tham gia Bộ sưu tập Chuỗi, bạn có thể sử dụng phương thức String.join () mới :

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

Nếu bạn có Bộ sưu tập với loại khác ngoài Chuỗi, bạn có thể sử dụng API Stream với Trình thu thập tham gia :

List<Person> list = Arrays.asList(
  new Person("John", "Smith"),
  new Person("Anna", "Martinez"),
  new Person("Paul", "Watson ")
);

String joinedFirstNames = list.stream()
  .map(Person::getFirstName)
  .collect(Collectors.joining(", ")); // "John, Anna, Paul"

Các StringJoinerlớp cũng có thể hữu ích.


7
Thật không may, String.join chỉ chấp nhận CharSequences và không như người ta hy vọng đối tượng. Thậm chí không chắc chắn nếu nó không an toàn
Marc

@MarcassertThat(String.join(", ", Lists.newArrayList("1", null)), is("1, null"));
Sanghyun Lee

StringJoiner đặc biệt hữu ích nếu một người muốn tham gia vào một cái gì đó như [a, b, c]bao gồm cả niềng răng.
koppor 17/2/2016

@Marc String.join cũng chấp nhận Iterable<CharSequence>; Mối quan hệ giao diện:Iterable -> Collection -> List
aff

306

Tất cả các tham chiếu đến Apache Commons đều ổn (và đó là những gì hầu hết mọi người sử dụng) nhưng tôi nghĩ tương đương với Guava , Joiner , có API đẹp hơn nhiều.

Bạn có thể làm trường hợp tham gia đơn giản với

Joiner.on(" and ").join(names)

nhưng cũng dễ dàng đối phó với null

Joiner.on(" and ").skipNulls().join(names);

hoặc là

Joiner.on(" and ").useForNull("[unknown]").join(names);

và (đủ hữu ích theo như tôi quan tâm để sử dụng nó theo sở thích cho commons-lang), khả năng đối phó với Bản đồ:

Map<String, Integer> ages = .....;
String foo = Joiner.on(", ").withKeyValueSeparator(" is ").join(ages);
// Outputs:
// Bill is 25, Joe is 30, Betty is 35

đó là cực kỳ hữu ích để gỡ lỗi, vv


10
cảm ơn đã cắm Công cụ tham gia của chúng tôi cũng cung cấp cho bạn tùy chọn để nối trực tiếp vào Appendable (chẳng hạn như StringBuilder hoặc bất kỳ Nhà văn nào) mà không tạo Chuỗi trung gian mà thư viện Apache dường như thiếu.
Kevin Bourrillion

2
Điều này thật tuyệt, nhưng bạn có thể vui lòng thêm một .useForLastSeparator()hoặc một số phương pháp tương tự không? Bằng cách đó, bạn có thể nhận được một cái gì đó như "và" chỉ giữa hai mục cuối cùng (với "," cho phần còn lại).
Jeff Evans

2
Vâng, nó là chủ đề an toàn. Trạng thái duy nhất được lưu trong một công cụ tham gia là separator(đó là final). Mọi thứ tôi từng thấy ở Guava, nơi tôi từng sử dụng tương đương Apache Commons đã tốt hơn rất nhiều (đọc: nói chung sạch hơn, nhanh hơn, an toàn hơn và mạnh mẽ hơn) ở Guava với ít lỗi vỏ máy hơn và các vấn đề an toàn luồng và bộ nhớ nhỏ hơn. Hiệu trưởng hướng dẫn "cách tốt nhất có thể" của họ dường như vẫn đúng cho đến nay.
Shadow Man

Nếu bạn cần đi theo hướng ngược lại (nghĩa là tách Chuỗi thành từng mảnh), hãy kiểm tra Bộ tách lớp Guava - cũng được thiết kế / triển khai rất tốt.
Matt Passell

Trong Java 8, có một String.join()phương thức và một StringJoinerlớp.
theTechnoKid

138

Không nằm ngoài hộp, nhưng nhiều thư viện có tương tự:

Cộng đồng Lang:

org.apache.commons.lang.StringUtils.join(list, conjunction);

Mùa xuân:

org.springframework.util.StringUtils.collectionToDelimitedString(list, conjunction);

125

Trên Android, bạn có thể sử dụng lớp TextUtils .

TextUtils.join(" and ", names);

11
Tôi vẫn đang tìm kiếm điều này. String.joinyêu cầu tối thiểu api cấp 26 trên Android.
okarakose

49

Không, không có phương thức tiện lợi như vậy trong API Java tiêu chuẩn.

Không có gì đáng ngạc nhiên, Apache Commons cung cấp một thứ như vậy trong lớp StringUtils của họ trong trường hợp bạn không muốn tự viết nó.


11
Tôi luôn nói với tôi rằng String có sự phân chia nhưng không tham gia. Theo một cách nào đó, nó có ý nghĩa như vậy, nhưng thật khó chịu khi ít nhất không có một phương thức tĩnh cho nó trong Chuỗi.
Powerlord

3
Tôi với bạn Bemrose. Ít nhất họ đã cho chúng ta một isEmpty()phương thức thay vì phương thức (tĩnh) join()trong String...: rollseyes: :)
Bart

41

Ba khả năng trong Java 8:

List<String> list = Arrays.asList("Alice", "Bob", "Charlie")

String result = String.join(" and ", list);

result = list.stream().collect(Collectors.joining(" and "));

result = list.stream().reduce((t, u) -> t + " and " + u).orElse("");

27

Với trình thu thập java 8, điều này có thể được thực hiện với đoạn mã sau:

Arrays.asList("Bill", "Bob", "Steve").stream()
.collect(Collectors.joining(" and "));

Ngoài ra, giải pháp đơn giản nhất trong java 8:

String.join(" and ", "Bill", "Bob", "Steve");

hoặc là

String.join(" and ", Arrays.asList("Bill", "Bob", "Steve"));

21

Tôi đã viết cái này (tôi sử dụng nó cho đậu và khai thác toString, vì vậy đừng viết Collection<String>):

public static String join(Collection<?> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<?> 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 TLD 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 .tldhồ sơ:

<?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 tin như:

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

1
đề nghị thay đổi Collection<>thành Iterable<>?
Jason S

@JasonS +1. Điểm hay, nhưng đọc stackoverflow.com/questions/1159797/ Cách
gavenkoa

19

Mã bạn có là cách đúng để làm điều đó nếu bạn muốn sử dụng JDK mà không cần bất kỳ thư viện bên ngoài nào. Không có "một lớp lót" đơn giản nào mà bạn có thể sử dụng trong JDK.

Nếu bạn có thể sử dụng các lib bên ngoài, tôi khuyên bạn nên tìm hiểu về lớp org.apache.commons.lang.StringUtils trong thư viện Apache Commons.

Một ví dụ về cách sử dụng:

List<String> list = Arrays.asList("Bill", "Bob", "Steve");
String joinedResult = StringUtils.join(list, " and ");

17

Một cách chính thống để đạt được nó, là bằng cách xác định một chức năng mới:

public static String join(String joinStr, String... strings) {
    if (strings == null || strings.length == 0) {
        return "";
    } else if (strings.length == 1) {
        return strings[0];
    } else {
        StringBuilder sb = new StringBuilder(strings.length * 1 + strings[0].length());
        sb.append(strings[0]);
        for (int i = 1; i < strings.length; i++) {
            sb.append(joinStr).append(strings[i]);
        }
        return sb.toString();
    }
}

Mẫu vật:

String[] array = new String[] { "7, 7, 7", "Bill", "Bob", "Steve",
        "[Bill]", "1,2,3", "Apple ][","~,~" };

String joined;
joined = join(" and ","7, 7, 7", "Bill", "Bob", "Steve", "[Bill]", "1,2,3", "Apple ][","~,~");
joined = join(" and ", array); // same result

System.out.println(joined);

Đầu ra:

7, 7, 7 và Bill và Bob và Steve và [Bill] và 1,2,3 và Apple] [và ~, ~


5
đưa ra một tham số phương thức cùng tên với chính phương thức đó có lẽ không phải là ý tưởng tốt nhất. separatorsẽ rất nhiều gợi ý.
ccpizza

10

Giải pháp Java 8 với java.util.StringJoiner

Java 8 đã có một StringJoinerlớp. Nhưng bạn vẫn cần phải viết một chút bản tóm tắt, vì đó là Java.

StringJoiner sj = new StringJoiner(" and ", "" , "");
String[] names = {"Bill", "Bob", "Steve"};
for (String name : names) {
   sj.add(name);
}
System.out.println(sj);

Bạn không cần phải viết một chút bản tóm tắt nếu bạn sử dụng phương thức thuận tiện hơn String.join () .
Andy Thomas

9

Bạn có thể sử dụng thư viện apache commons có lớp StringUtils và phương thức nối.

Kiểm tra liên kết này: https://commons.apache.org/proper/commons-lang/javadocs/api.2.0/org/apache/commons/lang/StringUtils.html

Lưu ý rằng liên kết ở trên có thể trở nên lỗi thời theo thời gian, trong trường hợp đó bạn chỉ có thể tìm kiếm trên web cho "apache commons StringUtils", cho phép bạn tìm tài liệu tham khảo mới nhất.

(được tham chiếu từ luồng này) Tương đương Java của C # String.Format () và String.Join ()


7

Bạn có thể làm được việc này:

String aToString = java.util.Arrays.toString(anArray);
// Do not need to do this if you are OK with '[' and ']'
aToString = aToString.substring(1, aToString.length() - 1);

Hoặc một lớp lót (chỉ khi bạn không muốn '[' và ']')

String aToString = java.util.Arrays.toString(anArray).substring(1).replaceAll("\\]$", "");

Hi vọng điêu nay co ich.


1
Điều này sẽ không nối sự lựa chọn.
hexium

GIÁO SƯ!! Tôi xin lỗi, tôi đã không phát hiện ra điều đó.
NawaMan

6

Một cách thú vị để làm điều đó với JDK thuần túy, trong một dòng nhiệm vụ:

String[] array = new String[] { "Bill", "Bob", "Steve","[Bill]","1,2,3","Apple ][" };
String join = " and ";

String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "");

System.out.println(joined);

Đầu ra:

Bill và Bob và Steve và [Bill] và 1,2,3 và Apple] [


Một cách không quá hoàn hảo và không quá vui!

String[] array = new String[] { "7, 7, 7","Bill", "Bob", "Steve", "[Bill]",
        "1,2,3", "Apple ][" };
String join = " and ";

for (int i = 0; i < array.length; i++) array[i] = array[i].replaceAll(", ", "~,~");
String joined = Arrays.toString(array).replaceAll(", ", join)
        .replaceAll("(^\\[)|(\\]$)", "").replaceAll("~,~", ", ");

System.out.println(joined);

Đầu ra:

7, 7, 7 và Bill và Bob và Steve và [Bill] và 1,2,3 và Apple] [


Hãy thử với "[Bill]", "1,2,3" và "Apple] [". Sáng tạo, nhưng nó có trường hợp whwen nó không chính xác.
Jason S

1
Bây giờ hãy thử nó với "~, ~". Bạn không thể tạo một chương trình với loại kiến ​​trúc này hoàn toàn chống đạn.
Jason S

Vâng, tôi biết ... Tôi đã gỡ bỏ downvote. Nhưng bạn nên suy nghĩ kỹ hơn một chút về việc đăng câu trả lời ở đây, đặc biệt là những câu hỏi như câu hỏi này đã được vài năm tuổi. Các câu trả lời không chỉ hữu ích ngay lập tức cho người đăng ban đầu mà còn hiển thị trên các tìm kiếm của Google. (Trên thực tế, đối với các câu hỏi đã được vài năm tuổi, câu trả lời mới có thể không có giá trị đối với người đăng ban đầu.) Các giải pháp có nhược điểm hoặc lỗi có thể gây hại cho những người tìm thấy nó sau bối cảnh.
Jason S

1
Tôi học được rất nhiều ở đây từ những người khác như tôi, với giải pháp không hoàn hảo, nhưng thực sự giàu kiến ​​thức và suy nghĩ phụ. Thư giãn cho người khác, mỗi người phải hiểu mỗi dòng mã của họ làm gì. Hãy nhớ lập trình giống như một nghệ thuật, và thậm chí mỗi dòng mã, có nghĩa là một cái gì đó về tính cách lập trình viên. Và vâng, tôi sẽ không sử dụng mã này trong sản xuất!
Daniel De León


4

Nếu bạn đang sử dụng Bộ sưu tập Eclipse (trước đây là Bộ sưu tập GS ), bạn có thể sử dụng makeString()phương thức này.

List<String> list = Arrays.asList("Bill", "Bob", "Steve");

String string = ListAdapter.adapt(list).makeString(" and ");

Assert.assertEquals("Bill and Bob and Steve", string);

Nếu bạn có thể chuyển đổi loại của bạn Listsang loại Bộ sưu tập Eclipse, thì bạn có thể thoát khỏi bộ điều hợp.

MutableList<String> list = Lists.mutable.with("Bill", "Bob", "Steve");
String string = list.makeString(" and ");

Nếu bạn chỉ muốn một chuỗi được phân tách bằng dấu phẩy, bạn có thể sử dụng phiên bản makeString()không có tham số.

Assert.assertEquals(
    "Bill, Bob, Steve", 
    Lists.mutable.with("Bill", "Bob", "Steve").makeString());

Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse.


3

Với luồng java 1.8 có thể được sử dụng,

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
List<String> list = Arrays.asList("Bill","Bob","Steve").
String str = list.stream().collect(Collectors.joining(" and "));

3

BIÊN TẬP

Tôi cũng nhận thấy toString() vấn đề triển khai cơ bản và về yếu tố chứa dấu phân cách nhưng tôi nghĩ rằng tôi đang bị hoang tưởng.

Vì tôi có hai nhận xét về vấn đề đó, tôi đang thay đổi câu trả lời của mình thành:

static String join( List<String> list , String replacement  ) {
    StringBuilder b = new StringBuilder();
    for( String item: list ) { 
        b.append( replacement ).append( item );
    }
    return b.toString().substring( replacement.length() );
}

Mà trông khá giống với câu hỏi ban đầu.

Vì vậy, nếu bạn không muốn thêm toàn bộ bình vào dự án của mình, bạn có thể sử dụng cái này.

Tôi nghĩ không có gì sai với mã gốc của bạn. Trên thực tế, sự thay thế mà mọi người đang đề xuất trông gần giống nhau (mặc dù nó có một số xác nhận bổ sung)

Đây là, cùng với giấy phép Apache 2.0.

public static String join(Iterator iterator, String separator) {
    // handle null, zero and one elements before building a buffer
    if (iterator == null) {
        return null;
    }
    if (!iterator.hasNext()) {
        return EMPTY;
    }
    Object first = iterator.next();
    if (!iterator.hasNext()) {
        return ObjectUtils.toString(first);
    }

    // two or more elements
    StringBuffer buf = new StringBuffer(256); // Java default is 16, probably too small
    if (first != null) {
        buf.append(first);
    }

    while (iterator.hasNext()) {
        if (separator != null) {
            buf.append(separator);
        }
        Object obj = iterator.next();
        if (obj != null) {
            buf.append(obj);
        }
    }
    return buf.toString();
}

Bây giờ chúng tôi biết, cảm ơn bạn nguồn mở


Điều này sẽ hoạt động ngay bây giờ, nhưng bạn không thể chắc chắn List.toString () sẽ hoạt động như thế nào trong tương lai. Tôi sẽ không bao giờ tin tưởng một hoạt động toString () ngoài việc in một số chuỗi thông tin về đối tượng được đề cập. Để bảo vệ bạn, bạn không phải là người duy nhất đề xuất giải pháp này.
Hans Doggen

Nếu nội dung của một phần tử trong danh sách chứa chuỗi con ", "thì sao?
Bart Kiers

@Hans & @Bart: Bạn nói đúng. Tôi nghĩ rằng không ai khác sẽ nhận thấy: P Tôi đang thay đổi câu trả lời của tôi.
OscarRyz

Nó phụ thuộc vào trường hợp sử dụng. Đối với mục đích gỡ lỗi, ToString () chỉ là IMO tốt ... miễn là bạn không dựa vào định dạng cụ thể. Và ngay cả như vậy, nếu bạn viết một bài kiểm tra cho mã của mình, mọi thay đổi trong tương lai sẽ bị bắt.
akostadinov

2

API Guava của Google cũng có .join (), mặc dù (như hiển nhiên với các câu trả lời khác), Apache Commons có khá nhiều tiêu chuẩn ở đây.


1

Java 8 không mang lại

Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)

phương pháp đó là nullsafe bằng cách sử dụng prefix + suffix cho các giá trị null.

Nó có thể được sử dụng theo cách sau:

String s = stringList.stream().collect(Collectors.joining(" and ", "prefix_", "_suffix"))

Các Collectors.joining(CharSequence delimiter)phương pháp chỉ gọi joining(delimiter, "", "")nội bộ.


0

Bạn có thể sử dụng điều này từ StringUtils của Spring Framework. Tôi biết nó đã được đề cập, nhưng bạn thực sự có thể lấy mã này và nó hoạt động ngay lập tức mà không cần Spring cho nó.

// from https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/StringUtils.java

/*
 * Copyright 2002-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class StringUtils {
    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {
        if(coll == null || coll.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        Iterator<?> it = coll.iterator();
        while (it.hasNext()) {
            sb.append(prefix).append(it.next()).append(suffix);
            if (it.hasNext()) {
                sb.append(delim);
            }
        }
        return sb.toString();
    }
}

-3

Thử cái này:

java.util.Arrays.toString(anArray).replaceAll(", ", ",")
                .replaceFirst("^\\[","").replaceFirst("\\]$","");

1
Có lẽ bởi vì câu hỏi yêu cầu sử dụng Danh sách so với Mảng? nhún vai
Nick Coelius

4
Bởi vì nếu bạn có "," trong chuỗi của mình thì nó sẽ không hoạt động.
Cyrille Pontvieux
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.