Conversion de la liste <Integer> en liste <String>


105

J'ai une liste d'entiers List<Integer>et j'aimerais convertir tous les objets entiers en chaînes, finissant ainsi avec un nouveau List<String>.

Naturellement, je pourrais créer un nouveau List<String>et faire une boucle dans la liste en appelant String.valueOf()chaque entier, mais je me demandais s'il y avait une meilleure façon (lire: plus automatique ) de le faire?

Réponses:


77

Autant que je sache, itérer et instancier est le seul moyen de le faire. Quelque chose comme (pour d'autres, une aide potentielle, car je suis sûr que vous savez comment faire cela):

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

Quand c'est simple, cela s'appelle la beauté.
Elbek le

1
L'affiche originale semblait indiquer qu'il y avait pensé mais jugeait cette solution trop complexe ou fastidieuse. Mais j'ai du mal à imaginer ce qui pourrait être plus facile. Oui, parfois vous devez écrire 3 ou 4 lignes de code pour faire un travail.
Jay

Mais cela vous lie à ArrayList. Cela peut-il être fait en utilisant la même implémentation que la liste d'origine?
alianos-

@Andreas oldList.getClass (). NewInstance () fera l'affaire
Lluis Martinez

96

En utilisant Google Collections de Guava-Project , vous pouvez utiliser la transformméthode de la 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());

Le Listrenvoyé par transformest une vue sur la liste de sauvegarde - la transformation sera appliquée à chaque accès à la liste transformée.

Sachez que Functions.toStringFunction()cela lèvera un NullPointerExceptionlorsqu'il est appliqué à null, donc ne l'utilisez que si vous êtes sûr que votre liste ne contiendra pas null.


1
Ce sera bien s'il y a plus de fonctions prêtes à côté de Functions.toStringFunction ()
ThiamTeck

1
propre mais peut-être pas aussi rapide .. 1 appel de fonction supplémentaire par valeur?
h3xStream

3
HotSpot peut intégrer des appels de fonction - donc s'il est suffisamment appelé, cela ne devrait pas faire de différence.
Ben Lings

3
Je ne déconseille pas cela car c'est en effet une solution. Mais encourager les gens à ajouter une dépendance à une bibliothèque pour résoudre une tâche aussi simple est un jeu d'enfant pour moi.
estani

1
Belle solution si vous utilisez déjà Guava dans notre solution.
dudinha-dedalus

86

Solution pour Java 8. Un peu plus longue que celle de Guava, mais au moins vous n'avez pas besoin d'installer de bibliothèque.

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
Bien que ce soit un peu plus long pour l' toStringexemple, cela finit par être plus court pour les conversions non prises en charge par la bibliothèque de fonctions de Guava. Les fonctions personnalisées sont toujours faciles, mais c'est beaucoup plus de code que ce flux Java 8
lightswitch05

40

Ce que vous faites est bien, mais si vous ressentez le besoin de «Java-it-up», vous pouvez utiliser un Transformer et la méthode collect d' Apache Commons , par exemple:

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

..puis..

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

1
CollectionUtils.collect (collectionOfIntegers, nouveau org.apache.commons.collections.functors.StringValueTransformer ()); Mais, StringValueTransformer utilise le String.valueOf ...
Kannan Ekanath

5
À moins que de nouveaux travaux n'aient été effectués sur les collections Apache, ils ne font pas de génériques.
KitsuneYMG

1
C'est vraiment Java-ing-it down. Ce n'est pas du Java idiomatique, mais plutôt de la programmation fonctionnelle. Peut-être que lorsque nous obtenons des fermetures dans Java 8, pourriez-vous l'appeler Java idiomatique.
Christoffer Hammarström

Vous voulez certainement utiliser Collections4 pour cela ( et non les anciennes collections 3.x) pour le soutien des génériques: commons.apache.org/proper/commons-collections/apidocs/org/...
JRA_TLL

Définir une nouvelle classe juste pour être "plus POO ou idiomatique" ... Je ne vois pas en quoi c'est mieux que la simple boucle for-each. Cela nécessite plus de code et éloigne la fonctionnalité (ce qui pourrait être atténué par des classes anonymes, mais quand même). Ce style fonctionnel ne devient utile que lorsqu'il existe une syntaxe décente (c'est-à-dire des expressions lambda depuis Java 8), comme les langages fonctionnels le fournissent depuis des décennies.
TheOperator

9

La source de String.valueOf montre ceci:

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

Pas que cela compte beaucoup, mais j'utiliserais toString.


9

Au lieu d'utiliser String.valueOf, j'utiliserais .toString (); cela évite une partie de la boxe automatique décrite par @ johnathan.holland

Le javadoc dit que valueOf renvoie la même chose que Integer.toString ().

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

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

comme l'a souligné Tom Hawtin dans la réponse «gagnante», on ne peut pas instancier List <String> car il ne s'agit que d'une interface.
Stu Thompson le

Heh je le savais. J'ai juste écrit le code sans l'essayer. Je vais le réparer dans ma réponse.
ScArcher2

9

Voici une solution unique sans tricher avec une bibliothèque non-JDK.

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

7

Une autre solution utilisant Guava et Java 8

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

3

Pas le noyau Java, ni générique, mais la populaire bibliothèque de collections Jakarta commons a quelques abstractions utiles pour ce type de tâche. Plus précisément, jetez un œil aux méthodes de collecte sur

CollectionUtils

Quelque chose à considérer si vous utilisez déjà des collections communes dans votre projet.


4
N'utilisez jamais de collections Apache. Ils sont vieux, obsolètes, non sécurisés et mal écrits.
KitsuneYMG

3

Aux personnes concernées par la "boxe" dans la réponse de jsight: il n'y en a pas. String.valueOf(Object)est utilisé ici, et aucun déballage intn'est jamais effectué.

Le fait que vous utilisiez Integer.toString()ou String.valueOf(Object)dépend de la manière dont vous souhaitez gérer les valeurs nulles possibles. Voulez-vous lever une exception (probablement), ou avoir des chaînes «nulles» dans votre liste (peut-être). Si c'est le premier, voulez-vous lancer un NullPointerExceptionou un autre type?

De plus, un petit défaut dans la réponse de jsight: Listc'est une interface, vous ne pouvez pas utiliser le nouvel opérateur dessus. J'utiliserais probablement un java.util.ArrayListdans ce cas, d'autant plus que nous savons à l'avance combien de temps la liste est susceptible d'être.


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

2

@Jonathan: Je peux me tromper, mais je crois que String.valueOf () dans ce cas appellera la fonction String.valueOf (Object) plutôt que d'être encadré par String.valueOf (int). String.valueOf (Object) renvoie simplement "null" s'il est nul ou appelle Object.toString () s'il n'est pas nul, ce qui ne devrait pas impliquer de boxe (bien que l'instanciation de nouveaux objets chaîne soit évidemment impliquée).


2

Je pense que l'utilisation d'Object.toString () à des fins autres que le débogage est probablement une très mauvaise idée, même si dans ce cas, les deux sont fonctionnellement équivalents (en supposant que la liste n'a pas de valeurs nulles). Les développeurs sont libres de modifier le comportement de toute méthode toString () sans aucun avertissement, y compris les méthodes toString () de toutes les classes de la bibliothèque standard.

Ne vous inquiétez même pas des problèmes de performances causés par le processus de boxe / déballage. Si les performances sont essentielles, utilisez simplement une baie. Si c'est vraiment critique, n'utilisez pas Java. Essayer de déjouer la JVM ne conduira qu'à un chagrin d'amour.


2

Une réponse réservée aux experts:

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

Cela fonctionne mais au détriment de l'inefficacité. Les chaînes Java sont de deux octets par caractère, donc le "," ajoute un coût fixe de quatre octets par entier avant de compter l'entier lui-même .... entre autres.
Robert Christian

Je pense que l'expression régulière pourrait être plus un problème en termes d'efficacité du cycle CPU brut. En termes de mémoire, je suppose qu'une implémentation raisonnable (en supposant que l'implémentation "Sun" déraisonnable de String) partagera le même tableau de support (de all), donc sera en fait vraiment assez efficace en mémoire, ce qui serait important pour les performances à long terme. A moins que vous ne vouliez garder un seul des éléments bien sûr ...
Tom Hawtin - tackline

2

Lambdaj permet de le faire d'une manière très simple et lisible. Par exemple, en supposant que vous ayez une liste d'entiers et que vous vouliez les convertir dans la représentation String correspondante, vous pourriez écrire quelque chose comme ça;

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 applique la fonction de conversion uniquement pendant que vous itérez sur le résultat.


1

Vous ne pouvez pas éviter les "frais généraux de boxe"; Les faux conteneurs génériques de Java ne peuvent stocker que des objets, donc vos entiers doivent être encadrés dans des entiers. En principe, cela pourrait éviter le downcast d'Object en Integer (car c'est inutile, car Object est assez bon pour String.valueOf et Object.toString) mais je ne sais pas si le compilateur est assez intelligent pour le faire. La conversion de String en Object devrait être plus ou moins un no-op, donc je serais peu enclin à m'inquiéter à ce sujet.


le compilateur n'est PAS assez intelligent pour faire cela. Lorsque javac s'exécute, il supprime en fait toutes les informations de type générique. L'implémentation sous-jacente d'une collection générique stocke TOUJOURS les références d'objet. Vous pouvez en fait omettre la paramétrisation <T> et obtenir un type "brut". "List l = new List ()" versus "List <String> l = new List <String> ()". bien sûr, cela signifie que "List <String> l = (List <String>) new List <Integer> ()" sera en fait compilé et exécuté, mais est, évidemment, très dangereux.
Dave Dopson le

1

Je n'ai vu aucune solution qui suit le principe de la complexité spatiale. Si la liste d'entiers a un grand nombre d'éléments, c'est un gros problème.

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

Nous pouvons utiliser l'itérateur pour réaliser la même chose.

    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

Utilisation des flux: si le résultat est une liste d'entiers ( List<Integer> result), alors:

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

Une des façons de le résoudre. J'espère que cela t'aides.


1

Une solution légèrement plus concise en utilisant la méthode forEach sur la liste d'origine:

    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

Juste pour le plaisir, une solution utilisant le framework jsr166y fork-join qui devrait dans 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();

(Avertissement: non compilé. Les spécifications ne sont pas finalisées. Etc.)

Il est peu probable que JDK7 soit un peu d'inférence de type et de sucre syntaxique pour rendre l'appel withMapping moins verbeux:

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

0

C'est une chose tellement basique à faire que je n'utiliserais pas de bibliothèque externe (cela entraînerait une dépendance dans votre projet dont vous n'avez probablement pas besoin).

Nous avons une classe de méthodes statiques spécialement conçues pour faire ce genre de travail. Parce que le code est si simple, nous laissons Hotspot faire l'optimisation pour nous. Cela semble être un thème dans mon code récemment: écrivez du code très simple (simple) et laissez Hotspot faire sa magie. Nous avons rarement des problèmes de performances autour d'un code comme celui-ci - lorsqu'une nouvelle version de VM arrive, vous bénéficiez de tous les avantages de vitesse supplémentaires, etc.

Même si j'aime les collections Jakarta, elles ne prennent pas en charge les génériques et utilisent la version 1.4 comme écran LCD. Je me méfie des collections Google car elles sont répertoriées comme niveau de support Alpha!


-1

Je voulais juste intervenir avec une solution orientée objet au problème.

Si vous modélisez des objets de domaine, la solution se trouve dans les objets de domaine. Le domaine ici est une liste d'entiers pour lesquels nous voulons des valeurs de chaîne.

Le moyen le plus simple serait de ne pas convertir du tout la liste.

Cela étant dit, pour convertir sans convertir, changez la liste d'origine des entiers en Liste de valeurs, où Value ressemble à quelque chose comme ça ...

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

Ce sera plus rapide et occupera moins de mémoire que la copie de la liste.

Bonne chance!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.