Sử dụng Java 8 để chuyển đổi danh sách các đối tượng thành một chuỗi thu được từ phương thức toString ()


206

Có rất nhiều điều mới hữu ích trong Java 8. Ví dụ: tôi có thể lặp lại với một luồng qua danh sách các đối tượng và sau đó tổng hợp các giá trị từ một trường cụ thể của các Objectthể hiện. Ví dụ

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

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

Vì vậy, tôi đang hỏi liệu có cách nào để xây dựng một Stringkết nối đầu ra của toString()phương thức từ các thể hiện trong một dòng không.

List<Integer> list = ...

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

Giả sử listcó chứa số nguyên 1, 23, tôi hy vọng đó concatenated"123"hoặc "1,2,3".


Câu trả lời:


368

Một cách đơn giản là nối các mục danh sách của bạn vào 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);

bạn cũng có thể thử:

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

Giải thích: map chuyển đổi luồng Integer thành luồng String, sau đó nó được giảm xuống dưới dạng nối của tất cả các phần tử.

Lưu ý: Đây là hiệu suất normal reductiontrong O (n 2 )

để có hiệu suất tốt hơn, hãy sử dụng một StringBuilderhoặc mutable reductiontương tự như câu trả lời của F. Böller.

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

Tham chiếu: Giảm luồng


2
Vâng, không phải là một giải pháp nội tuyến, mà là một giải pháp.
mat_boy

đã không nhận được điều đó. Bạn đang mong đợi điều gì?
Shail016

2
tôi không hiểu tại sao "giảm bình thường thực hiện trong O (n2)". với tôi nó "trông" giống O (n) ...
datahaki

2
@datahaki Tôi không phải là một người Java, nhưng tôi đoán rằng việc nối (giảm) chuỗi lặp lại yêu cầu phân bổ (tái) mảng ở mỗi lần lặp, điều đó làm cho O (n ^ 2). joinở phía bên kia sẽ phân bổ một mảng duy nhất đủ lớn để lưu trữ chuỗi cuối cùng trước khi điền vào nó, làm cho nó thành O (n).
Eli Korvigo

2
Mẹo rất tốt. Có lẽ bạn có thể đơn giản hóa bằng cách sử dụng ký hiệu "::". Vì vậy, list.stream().map(Object::toString).reduce("", String::concat). Sử dụng map e-> e.toString()là một chút dư thừa.
e2a

194

Có một trình thu thập joiningtrong API. Đó là một phương thức tĩnh trong Collectors.

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

Không hoàn hảo vì các cuộc gọi cần thiết của toString, nhưng hoạt động. Phân định khác nhau là có thể.


7
Để hoàn thiện: để mã chính xác hoạt động, bạn cần phảiimport static java.util.stream.Collectors.joining;
Mahdi

7
Tại sao chúng ta cần ánh xạ và 'toString' rõ ràng? Nếu trình thu thập đang mong đợi các phần tử Chuỗi thì chuyển đổi sẽ được gọi hoàn toàn, không?!
Basel Shishani

10

Chỉ trong trường hợp bất cứ ai đang cố gắng làm điều này mà không có java 8, có một mẹo khá hay. List.toString () đã trả về một bộ sưu tập trông như thế này:

[1,2,3]

Tùy thuộc vào yêu cầu cụ thể của bạn, điều này có thể được xử lý hậu kỳ theo bất cứ điều gì bạn muốn miễn là các mục trong danh sách của bạn không chứa [] hoặc ,.

Ví dụ:

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

hoặc nếu dữ liệu của bạn có thể chứa dấu ngoặc vuông này:

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

sẽ giúp bạn có một đầu ra khá hợp lý.

Một mục mảng trên mỗi dòng có thể được tạo như thế này:

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

Tôi đã sử dụng kỹ thuật này để tạo các chú giải công cụ html từ một danh sách trong một ứng dụng nhỏ, với nội dung như:

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

Nếu bạn có một mảng thì hãy bắt đầu với Arrays.asList (list) .toString ()

Tôi hoàn toàn sở hữu một thực tế rằng điều này không tối ưu, nhưng nó không hiệu quả như bạn nghĩ và khá đơn giản để đọc và hiểu. Tuy nhiên, điều này khá không linh hoạt - đặc biệt là đừng cố tách các phần tử bằng thay thế nếu dữ liệu của bạn có thể chứa dấu phẩy và sử dụng phiên bản chuỗi con nếu bạn có dấu ngoặc vuông trong dữ liệu của mình, nhưng đối với một mảng số thì khá nhiều hoàn hảo.


Mặc dù điều này thường sẽ hoạt động, nhưng nó không được đảm bảo - Listkhông thực thi cấu trúc của toString(). AbstractCollectiontuy nhiên, mặc định sử dụng cấu trúc này và tôi nghĩ rằng tất cả các Listtriển khai mục đích chung trong Java SE cũng làm như vậy. Như một ví dụ về điều đó không xảy ra, org.springframework.util.AutoPopulatingListtrong Spring không thực hiện toString()và do đó sẽ trả về ví dụ " org.springframework.util.AutoPopulatingList@170a1".
M. Justin

Arrays.toString()sẽ là một lựa chọn tốt hơn Arrays.asList(list).toString(), vì nó được xác định để trả về một chuỗi tương đương, nó ngắn gọn hơn và nó không yêu cầu tạo đối tượng bổ sung.
M. Justin

@ M.Justin Không phải là một điểm xấu cho phần mảng của câu trả lời này, nhưng còn một "Danh sách" đơn giản thì sao? Bạn thực sự không thể sử dụng Arrays.toString () cho điều đó. Làm thế nào bạn có thể đề xuất sử dụng kỹ thuật này trên một cái gì đó như AutoPopulationList mà bạn đã đề cập? Có thể: ArrayList mới (autoPopulationList) .toString ()? Hoặc tôi đoán bạn có thể chuyển đổi nó thành một mảng. Có lẽ nếu bạn gặp phải một vấn đề như vậy, bạn nên hy vọng bạn ở mức 1.8 trở lên và có thể sử dụng một trong những câu trả lời khác trong chuỗi này ...
Bill K

5

Các câu trả lời khác là tốt. Tuy nhiên, bạn cũng có thể truyền Collector.toList () làm tham số cho Stream.collect () để trả về các phần tử dưới dạng ArrayList.

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

Nếu bạn sử dụng System.out.print(ln), (list)nó sẽ sử dụng toString()phương thức của các phần tử để in danh sách. Vì vậy, đoạn mã này chỉ lặp lại những gì xảy ra bên trong toStringDanh sách.
Shirkam

1
Nên là Collector.toList () trừ khi bạn nhập tĩnh.
mwieczorek

Đúng ... Tôi đã nhập tĩnh cho ví dụ. Tôi đã nói, "Collector.toList ()" trong phần thân của câu trả lời ...;)
Eddie B

2

StringListName = ObjectListName.stream (). Map (m -> m.toString ()) .collect (Collector.toList ());


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

Hoặc là

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

Cách tiếp cận này cho phép bạn cũng xây dựng kết quả chuỗi từ danh sách các đối tượng Ví dụ

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

Ở đây, hàm giảm cho phép bạn có một số giá trị ban đầu mà bạn muốn nối thêm chuỗi mới Ví dụ:

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

1

Thử nghiệm cả hai cách tiếp cận đề xuất trong Shail016bpedroso câu trả lời ( https://stackoverflow.com/a/24883180/2832140 ), đơn giản StringBuilder+ append(String)trong một forvòng lặp, dường như để thực hiện nhanh hơn nhiều list.stream().map([...].

Ví dụ: Mã này đi qua một Map<Long, List<Long>>chuỗi xây dựng chuỗi json, sử dụng 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());
}

Trên máy ảo dev của tôi, Junit thường mất từ ​​0,35 đến 1,2 giây để thực hiện kiểm tra. Trong khi, sử dụng mã sau đây, phải mất từ ​​0,15 đến 0,33 giây:

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

1

Có một phương pháp trong API chuỗi cho các usecase "danh sách tham gia chuỗi" đó, thậm chí bạn không cần Stream.

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

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

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

0

Chúng ta có thể thử điều này

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
Thông thường nên thêm một lời giải thích cho bài đăng của bạn nói về cách thức hoạt động của mã. Điều này cho phép các nhà phát triển mới hiểu những gì mã làm.
Caleb Kleveter

1
@CalebKleveter nó giảm một Listthành một Stringvà tách từng phần tử bằng dấu phẩy ( ,).
Joe Almore

2
Giải pháp của bạn CHỈ hoạt động nếu danh sách là Danh sách <Chuỗi>. OP đã không chỉ định lớp như vậy. Trong câu hỏi của anh ấy, ngay cả một Danh sách <Integer> cũng được chỉ ra. Mã của bạn không biên dịch trong trường hợp đó.
RubioRic

0

Tôi sẽ sử dụng các luồng api để chuyển đổi một luồng số nguyên thành một chuỗi. Vấn đề với một số câu trả lời được cung cấp là chúng tạo ra thời gian chạy O (n ^ 2) do xây dựng Chuỗi. Một giải pháp tốt hơn là sử dụng StringBuilder, sau đó nối các chuỗi lại với nhau như bước cuối cùng.

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

Bạn có thể thực hiện quá trình này một bước nữa bằng cách chuyển đổi bộ sưu tập đối tượng thành một chuỗi.

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

Với Java 8+

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

Không phải là hiệu quả nhất, nhưng nó là một giải pháp với một lượng nhỏ mã.


-2

Ngoài ra, bạn có thể làm như thế này.

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

Giải pháp của bạn CHỈ hoạt động nếu danh sách là Danh sách <Chuỗi>. OP đã không chỉ định lớp như vậy. Trong câu hỏi của anh ấy, ngay cả một Danh sách <Integer> cũng được chỉ ra.
RubioRic
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.