Convertendo List <Integer> em List <String>


105

Eu tenho uma lista de inteiros List<Integer>e gostaria de converter todos os objetos inteiros em Strings, terminando assim com um novo List<String>.

Naturalmente, eu poderia criar um novo List<String>e percorrer a lista chamando String.valueOf()cada número inteiro, mas gostaria de saber se há uma maneira melhor (leia-se: mais automática ) de fazer isso?

Respostas:


77

Pelo que eu sei, iterar e instanciar é a única maneira de fazer isso. Algo como (para outros ajuda em potencial, já que tenho certeza que você sabe fazer isso):

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

Quando é simples, isso se chama beleza.
Elbek,

1
O pôster original parecia indicar que ele havia pensado nisso, mas considerava a solução muito complexa ou tediosa. Mas tenho dificuldade em imaginar o que poderia ser mais fácil. Sim, às vezes você precisa escrever 3 ou 4 linhas de código para fazer um trabalho.
Jay,

Mas isso o liga a ArrayList. Isso pode ser feito usando a mesma implementação da lista original?
alianos

@Andreas oldList.getClass (). NewInstance () servirá
Lluis Martinez

96

Usando as coleções do Google do Guava-Project , você pode usar o transformmétodo na classe 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());

O Listretornado por transformé uma visualização na lista de apoio - a transformação será aplicada em cada acesso à lista transformada.

Esteja ciente de que Functions.toStringFunction()lançará um NullPointerExceptionquando aplicado a null, portanto, use-o somente se tiver certeza de que sua lista não conterá null.


1
Será bom se houver mais funções prontas ao lado de Functions.toStringFunction ()
ThiamTeck

1
limpo, mas talvez não tão rápido .. 1 chamada de função extra por valor?
h3xStream

3
O HotSpot pode fazer chamadas de função inline - então, se for chamado o suficiente, não deve fazer diferença.
Ben Lings

3
Eu não votei contra isso porque é de fato uma solução. Mas encorajar as pessoas a adicionar uma dependência de biblioteca para resolver uma tarefa tão simples é impossível para mim.
estani

1
Boa solução se você já está usando Guava em nossa solução.
dudinha-dedalus

86

Solução para Java 8. Um pouco mais longa que a Guava, mas pelo menos não precisa instalar biblioteca.

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
Embora seja um pouco mais longo para o toStringexemplo, ele acaba sendo mais curto para conversões não suportadas pela biblioteca de funções do Guava. Funções personalizadas ainda são fáceis, mas é significativamente mais código do que este fluxo Java 8
lightswitch05

40

O que você está fazendo está bem, mas se sentir a necessidade de fazer um 'Java-it-up', você pode usar um Transformer e o método de coleta do Apache Commons , por exemplo:

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

..e depois..

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

1
CollectionUtils.collect (collectionOfIntegers, novo org.apache.commons.collections.functors.StringValueTransformer ()); Mas, StringValueTransformer usa String.valueOf ...
Kannan Ekanath

5
A menos que um novo trabalho tenha sido feito nas coleções do Apache, eles não fazem genéricos.
KitsuneYMG

1
Isso é realmente Java-in-it down. Isso não é Java idiomático e mais parecido com programação funcional. Talvez, quando tivermos encerramentos em Java 8, você possa chamá-lo de Java idiomático.
Christoffer Hammarström

Você definitivamente deseja usar Collections4 para isso (não as coleções 3.x antigas) para suporte de genéricos: commons.apache.org/proper/commons-collections/apidocs/org/…
JRA_TLL

Definir uma nova classe apenas para ser "mais OOP ou idiomática" ... Não vejo como isso é melhor do que o simples loop for-each. Ele requer mais código e remove a funcionalidade (o que poderia ser atenuado por classes anônimas, mas ainda assim). Esse estilo funcional só começa a se tornar útil quando há uma sintaxe decente (ou seja, expressões lambda desde Java 8), como as linguagens funcionais a fornecem há décadas.
TheOperator

9

A fonte de String.valueOf mostra isso:

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

Não que isso importe muito, mas eu usaria toString.


9

Em vez de usar String.valueOf, eu usaria .toString (); evita alguns dos boxe automático descritos por @ johnathan.holland

O javadoc diz que valueOf retorna a mesma coisa que Integer.toString ().

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

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

conforme apontado por Tom Hawtin na resposta 'vencedora', não se pode instanciar List <String>, pois é apenas uma interface.
Stu Thompson

Heh, eu sabia disso. Apenas escrevi o código sem tentar. Vou consertar na minha resposta.
ScArcher2

9

Aqui está uma solução de uma linha sem trapacear com uma biblioteca não JDK.

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

7

Outra solução usando Guava e Java 8

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

3

Não é o núcleo do Java e não é genérico, mas a popular biblioteca de coleções comuns de Jakarta tem algumas abstrações úteis para esse tipo de tarefa. Especificamente, dê uma olhada nos métodos de coleta em

CollectionUtils

Algo a considerar se você já estiver usando coleções comuns em seu projeto.


4
Nunca use coleções do Apache. Eles são antigos, desatualizados, não são seguros para o tipo e são mal escritos.
KitsuneYMG

3

Para as pessoas preocupadas com o "boxe" na resposta de jsight: não há nenhum. String.valueOf(Object)é usado aqui, e nenhum unboxing para inté executado.

Se você usa Integer.toString()ou String.valueOf(Object)depende de como deseja tratar possíveis nulos. Você deseja lançar uma exceção (provavelmente) ou ter Strings "nulas" em sua lista (talvez). Se for o primeiro, você quer jogar um NullPointerExceptionou algum outro tipo?

Além disso, uma pequena falha na resposta do jsight: Listé uma interface, você não pode usar o novo operador nela. Eu provavelmente usaria um java.util.ArrayListneste caso, especialmente porque sabemos de antemão o tamanho provável da lista.


3
List<String> stringList = integerList.stream().map((Object s)->String.valueOf(s)).collect(Collectors.toList())

2

@Jonathan: Posso estar enganado, mas acredito que String.valueOf () neste caso chamará a função String.valueOf (Object) em vez de ser encaixotado em String.valueOf (int). String.valueOf (Object) apenas retorna "nulo" se for nulo ou chama Object.toString () se não nulo, o que não deve envolver boxing (embora, obviamente, instanciar novos objetos de string esteja envolvido).


2

Acho que usar Object.toString () para qualquer propósito diferente de depuração é provavelmente uma ideia muito ruim, embora, nesse caso, os dois sejam funcionalmente equivalentes (assumindo que a lista não tenha nulos). Os desenvolvedores são livres para alterar o comportamento de qualquer método toString () sem qualquer aviso, incluindo os métodos toString () de qualquer classe na biblioteca padrão.

Nem se preocupe com os problemas de desempenho causados ​​pelo processo de boxing / unboxing. Se o desempenho for crítico, basta usar um array. Se for realmente crítico, não use Java. Tentar ser mais esperto que o JVM só levará à dor de cabeça.


2

Uma resposta apenas para especialistas:

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

Isso funciona, mas às custas da ineficiência. Java Strings são dois bytes por caractere, então o "," adiciona um custo fixo de quatro bytes por inteiro antes de contar o próprio inteiro .... entre outras coisas.
Robert Christian,

Acho que o regex pode ser mais um problema em termos de eficiência bruta do ciclo da CPU. Em termos de memória, acho que uma implementação razoável (supondo que a implementação irracional "Sun" de String) compartilhará a mesma matriz de apoio (de all), então será realmente bastante eficiente em termos de memória, o que seria importante para o desempenho de longo prazo. A menos que você queira manter apenas um dos elementos do curso ...
Tom Hawtin - tackline

2

Lambdaj permite fazer isso de uma forma muito simples e legível. Por exemplo, supondo que você tenha uma lista de Inteiros e deseja convertê-los na representação String correspondente, você poderia escrever algo assim;

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 aplica a função de conversão apenas enquanto você está iterando no resultado.


1

Você não pode evitar a "sobrecarga do boxe"; Os falsos contêineres genéricos do Java só podem armazenar objetos, então seus ints devem ser encaixotados em inteiros. Em princípio, isso poderia evitar o downcast de Object para Integer (uma vez que é inútil, porque Object é bom o suficiente para String.valueOf e Object.toString), mas não sei se o compilador é inteligente o suficiente para fazer isso. A conversão de String para Object deve ser mais ou menos autônoma, então eu não gostaria de me preocupar com isso.


o compilador NÃO é inteligente o suficiente para fazer isso. Quando o javac é executado, ele remove todas as informações do tipo genérico. A implementação subjacente de uma coleção de genéricos SEMPRE armazena referências de objetos. Você pode realmente omitir a parametrização <T> e obter um tipo "bruto". "List l = new List ()" versus "List <String> l = new List <String> ()". obviamente, isso significa que "List <String> l = (List <String>) new List <Integer> ()" irá compilar e executar, mas é, obviamente, muito perigoso.
Dave Dopson

1

Não vi nenhuma solução que siga o princípio da complexidade do espaço. Se a lista de inteiros tiver um grande número de elementos, é um grande problema.

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

Podemos usar o iterador para conseguir o mesmo.

    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

Usando Streams: Se digamos que o resultado é uma lista de inteiros ( List<Integer> result), então:

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

Uma das maneiras de resolver isso. Espero que isto ajude.


1

Uma solução um pouco mais concisa usando o método forEach na lista original:

    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

Apenas por diversão, uma solução usando a estrutura de junção de garfo jsr166y que deveria estar no 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();

(Isenção de responsabilidade: não compilado. A especificação não foi finalizada. Etc.)

O improvável de estar no JDK7 é um pouco de inferência de tipo e açúcar sintático para tornar a chamada withMapping menos detalhada:

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

0

Isso é algo tão básico que eu não usaria uma biblioteca externa (isso causará uma dependência em seu projeto que você provavelmente não precisa).

Temos uma classe de métodos estáticos criados especificamente para esse tipo de trabalho. Como o código para isso é tão simples, deixamos o Hotspot fazer a otimização para nós. Este parece ser um tema em meu código recentemente: escrever código muito simples (direto) e deixar o Hotspot fazer sua mágica. Raramente temos problemas de desempenho em torno de código como este - quando uma nova versão VM chega, você obtém todos os benefícios extras de velocidade, etc.

Por mais que eu ame as coleções Jakarta, elas não suportam Genéricos e usam 1.4 como LCD. Desconfio das Coleções do Google porque elas estão listadas como nível de suporte Alfa!


-1

Eu só queria sugerir uma solução orientada a objetos para o problema.

Se você modelar objetos de domínio, a solução está nos objetos de domínio. O domínio aqui é uma lista de inteiros para os quais queremos valores de string.

A maneira mais fácil seria não converter a lista.

Dito isso, para converter sem converter, altere a lista original de Inteiro para Lista de Valor, onde Valor se parece com isto ...

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

Isso será mais rápido e ocupará menos memória do que copiar a lista.

Boa sorte!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.