Використання 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".


Відповіді:


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

Пояснення: map перетворює Integer stream в String stream, потім зменшується як конкатенація всіх елементів.

Примітка. Це normal reductionвиконується в O (n 2 )

для кращої продуктивності використовуйте відповідь Ф. Бьоллера StringBuilderабо mutable reductionподібний до нього.

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

Посилання: Зменшення потоку


2
Так, не вбудоване рішення, а рішення.
mat_boy

не зрозумів цього. Що ви очікуєте?
Shail016

2
я не розумію, чому "нормальне зменшення, яке працює в O (n2)". мені це «схоже» більше на О (п) ...
datahaki

2
@datahaki Я не хлопець на Java, але я думаю, що ітераційне об'єднання (зменшення) рядків вимагає виділення масиву (пере) при кожній ітерації, що робить його O (n ^ 2). joinз іншого боку, слід виділити один масив, достатньо великий, щоб зберігати остаточний рядок перед його заповненням, роблячи його O (n).
Елі Корвіго

2
Дуже хороша порада. Можливо, ви можете спростити, використовуючи позначення "::". Так, list.stream().map(Object::toString).reduce("", String::concat). Використання map e-> e.toString()трохи зайве.
e2a

194

В joiningAPI є колектор . Це статичний метод в Collectors.

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

Не ідеально через необхідний дзвінок toString, але працює. Можливі різні роздільники.


7
Для повноти: щоб отримати точний код для роботи, вам потрібноimport static java.util.stream.Collectors.joining;
Махді,

7
Навіщо нам потрібне відображення та явне `toString '? Якщо колектор очікує елементів String, тоді перетворення слід викликати неявно, ні ?!
Базель Шишані

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

Я повністю володію тим, що це не оптимально, але це не так неефективно, як ви могли подумати, і досить просто читати і розуміти. Це, однак, досить негнучко - зокрема, не намагайтеся відокремлювати елементи заміноюAll, якщо ваші дані можуть містити коми і використовуйте версію підрядка, якщо у вас є квадратні дужки, але для масиву чисел це досить багато досконалий.


Хоча це зазвичай спрацює, це не гарантується - Listне нав'язує структуру toString(). AbstractCollectionоднак, використовує цю структуру за замовчуванням, і я думаю, що всі загальні цілі Listреалізації в Java SE також добре. Як приклад того, що цього не відбувається, org.springframework.util.AutoPopulatingListнавесні не реалізується toString()і, таким чином, повернеться, наприклад, " org.springframework.util.AutoPopulatingList@170a1".
М. Джастін

Arrays.toString()був би кращим вибором, ніж Arrays.asList(list).toString(), оскільки це визначено для повернення еквівалентного рядка, він більш стислий і не потребує додаткового створення об'єкта.
М. Джастін

@ M.Justin Непогана частина масиву цієї відповіді, але як бути з простим "Список"? Ви не можете реально використовувати Arrays.toString () для цього. Як би ви запропонували використовувати цю техніку на щось подібне до згаданого вами автопопуляційного списку? Можливо: новий ArrayList (autoPopulatingList) .toString ()? Або я думаю, ви могли б перетворити його в масив. Можливо, якщо ви зіткнулися з такою проблемою, ви можете сподіватися, що ви перебуваєте на 1.8 або пізнішій версії і зможете скористатися однією з інших відповідей у ​​цій темі ...
Білл К

5

Інші відповіді чудово. Однак ви також можете передати Collectors.toList () як параметр Stream.collect (), щоб повернути елементи як ArrayList.

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

Якщо ви використовуєте System.out.print(ln), (list)він буде використовувати toString()метод елементів для друку списку. Отже, цей фрагмент коду просто повторює те, що відбувається всередині toStringсписку.
Ширкам

1
Повинен бути Collectors.toList (), якщо ви не імпортуєте статичну.
mwieczorek

Так ... я імпортував статичний приклад. Я заявив, що "Collectors.toList ()" в тілі відповіді ...;)
Едді B


2

Існує метод в String API для тих "приєднань до списку рядкових" випадків використання, вам навіть Stream не потрібен.

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 ( 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 для обстеження зазвичай потрібно від 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і розділяє кожен елемент комою ( ,).
Джо Алмор

2
Ваше рішення працює ТІЛЬКО, якщо список є Список <String>. ОП не вказав такого класу. У його питанні вказується навіть Список <Integer>. Ваш код не компілюється в такому випадку.
RubioRic

0

Я буду використовувати потоки api для перетворення потоку цілих чисел в єдиний рядок. Проблема деяких із наданих відповідей полягає в тому, що вони створюють O (n ^ 2) час виконання через створення String. Краще рішення - використовувати 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>. ОП не вказав такого класу. У його питанні вказується навіть Список <Integer>.
RubioRic
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.