Преобразование List <Integer> в List <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метод в классе Lists

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это вид в списке основы - преобразование будет применяться при каждом доступе к преобразованному списку.

Имейте в виду, что при применении к null Functions.toStringFunction()будет генерироваться a NullPointerException, поэтому используйте его только в том случае, если вы уверены, что ваш список не будет содержать null.


1
Было бы неплохо, если бы помимо Functions.toStringFunction () было больше готовых функций
ThiamTeck 07

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 Functions. Пользовательские функции по-прежнему просты, но это значительно больше кода, чем этот поток Java 8
lightswitch05,

40

То, что вы делаете, в порядке, но если вы чувствуете необходимость «Java-it-up», вы можете использовать Transformer и метод collect из 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 down. Это не идиоматическая Java, а больше похоже на функциональное программирование. Может быть, когда мы получим замыкания в Java 8, вы сможете назвать это идиоматической Java.
Christoffer Hammarström

Вы определенно хотите использовать для этого Collections4 (а не старые коллекции 3.x) для поддержки универсальных шаблонов
JRA_TLL 06

Определение нового класса только для того, чтобы быть «более ООП или идиоматическим» ... Я не понимаю, чем это лучше, чем простой цикл for-each. Это требует больше кода и убирает функциональность (что могло бы быть смягчено анонимными классами, но все же). Этот функциональный стиль становится полезным только тогда, когда есть приличный синтаксис (например, лямбда-выражения, начиная с Java 8), как функциональные языки обеспечивали его десятилетиями.
TheOperator

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

как указал Том Хотин в «выигрышном» ответе, нельзя использовать экземпляр List <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

Что следует учитывать, если вы уже используете в своем проекте общие коллекции.


4
Никогда не используйте коллекции Apache. Они старые, устаревшие, небезопасные и плохо написанные.
KitsuneYMG

3

Тем, кого беспокоит «бокс», в jsight ответ: нет. String.valueOf(Object)здесь используется, и распаковка в intне выполняется.

Используете ли вы Integer.toString()или это String.valueOf(Object)зависит от того, как вы хотите обрабатывать возможные значения NULL. Вы хотите выбросить исключение (возможно) или иметь в вашем списке «нулевые» строки (возможно). Если первое, вы хотите бросить какой- NullPointerExceptionнибудь другой тип?

Также есть один небольшой недостаток в ответе jsight: Listэто интерфейс, в нем нельзя использовать оператор new. Я бы, вероятно, использовал java.util.ArrayListв этом случае, тем более что мы заранее знаем, насколько длинным может быть список.



2

@Jonathan: Я могу ошибаться, но я считаю, что String.valueOf () в этом случае вызовет функцию String.valueOf (Object), а не будет вставлен в String.valueOf (int). String.valueOf (Object) просто возвращает «null», если он равен нулю, или вызывает Object.toString (), если не равно null, что не должно включать бокс (хотя, очевидно, задействовано создание экземпляров новых строковых объектов).


2

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

Даже не беспокойтесь о проблемах с производительностью, вызванных процессом упаковки / распаковки. Если производительность критична, просто используйте массив. Если это действительно важно, не используйте Java. Попытка перехитрить JVM приведет только к душевной боли.


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 - это два байта на символ, поэтому "," добавляет четырехбайтовую фиксированную стоимость на целое число перед подсчетом самого целого числа .... среди прочего.
Роберт Кристиан,

Я думаю, что регулярное выражение может быть больше проблемой с точки зрения необработанной эффективности цикла процессора. Что касается памяти, я предполагаю, что разумная реализация (предполагая, что неразумная реализация "Sun" String) будет использовать один и тот же резервный массив (from all), поэтому на самом деле будет достаточно эффективно использовать память, что было бы важно для долгосрочной производительности. Если, конечно, вы не хотите сохранить только один из элементов ...
Том Хотин - tackline

2

Lambdaj позволяет сделать это очень простым и понятным способом. Например, предположим, что у вас есть список целых чисел и вы хотите преобразовать их в соответствующее строковое представление, вы можете написать что-то вроде этого;

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 = новый список ()» по сравнению с «Список <String> l = новый список <String> ()». конечно, это означает, что «List <String> l = (List <String>) new List <Integer> ()» действительно будет компилироваться и запускаться, но это, очевидно, очень опасно.
Dave Dopson

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

Просто для удовольствия, решение, использующее платформу fork-join jsr166y, которая должна быть в 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 есть немного вывода типа и синтаксический сахар, чтобы сделать это с помощью вызова сопоставления менее подробным:

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

0

Это такая простая вещь, что я бы не стал использовать внешнюю библиотеку (это вызовет зависимость в вашем проекте, которая вам, вероятно, не понадобится).

У нас есть класс статических методов, специально созданных для выполнения подобных задач. Поскольку код для этого очень прост, мы позволяем Hotspot выполнять оптимизацию за нас. Похоже, что в последнее время это тема в моем коде: напишите очень простой (понятный) код и позвольте Hotspot творить чудеса. У нас редко возникают проблемы с производительностью вокруг такого кода - с выходом новой версии виртуальной машины вы получаете все дополнительные преимущества скорости и т. Д.

Как бы мне ни нравились коллекции Jakarta, они не поддерживают Generics и используют 1.4 в качестве ЖК-дисплея. Я с осторожностью отношусь к коллекциям Google, потому что они указаны как уровень поддержки Alpha!


-1

Я просто хотел предложить объектно-ориентированное решение проблемы.

Если вы моделируете объекты предметной области, то решение находится в объектах предметной области. Домен здесь - это список целых чисел, для которых нам нужны строковые значения.

Самый простой способ - вообще не преобразовывать список.

При этом, чтобы преобразовать без преобразования, измените исходный список Integer на List of Value, где Value выглядит примерно так ...

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.