У меня есть список целых чисел, List<Integer>, и я хотел бы преобразовать все целочисленные объекты в строки, завершив тем самым новый List<String>.
Естественно, я мог бы создать новый List<String> и пройти по списку, вызывая String.valueOf() для каждого целого числа, но мне было интересно, есть ли лучший (читай: более автоматический) способ сделать это?




Насколько мне известно, итерация и создание экземпляра - единственный способ сделать это. Что-то вроде (для потенциальной помощи другим, поскольку я уверен, что вы знаете, как это сделать):
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));
}
Исходный плакат, казалось, указывал на то, что он думал об этом, но считал это решение слишком сложным или утомительным. Но мне сложно представить, что может быть проще. Да, иногда вам нужно написать 3 или 4 строки кода, чтобы выполнить работу.
Но это привязывает вас к ArrayList. Можно ли это сделать, используя ту же реализацию, что и в исходном списке?
@Andreas oldList.getClass (). NewInstance () будет делать
Красиво и чисто.
@Jonathan: Я могу ошибаться, но я считаю, что String.valueOf () в этом случае вызовет функцию String.valueOf (Object), а не будет вставлен в String.valueOf (int). String.valueOf (Object) просто возвращает «null», если он равен нулю, или вызывает Object.toString (), если он не равен нулю, что не должно включать бокс (хотя, очевидно, задействовано создание экземпляров новых строковых объектов).
Вместо 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
Хех, я знал это. Я просто написал код, не пробуя его. Я исправлю это в своем ответе.
Я думаю, что использование Object.toString () для любых целей, кроме отладки, вероятно, действительно плохая идея, хотя в этом случае они функционально эквивалентны (при условии, что в списке нет нулей). Разработчики могут изменять поведение любого метода toString () без предупреждения, включая методы toString () любых классов в стандартной библиотеке.
Даже не беспокойтесь о проблемах с производительностью, вызванных процессом упаковки / распаковки. Если производительность критична, просто используйте массив. Если это действительно критично, не используйте Java. Попытка перехитрить JVM приведет только к душевной боли.
Источник String.valueOf показывает это:
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
Не то чтобы это важно, но я бы использовал toString.
Вы не можете избежать «накладных расходов на бокс»; Поддельные универсальные контейнеры Java могут хранить только объекты, поэтому ваши целые числа должны быть помещены в целые числа. В принципе, можно было бы избежать перехода от Object к Integer (поскольку это бессмысленно, потому что Object достаточно хорош как для String.valueOf, так и для Object.toString), но я не знаю, достаточно ли умен компилятор для этого. Преобразование из String в Object должно быть более или менее беспроблемным, поэтому я не склонен беспокоиться об этом.
компилятор НЕ достаточно умен для этого. Когда запускается javac, он фактически удаляет всю информацию о типах обобщенных типов. Базовая реализация коллекции универсальных шаблонов ВСЕГДА хранит ссылки на объекты. Фактически вы можете опустить
Не ядро Java и не обобщенное, но популярная библиотека коллекций общего пользования Джакарты содержит некоторые полезные абстракции для такого рода задач. В частности, взгляните на методы сбора на
Что следует учитывать, если вы уже используете в своем проекте общие коллекции.
Никогда не используйте коллекции Apache. Они старые, устаревшие, небезопасные и плохо написанные.
То, что вы делаете, в порядке, но если вы чувствуете необходимость «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);
CollectionUtils.collect (collectionOfIntegers, новый org.apache.commons.collections.functors.StringValueTransform er ()); Но StringValueTransformer использует String.valueOf ...
Если над коллекциями apache не была проделана новая работа, они не делают дженериков.
Это действительно Java-ing-it down. Это не идиоматическая Java, а больше похоже на функциональное программирование. Может быть, когда мы получим замыкания в Java 8, вы сможете назвать это идиоматической Java.
Вы определенно хотите использовать для этого Collections4 (а не старые коллекции 3.x) для поддержки дженериков: commons.apache.org/proper/commons-collections/apidocs/org/…
Определение нового класса просто для того, чтобы быть «более ООП или идиоматическим» ... Я не понимаю, чем это лучше, чем простой цикл for-each. Это требует большего количества кода и отодвигает функциональность (что может быть смягчено анонимными классами, но все же). Этот функциональный стиль становится полезным только тогда, когда есть приличный синтаксис (т.е. лямбда-выражения, начиная с Java 8), как функциональные языки обеспечивали его десятилетиями.
Людям, озабоченным «боксом» в jsight ответ: нет. Здесь используется String.valueOf(Object), и распаковка на int никогда не выполняется.
Используете ли вы Integer.toString() или String.valueOf(Object), зависит от того, как вы хотите обрабатывать возможные значения NULL. Вы хотите выбросить исключение (возможно) или иметь в вашем списке «нулевые» строки (возможно). Если первое, то вы хотите скинуть NullPointerException или какой другой тип?
Также есть один небольшой недостаток в ответе jsight: List - это интерфейс, в нем нельзя использовать оператор new. Я бы, вероятно, использовал в этом случае java.util.ArrayList, тем более что мы заранее знаем, какой длины будет список.
Ответ только для экспертов:
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 - это два байта на символ, поэтому "," добавляет четырехбайтовую фиксированную стоимость на целое число перед подсчетом самого целого числа .... среди прочего.
Я думаю, что регулярное выражение может быть больше проблемой с точки зрения необработанной эффективности цикла процессора. Что касается памяти, я полагаю, что разумная реализация (при условии, что неразумная реализация String "Sun") будет использовать один и тот же массив резервных копий (из all), поэтому на самом деле будет достаточно эффективно использовать память, что было бы важно для долгосрочной производительности. Если, конечно, вы не хотите сохранить только один из элементов ...
Просто для удовольствия, решение, использующее структуру 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))
Это настолько простая вещь, что я бы не стал использовать внешнюю библиотеку (это вызовет зависимость в вашем проекте, которая вам, вероятно, не понадобится).
У нас есть класс статических методов, специально созданных для выполнения таких задач. Поскольку код для этого очень прост, мы позволяем Hotspot выполнять оптимизацию за нас. Кажется, в последнее время это тема в моем коде: напишите очень простой (понятный) код и позвольте Hotspot творить чудеса. У нас редко возникают проблемы с производительностью вокруг такого кода - когда выходит новая версия виртуальной машины, вы получаете все дополнительные преимущества скорости и т. д.
Как бы мне ни нравились коллекции Jakarta, они не поддерживают Generics и используют 1.4 в качестве ЖК-дисплея. Я с осторожностью отношусь к коллекциям Google, потому что они указаны как уровень поддержки Alpha!
Используя Коллекции 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() выдаст NullPointerException при применении к null, поэтому используйте его только в том случае, если вы уверены, что ваш список не будет содержать null.
Было бы неплохо, если бы помимо Functions.toStringFunction () было больше готовых функций
чисто, но, может быть, не так быстро .. 1 дополнительный вызов функции на значение?
HotSpot может встраивать вызовы функций, поэтому, если он вызывается достаточно, это не имеет значения.
Я не отвергаю это, потому что это действительно решение. Но побуждать людей добавлять библиотечные зависимости для решения такой простой задачи для меня - непростая задача.
Хорошее решение, если вы уже используете Guava в нашем решении.
Потенциальная проблема заключается в том, что преобразование применяется лениво (потенциально много раз). Вместо этого вы можете вернуть new ArrayList<>(Lists.transform(integers, Functions.toStringFunction())), чтобы избежать этого,
Обратное, если вы наткнулись на этот вопрос в поисках String -> Number: Lists.transform(listOfStrings, Ints.stringConverter());
Лямбдаж позволяет сделать это очень простым и читаемым способом. Например, предположим, что у вас есть список целых чисел и вы хотите преобразовать их в соответствующее строковое представление, вы можете написать что-то вроде этого;
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 применяет функцию преобразования только во время итерации результата.
Вот однострочное решение без обмана с библиотекой, отличной от JDK.
List<String> strings = Arrays.asList(list.toString().replaceAll("\[(.*)\]", "").split(", "));
Я просто хотел предложить объектно-ориентированное решение проблемы.
Если вы моделируете объекты предметной области, то решение находится в объектах предметной области. Домен здесь - это список целых чисел, для которых нам нужны строковые значения.
Самый простой способ - вообще не преобразовывать список.
При этом, чтобы преобразовать без преобразования, измените исходный список Integer на List of Value, где Value выглядит примерно так ...
class Value {
Integer value;
public Integer getInt()
{
return value;
}
public String getString()
{
return String.valueOf(value);
}
}
Это будет быстрее и займет меньше памяти, чем копирование списка.
Удачи!
Решение для 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());
Хотя это немного больше для примера toString, оно оказывается короче для преобразований, не поддерживаемых библиотекой функций Guava. Пользовательские функции по-прежнему просты, но это значительно больше кода, чем этот поток Java 8
Вот и «обратный» путь для будущих читателей. Спасибо трейказ! Коллекция
Другое решение с использованием Guava и Java 8
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = Lists.transform(numbers, number -> String.valueOf(number));
I didn't see any solution which is following the principal of space complexity. If list of integers has large number of elements then it's big problem.
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();
}
List<String> stringList = integerList.stream().map((Object s)->String.valueOf(s)).collect(Collectors.toList())
@RequiresApi (api = Build.VERSION_CODES.N)
Использование потоков:
Если, скажем, результат - это список целых чисел (List<Integer> result), то:
List<String> ids = (List<String>) result.stream().map(intNumber -> Integer.toString(intNumber)).collect(Collectors.toList());
Один из способов ее решения. Надеюсь это поможет.
Чуть более краткое решение с использованием метода 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)));
Когда это просто, это называется красотой.