Conversione di List <Integer> in List <String>


105

Ho un elenco di numeri interi List<Integer>e vorrei convertire tutti gli oggetti interi in stringhe, terminando così con un nuovo file List<String>.

Naturalmente, potrei creare un nuovo List<String>e scorrere l'elenco chiamando String.valueOf()ogni numero intero, ma mi chiedevo se ci fosse un modo migliore (leggi: più automatico ) per farlo?

Risposte:


77

Per quanto ne so, iterare e istanziare è l'unico modo per farlo. Qualcosa del tipo (per altri potenziali aiuti, dal momento che sono sicuro che tu sappia come farlo):

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 è semplice, si chiama bellezza.
Elbek

1
Il poster originale sembrava indicare che aveva pensato a questo ma considerava questa soluzione troppo complessa o noiosa. Ma non riesco a immaginare cosa potrebbe essere più facile. Sì, a volte devi scrivere 3 o 4 righe di codice per portare a termine un lavoro.
Jay

Ma questo ti lega ad ArrayList. È possibile farlo utilizzando la stessa implementazione dell'elenco originale?
alianos

@Andreas oldList.getClass (). NewInstance () farà
Lluis Martinez

96

Utilizzando Google Collections da Guava-Project , potresti utilizzare il transformmetodo nella 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());

Il Listrestituito da transformè una vista nell'elenco di backup: la trasformazione verrà applicata a ogni accesso all'elenco trasformato.

Tieni presente che Functions.toStringFunction()lancerà un NullPointerExceptionquando applicato a null, quindi usalo solo se sei sicuro che il tuo elenco non conterrà null.


1
Sarebbe bello se ci fossero più funzioni pronte accanto a Functions.toStringFunction ()
ThiamTeck

1
pulito ma forse non così veloce .. 1 chiamata di funzione extra per valore?
h3xStream

3
HotSpot può chiamare funzioni inline, quindi se viene chiamato abbastanza, non dovrebbe fare la differenza.
Ben Lings

3
Non lo sottovaluto perché è davvero una soluzione. Ma incoraggiare le persone ad aggiungere una dipendenza dalla libreria per risolvere un compito così semplice per me è un no-go.
estani

1
Bella soluzione se stai già usando Guava nella nostra soluzione.
dudinha-dedalus

86

Soluzione per Java 8. Un po 'più lungo di Guava, ma almeno non devi installare una libreria.

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
Sebbene questo sia un po 'più lungo per l' toStringesempio, finisce per essere più breve per le conversioni non supportate dalla libreria di funzioni di Guava. Le funzioni personalizzate sono ancora facili, ma è molto più codice di questo flusso Java 8
lightswitch05

40

Quello che stai facendo va bene, ma se senti il ​​bisogno di 'Java-it-up' potresti usare un Transformer e il metodo collect di Apache Commons , ad esempio:

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

..e poi..

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

1
CollectionUtils.collect (collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer ()); Tuttavia, StringValueTransformer utilizza String.valueOf ...
Kannan Ekanath

5
A meno che non sia stato fatto del nuovo lavoro sulle raccolte Apache, non fanno i generici.
KitsuneYMG

1
Questo è davvero Java-ing-it down. Questo non è Java idiomatico e più simile alla programmazione funzionale. Forse quando avremo la chiusura in Java 8 potresti chiamarlo Java idiomatico.
Christoffer Hammarström

Sicuramente vuoi usare Collections4 per quello (non le vecchie 3.x Collections) per il supporto dei generici: commons.apache.org/proper/commons-collections/apidocs/org/…
JRA_TLL

Definire una nuova classe solo per essere "più OOP o idiomatica" ... Non vedo come questo sia migliore del semplice ciclo for-each. Richiede più codice e sposta la funzionalità (che potrebbe essere mitigata da classi anonime, ma comunque). Questo stile funzionale inizia a diventare utile solo quando c'è una sintassi decente (cioè espressioni lambda a partire da Java 8), come i linguaggi funzionali l'hanno fornita per decenni.
TheOperator

9

La fonte per String.valueOf mostra questo:

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

Non che importi molto, ma userei toString.


9

Invece di usare String.valueOf userei .toString (); evita parte dell'auto boxe descritta da @ johnathan.holland

Il javadoc dice che valueOf restituisce la stessa cosa di Integer.toString ().

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

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

come sottolineato da Tom Hawtin nella risposta "vincente", non è possibile istanziare List <String> poiché è solo un'interfaccia.
Stu Thompson

Eh lo sapevo. Ho appena scritto il codice senza provarlo. Lo aggiusterò nella mia risposta.
ScArcher2

9

Ecco una soluzione one-liner senza barare con una libreria non JDK.

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

7

Un'altra soluzione che utilizza 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

Non core Java, e non generici, ma la popolare libreria di collezioni Jakarta commons ha alcune utili astrazioni per questo tipo di attività. In particolare, dai un'occhiata ai metodi di raccolta su

CollectionUtils

Qualcosa da considerare se stai già utilizzando raccolte di beni comuni nel tuo progetto.


4
Non utilizzare mai le raccolte Apache. Sono vecchi, obsoleti, non sicuri per i tipi e scritti male.
KitsuneYMG

3

Per le persone preoccupate per la "boxe" nella risposta di jsight: non ce n'è. String.valueOf(Object)viene utilizzato qui e non intviene mai eseguito l' unboxing di .

Se usi Integer.toString()o String.valueOf(Object)dipende da come vuoi gestire eventuali valori nulli. Vuoi lanciare un'eccezione (probabilmente) o avere stringhe "nulle" nella tua lista (forse). Nel primo caso, vuoi lanciarne uno NullPointerExceptiono un altro tipo?

Inoltre, un piccolo difetto nella risposta di jsight: Listè un'interfaccia, non puoi usare il nuovo operatore su di essa. Probabilmente userò un java.util.ArrayListin questo caso, soprattutto perché sappiamo in anticipo quanto è probabile che sia lungo l'elenco.


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

2

@ Jonathan: potrei sbagliarmi, ma credo che String.valueOf () in questo caso chiamerà la funzione String.valueOf (Object) invece di essere inscatolato in String.valueOf (int). String.valueOf (Object) restituisce semplicemente "null" se è null o chiama Object.toString () se non è null, il che non dovrebbe coinvolgere il boxing (sebbene sia ovviamente coinvolta la creazione di istanze di nuovi oggetti stringa).


2

Penso che l'uso di Object.toString () per qualsiasi scopo diverso dal debug sia probabilmente una pessima idea, anche se in questo caso i due sono funzionalmente equivalenti (supponendo che l'elenco non abbia null). Gli sviluppatori sono liberi di modificare il comportamento di qualsiasi metodo toString () senza alcun preavviso, inclusi i metodi toString () di qualsiasi classe nella libreria standard.

Non preoccuparti nemmeno dei problemi di prestazioni causati dal processo di boxe / unboxing. Se le prestazioni sono critiche, usa semplicemente un array. Se è davvero fondamentale, non utilizzare Java. Cercare di superare in astuzia la JVM porterà solo angoscia.


2

Una risposta solo per esperti:

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

Funziona ma a scapito dell'inefficienza. Le stringhe Java sono due byte per carattere, quindi "," aggiunge un costo fisso di quattro byte per numero intero prima di contare il numero intero stesso ... tra le altre cose.
Robert Christian

Penso che la regex potrebbe essere più un problema in termini di efficienza del ciclo CPU grezzo. In termini di memoria, immagino che un'implementazione ragionevole (assumendo l'implementazione irragionevole di "Sun" String) condividerà lo stesso array di supporto (da all), quindi sarà effettivamente abbastanza efficiente in termini di memoria, il che sarebbe importante per prestazioni a lungo termine. A meno che tu non voglia mantenere solo uno degli elementi, ovviamente ...
Tom Hawtin - tackline

2

Lambdaj permette di farlo in un modo molto semplice e leggibile. Ad esempio, supponendo che tu abbia una lista di Integer e desideri convertirli nella corrispondente rappresentazione String potresti scrivere qualcosa del genere;

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 applica la funzione di conversione solo durante l'iterazione del risultato.


1

Non puoi evitare il "boxe overhead"; I falsi contenitori generici di Java possono memorizzare solo oggetti, quindi i tuoi int devono essere racchiusi in numeri interi. In linea di principio potrebbe evitare il downcast da Object a Integer (poiché è inutile, perché Object è abbastanza buono sia per String.valueOf che per Object.toString) ma non so se il compilatore è abbastanza intelligente da farlo. La conversione da String a Object dovrebbe essere più o meno un no-op, quindi non sarei incline a preoccuparmi di quello.


il compilatore NON è abbastanza intelligente per farlo. Quando javac viene eseguito, rimuove effettivamente tutte le informazioni sul tipo di generici. L'implementazione sottostante di una raccolta generics memorizza SEMPRE i riferimenti agli oggetti. Puoi effettivamente omettere la parametrizzazione <T> e ottenere un tipo "grezzo". "List l = new List ()" rispetto a "List <String> l = new List <String> ()". ovviamente, questo significa che "List <String> l = (List <String>) new List <Integer> ()" verrà effettivamente compilato ed eseguito, ma è, ovviamente, molto pericoloso.
Dave Dopson

1

Non ho visto alcuna soluzione che segua il principio della complessità spaziale. Se l'elenco di numeri interi ha un numero elevato di elementi, allora è un grosso 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>.

Possiamo usare l'iteratore per ottenere lo stesso risultato.

    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

Utilizzo di flussi: se diciamo che il risultato è un elenco di numeri interi ( List<Integer> result), allora:

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

Uno dei modi per risolverlo. Spero che questo ti aiuti.


1

Una soluzione leggermente più concisa che utilizza il metodo forEach nell'elenco originale:

    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

Solo per divertimento, una soluzione che utilizza il framework jsr166y fork-join che dovrebbe in 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();

(Dichiarazione di non responsabilità: non compilata. Le specifiche non sono state finalizzate. Ecc.)

È improbabile che sia in JDK7 un po 'di inferenza di tipo e zucchero sintattico per rendere meno prolissa la chiamata withMapping:

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

0

Questa è una cosa così semplice da fare che non userei una libreria esterna (causerà una dipendenza nel tuo progetto che probabilmente non ti serve).

Abbiamo una classe di metodi statici creati appositamente per eseguire questo tipo di lavori. Poiché il codice per questo è così semplice, lasciamo che Hotspot esegua l'ottimizzazione per noi. Questo sembra essere un tema nel mio codice di recente: scrivi codice molto semplice (diretto) e lascia che Hotspot faccia la sua magia. Raramente abbiamo problemi di prestazioni con codice come questo: quando arriva una nuova versione della VM, ottieni tutti i vantaggi di velocità extra, ecc.

Per quanto mi piacciano le collezioni Jakarta, non supportano i Generics e usano 1.4 come LCD. Sono diffidente nei confronti delle raccolte Google perché sono elencate come livello di supporto Alpha!


-1

Volevo solo intervenire con una soluzione orientata agli oggetti del problema.

Se si modellano oggetti di dominio, la soluzione è negli oggetti di dominio. Il dominio qui è un elenco di numeri interi per i quali vogliamo valori stringa.

Il modo più semplice sarebbe non convertire affatto l'elenco.

Detto questo, per convertire senza convertire, cambia l'elenco originale di Integer in List of Value, dove Value ha un aspetto simile a questo ...

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

Sarà più veloce e occuperà meno memoria rispetto alla copia dell'elenco.

In bocca al lupo!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.