Перетворення списку <Integer> у список <String>


105

У мене є список цілих чисел, List<Integer>і я хотів би перетворити всі цілі об'єкти в рядки, закінчивши таким чином новим List<String>.

Природно, я міг створити нове List<String>і циклічне виклик через список, що вимагає String.valueOf()кожного цілого числа, але мені було цікаво, чи існує кращий (читати: більш автоматичний ) спосіб зробити це?

Відповіді:


77

Наскільки мені відомо, ітерація та інстанція - це єдиний спосіб зробити це. Щось на зразок (для інших потенційна допомога, оскільки я впевнений, що ви знаєте, як це зробити):

List<Integer> oldList = ...
/* Specify the size of the list up front to prevent resizing. */
List<String> newList = new ArrayList<>(oldList.size());
for (Integer myInt : oldList) { 
  newList.add(String.valueOf(myInt)); 
}

Коли це просто, це називається красою.
Ельбек

1
Оригінальний плакат ніби вказував на те, що він думав про це, але вважав це рішення занадто складним або стомлюючим. Але мені важко уявити, що може бути простіше. Так, іноді вам потрібно написати 3 або 4 рядки коду, щоб виконати роботу.
Джей

Але це пов'язує вас з ArrayList. Чи можна це зробити за допомогою тієї ж реалізації, що і вихідний список?
alianos-

@Andreas oldList.getClass (). NewInstance () зробить
Lluis Martinez

96

Використовуючи колекції Google від Guava-Project , ви можете використовувати transformметод у класі Списки

import com.google.common.collect.Lists;
import com.google.common.base.Functions

List<Integer> integers = Arrays.asList(1, 2, 3, 4);

List<String> strings = Lists.transform(integers, Functions.toStringFunction());

ListПовертається transformце вид в списку основи - перетворення буде застосовуватися при кожному доступі до перетвореному списку.

Будьте в курсі, що Functions.toStringFunction()викинете a, NullPointerExceptionколи застосуєте до null, тому використовуйте його лише в тому випадку, якщо ви впевнені, що ваш список не буде містити null.


1
Буде добре, якщо поруч з функціями Functions.toStringFunction ()
ThiamTeck

1
чистий, але, можливо, не такий швидкий .. 1 додатковий виклик функції за значенням?
h3xStream

3
HotSpot може вбудовувати функціональні дзвінки - тому, якщо він викликається достатньо, він не повинен змінювати.
Бен Лінгс

3
Я не спростовую цього, тому що це дійсно рішення. Але заохочувати людей додавати залежність бібліотеки для вирішення такої простої задачі для мене не потрібно.
estani

1
Приємне рішення, якщо ви вже використовуєте Guava в нашому рішенні.
dudinha-dedalus

86

Рішення для Java 8. Трохи довше, ніж у Guava, але принаймні вам не потрібно встановлювати бібліотеку.

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

//...

List<Integer> integers = Arrays.asList(1, 2, 3, 4);
List<String> strings = integers.stream().map(Object::toString)
                                        .collect(Collectors.toList());

1
Хоча для toStringприкладу це трохи довше , воно стає коротшим для конверсій, не підтримуваних бібліотекою функцій Guava. Спеціальні функції все ще прості, але це значно більше коду, ніж цей потік Java 8
lightswitch05

40

Те, що ви робите, це добре, але якщо ви відчуваєте потребу в «Java-it-up», ви можете використовувати Трансформатор і метод збирання від Apache Commons , наприклад:

public class IntegerToStringTransformer implements Transformer<Integer, String> {
   public String transform(final Integer i) {
      return (i == null ? null : i.toString());
   }
}

..і потім..

CollectionUtils.collect(
   collectionOfIntegers, 
   new IntegerToStringTransformer(), 
   newCollectionOfStrings);

1
CollectionUtils.collect (collectionOfIntegers, новий org.apache.commons.collections.functors.StringValueTransformer ()); Але, StringValueTransformer використовує String.valueOf ...
Каннан Еканат

5
Якщо не зроблено нових робіт над колекціями apache, вони не роблять дженерики.
KitsuneYMG

1
Це дійсно Java-ing-it вниз. Це не ідіоматична Java, а більше схоже на функціональне програмування. Можливо, коли ми закриваємо в Java 8, ви можете назвати це ідіоматичною Java.
Крістофер Хаммарстрьом

Ви обов'язково хочете використовувати Collections4 для цього (а не старі колекції 3.x) для підтримки generics: generics commons.apache.org/proper/commons-collections/apidocs/org/…
JRA_TLL

Визначення нового класу просто для того, щоб бути "більш OOP або ідіоматичним" ... Я не бачу, наскільки це краще, ніж простий цикл для кожного. Це вимагає більше коду та відсуває функціональність подалі (що може бути пом’якшене анонімними класами, але все ж). Цей функціональний стиль стає корисним лише тоді, коли є гідний синтаксис (тобто лямбда-вирази з часу Java 8), як функціональні мови забезпечують його десятиліттями.
Оператор

9

Джерело для String.valueOf показує це:

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

Мало того, що це важливо, але я б скористався toString.


9

Замість використання String.valueOf я б використовував .toString (); це дозволяє уникнути деяких автобоксу, описаних @ johnathan.holland

Javadoc каже, що valueOf повертає те саме, що і Integer.toString ().

List<Integer> oldList = ...
List<String> newList = new ArrayList<String>(oldList.size());

for (Integer myInt : oldList) { 
  newList.add(myInt.toString()); 
}

як вказував Том Хоутін у відповіді "виграш", не можна примірник списку <String>, оскільки це лише інтерфейс.
Стю Томпсон

Хе, я це знав. Просто я написав код, не намагаючись його. Я зафіксую це у своїй відповіді.
ScArcher2

9

Ось однолінійне рішення без обману з бібліотекою, що не є JDK.

List<String> strings = Arrays.asList(list.toString().replaceAll("\\[(.*)\\]", "$1").split(", "));

7

Ще одне рішення з використанням Guava та Java 8

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = Lists.transform(numbers, number -> String.valueOf(number));

3

Не основна Java, і не загальна, але популярна бібліотека колекцій Джакарти має кілька корисних абстракцій для подібних завдань. Зокрема, перегляньте методи збирання

CollectionUtils

Що варто врахувати, якщо ви вже використовуєте колекції commons у своєму проекті.


4
Ніколи не використовуйте колекції Apache. Вони старі, застарілі, не безпечні для типів і погано написані.
KitsuneYMG

3

Людям, які турбуються про "бокс" у відповіді jsight: його немає. String.valueOf(Object)тут використовується, і не розблоковується дляint разу.

Використовуєте ви Integer.toString()чи String.valueOf(Object)залежить від того, як ви хочете обробити можливі нулі. Ви хочете викинути виняток (напевно) або мати "null" рядки у своєму списку (можливо). Якщо колишній, ви хочете кинути той NullPointerExceptionчи інший тип?

Крім того, одна невелика вада у відповіді jsight: Listце інтерфейс, на якому ви не можете використовувати нового оператора. Я, мабуть, використовував би java.util.ArrayListв цьому випадку, тим більше, що ми знаємо наперед, як довго цей список буде.



2

@Jonathan: Я можу помилитися, але я вважаю, що String.valueOf () в цьому випадку буде викликати функцію String.valueOf (Object), а не отримувати бокс для String.valueOf (int). String.valueOf (Object) просто повертає "null", якщо він є null, або викликає Object.toString (), якщо не є null, що не повинно залучати бокс (хоча, очевидно, задіяно нові об'єкти рядків).


2

Я думаю, що використання Object.toString () для будь-яких цілей, окрім налагодження, - це, мабуть, дуже погана ідея, навіть якщо в цьому випадку обидва є функціонально рівнозначними (якщо припустити, що у списку немає нулів). Розробники вільні змінювати поведінку будь-якого методу toString () без будь-якого попередження, включаючи методи toString () будь-яких класів у стандартній бібліотеці.

Навіть не хвилюйтеся з приводу продуктивних проблем, викликаних процесом боксу / розпакування. Якщо продуктивність критична, просто використовуйте масив. Якщо це дійсно критично, не використовуйте Java. Спроба перехитрити СВМ призведе лише до болю в серці.


2

Відповідь лише для експертів:

    List<Integer> ints = ...;
    String all = new ArrayList<Integer>(ints).toString();
    String[] split = all.substring(1, all.length()-1).split(", ");
    List<String> strs = Arrays.asList(split);

Це працює, але за рахунок неефективності. Струни Java - це два байти на char, тому "" додає фіксовану вартість у чотири байти на ціле число, перш ніж рахувати саме ціле число .... серед іншого.
Роберт Крістіан

Я думаю, що регулярний вираз може бути більшою проблемою в плані ефективності циклу. Щодо пам’яті, я думаю, що розумна реалізація (припускаючи, що «НД» необгрунтована реалізація String) поділиться тим самим резервним масивом (з all), тому буде насправді досить ефективною пам’яттю, що було б важливо для довгострокової роботи. Якщо тільки ви не хочете зберегти один із елементів, звичайно ...
Том Хоутін - таклін

2

Lambdaj дозволяє зробити це дуже простим і зрозумілим способом. Наприклад, якщо припустити, що у вас є список Integer і ви хочете їх перетворити у відповідне String-представлення, ви можете написати щось подібне;

List<Integer> ints = asList(1, 2, 3, 4);
Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
    public String convert(Integer i) { return Integer.toString(i); }
}

Lambdaj застосовує функцію перетворення лише тоді, коли ви повторюєте результат.


1

Ви не можете уникнути "боксу над головою"; У штучних універсальних контейнерах Java можуть зберігатися лише об'єкти, тому ваші вкладки повинні бути розміщені в цілі. В принципі, це може уникнути переходу з Object на Integer (оскільки це безглуздо, тому що Object досить хороший як для String.valueOf, так і для Object.toString), але я не знаю, чи компілятор досить розумний для цього. Перетворення з String в Object повинно бути більш-менш неоперативним, тому я не хочу переживати за це.


компілятор НЕ достатньо розумний для цього. Коли javac працює, він фактично викреслює всю інформацію про тип дженерики. В основі реалізації колекції дженериків ЗАВЖДИ зберігаються посилання на об’єкти. Ви можете фактично опустити параметризацію <T> і отримати "необроблений" тип. "Список l = новий List ()" проти "Список <String> l = новий Список <String> ()". Звичайно, це означає, що "Список <String> l = (Список <String>) новий Список <Integer> ()" насправді буде компілюватися та запускатися, але, очевидно, дуже небезпечно.
Дейв Допсон

1

Я не бачив жодного рішення, яке б відповідало принципу складності простору. Якщо список цілих чисел містить велику кількість елементів, то це велика проблема.

It will be really good to remove the integer from the List<Integer> and free
the space, once it's added to List<String>.

Ми можемо використовувати ітератор, щоб досягти того ж.

    List<Integer> oldList = new ArrayList<>();
    oldList.add(12);
    oldList.add(14);
    .......
    .......

    List<String> newList = new ArrayList<String>(oldList.size());
    Iterator<Integer> itr = oldList.iterator();
    while(itr.hasNext()){
        newList.add(itr.next().toString());
        itr.remove();
    }

1

Використання потоків: Якщо дозволити результат сказати список цілих чисел ( List<Integer> result), то:

List<String> ids = (List<String>) result.stream().map(intNumber -> Integer.toString(intNumber)).collect(Collectors.toList());

Один із шляхів її вирішення. Сподіваюся, це допомагає.


1

Трохи більш стисле рішення з використанням методу forEach у вихідному списку:

    List<Integer> oldList = Arrays.asList(1, 2, 3, 4, 5);
    List<String> newList = new ArrayList<>(oldList.size());
    oldList.forEach(e -> newList.add(String.valueOf(e)));

0

Для задоволення - рішення, що використовує jsr166y fork-join Framework, який повинен бути в JDK7.

import java.util.concurrent.forkjoin.*;

private final ForkJoinExecutor executor = new ForkJoinPool();
...
List<Integer> ints = ...;
List<String> strs =
    ParallelArray.create(ints.size(), Integer.class, executor)
    .withMapping(new Ops.Op<Integer,String>() { public String op(Integer i) {
        return String.valueOf(i);
    }})
    .all()
    .asList();

(Відмова: Не компілюється. Спеціалізація не доопрацьована. Тощо)

Навряд чи це буде в JDK7, це трохи виводу типу та синтаксичного цукру, щоб зробити це зMapping менш виразним:

    .withMapping(#(Integer i) String.valueOf(i))

0

Це така основна річ, що я б не використовував зовнішню бібліотеку (це спричинить залежність у вашому проекті, яка вам, мабуть, не потрібна).

У нас є клас статичних методів, спеціально створених для виконання таких завдань. Оскільки код для цього настільки простий, ми дозволяємо Hotspot зробити оптимізацію для нас. Здається, це тема в моєму коді останнім часом: написати дуже простий (прямолінійний) код і дозволити Hotspot зробити свою магію. У нас рідко виникають проблеми з таким кодом - коли з'являється нова версія VM, ви отримуєте всі додаткові переваги швидкості тощо.

Наскільки я люблю колекції Джакарти, вони не підтримують дженерики і використовують 1.4 як рідкокристалічний екран. Я насторожено ставлюся до колекцій Google, оскільки вони вказані як рівень підтримки Alpha!


-1

Мені просто хотілося прислухатись до об'єктно-орієнтованого рішення проблеми.

Якщо ви моделюєте доменні об’єкти, то рішення знаходиться в об’єктах домену. Тут домен - це Перелік цілих чисел, для яких ми хочемо рядкових значень.

Найпростішим способом було б взагалі не конвертувати список.

Це було сказано, щоб перетворити без перетворення, змініть початковий список Цілочисника на Список Значень, де Значення виглядає приблизно так ...

class Value {
    Integer value;
    public Integer getInt()
    {
       return value;
    }
    public String getString()
    {
       return String.valueOf(value);
    }
}

Це буде швидше і займе менше пам'яті, ніж копіювання списку.

Щасти!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.