Konvertieren von List <Integer> in List <String>


105

Ich habe eine Liste von Ganzzahlen List<Integer>und möchte alle Ganzzahlobjekte in Zeichenfolgen konvertieren, um eine neue zu erhalten List<String>.

Natürlich könnte ich eine neue List<String>Liste erstellen und die Liste String.valueOf()für jede Ganzzahl durchlaufen , aber ich habe mich gefragt, ob es eine bessere (sprich: automatischere ) Möglichkeit gibt, dies zu tun.

Antworten:


77

Soweit ich weiß, ist Iterieren und Instanziieren der einzige Weg, dies zu tun. So etwas wie (für andere potenzielle Hilfe, da ich sicher bin, dass Sie wissen, wie das geht):

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

Wenn es einfach ist, nennt man das Schönheit.
Elbek

1
Das Originalplakat schien darauf hinzudeuten, dass er darüber nachgedacht hatte, diese Lösung jedoch für zu komplex oder zu langwierig hielt. Aber ich kann mir kaum vorstellen, was einfacher sein könnte. Ja, manchmal müssen Sie 3 oder 4 Codezeilen schreiben, um einen Job zu erledigen.
Jay

Aber das bindet dich an ArrayList. Kann dies mit derselben Implementierung wie in der ursprünglichen Liste durchgeführt werden?
Alianos

@ Andreas oldList.getClass (). NewInstance () wird tun
Lluis Martinez

96

Mit Google Collections von Guava-Project können Sie die transformMethode in der Lists- Klasse verwenden

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

Das Listzurückgegebene von transformist eine Ansicht der Hintergrundliste - die Transformation wird bei jedem Zugriff auf die transformierte Liste angewendet.

Beachten Sie, dass bei Anwendung auf null Functions.toStringFunction()ein a NullPointerExceptionausgelöst wird. Verwenden Sie es daher nur, wenn Sie sicher sind, dass Ihre Liste keine null enthält.


1
Es wird schön sein, wenn es neben Functions.toStringFunction ()
ThiamTeck

1
sauber aber vielleicht nicht so schnell .. 1 zusätzlicher Funktionsaufruf pro Wert?
h3xStream

3
HotSpot kann Funktionsaufrufe inline ausführen. Wenn es also genug aufgerufen wird, sollte es keinen Unterschied machen.
Ben Lings

3
Ich stimme dem nicht zu, weil es in der Tat eine Lösung ist. Aber Leute zu ermutigen, eine Bibliotheksabhängigkeit hinzuzufügen, um solch eine einfache Aufgabe zu lösen, ist für mich ein No-Go.
Estani

1
Gute Lösung, wenn Sie Guava bereits in unserer Lösung verwenden.
Dudinha-Dedalus

86

Lösung für Java 8. Etwas länger als die für Guava, aber zumindest müssen Sie keine Bibliothek installieren.

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
Während dies für das toStringBeispiel etwas länger ist , wird es für Konvertierungen, die von der Guava-Funktionsbibliothek nicht unterstützt werden, kürzer. Benutzerdefinierte Funktionen sind immer noch einfach, aber es ist deutlich mehr Code als dieser Java 8-Stream
Lightswitch05

40

Was Sie tun, ist in Ordnung, aber wenn Sie das Bedürfnis haben, Java-it-up zu verwenden, können Sie einen Transformer und die Collect-Methode von Apache Commons verwenden , z.

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

..und dann..

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

1
CollectionUtils.collect (collectionOfIntegers, neue org.apache.commons.collections.functors.StringValueTransformer ()); Aber StringValueTransformer verwendet den String.valueOf ...
Kannan Ekanath

5
Sofern keine neuen Arbeiten an Apache-Sammlungen durchgeführt wurden, werden keine Generika erstellt.
KitsuneYMG

1
Das ist wirklich Java-ing-it down. Dies ist kein idiomatisches Java, sondern eher eine funktionale Programmierung. Wenn wir in Java 8 Schließungen erhalten, können Sie es vielleicht als idiomatisches Java bezeichnen.
Christoffer Hammarström

Sie möchten auf jeden Fall Collections4 dafür verwenden (nicht die alten 3.x-Sammlungen), um Generika zu unterstützen: commons.apache.org/proper/commons-collections/apidocs/org/…
JRA_TLL

Definieren einer neuen Klasse, nur um "OOP oder idiomatischer" zu sein ... Ich sehe nicht, wie dies besser ist als die einfache Schleife für jede Schleife. Es erfordert mehr Code und verschiebt die Funktionalität (die durch anonyme Klassen gemindert werden könnte, aber immer noch). Dieser funktionale Stil wird erst dann nützlich, wenn es eine anständige Syntax gibt (dh Lambda-Ausdrücke seit Java 8), wie sie funktionale Sprachen seit Jahrzehnten bereitstellen.
TheOperator

9

Die Quelle für String.valueOf zeigt Folgendes:

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

Nicht, dass es viel ausmacht, aber ich würde toString verwenden.


9

Anstatt String.valueOf zu verwenden, würde ich .toString () verwenden. Es vermeidet einige der von @ johnathan.holland beschriebenen Auto-Boxen

Das Javadoc sagt, dass valueOf dasselbe zurückgibt wie Integer.toString ().

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

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

Wie Tom Hawtin in der "Gewinner" -Antwort hervorhob, kann man List <String> nicht instanziieren, da es sich nur um eine Schnittstelle handelt.
Stu Thompson

Heh das wusste ich. Ich habe nur den Code geschrieben, ohne ihn auszuprobieren. Ich werde es in meiner Antwort beheben.
ScArcher2

9

Hier ist eine Einzeiler-Lösung ohne Betrug mit einer Nicht-JDK-Bibliothek.

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

7

Eine andere Lösung mit Guava und Java 8

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

3

Nicht Java-Kern und nicht generisch, aber die beliebte Sammlungsbibliothek von Jakarta Commons bietet einige nützliche Abstraktionen für diese Art von Aufgabe. Schauen Sie sich insbesondere die Erfassungsmethoden an

CollectionUtils

Beachten Sie Folgendes, wenn Sie in Ihrem Projekt bereits Commons-Sammlungen verwenden.


4
Verwenden Sie niemals Apache-Sammlungen. Sie sind alt, veraltet, nicht typsicher und schlecht geschrieben.
KitsuneYMG

3

An die Leute, die in jsights Antwort über "Boxen" besorgt sind: Es gibt keine. String.valueOf(Object)wird hier verwendet und es wird nie ein Unboxing intdurchgeführt.

Ob Sie verwenden Integer.toString()oder String.valueOf(Object)davon abhängen, wie Sie mit möglichen Nullen umgehen möchten. Möchten Sie (wahrscheinlich) eine Ausnahme auslösen oder "null" Zeichenfolgen in Ihrer Liste haben (möglicherweise). Wenn erstere, möchten Sie einen NullPointerExceptionoder einen anderen Typ werfen ?

Ein kleiner Fehler in der Antwort von jsight Listist eine Schnittstelle, für die Sie den neuen Operator nicht verwenden können. Ich würde java.util.ArrayListin diesem Fall wahrscheinlich ein verwenden , zumal wir im Voraus wissen, wie lang die Liste wahrscheinlich ist.


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

2

@ Jonathan: Ich könnte mich irren, aber ich glaube, dass String.valueOf () in diesem Fall die Funktion String.valueOf (Object) aufruft, anstatt in String.valueOf (int) gepackt zu werden. String.valueOf (Object) gibt nur "null" zurück, wenn es null ist, oder ruft Object.toString () auf, wenn es nicht null ist, was kein Boxen beinhalten sollte (obwohl offensichtlich das Instanziieren neuer String-Objekte beteiligt ist).


2

Ich denke, die Verwendung von Object.toString () für einen anderen Zweck als das Debuggen ist wahrscheinlich eine wirklich schlechte Idee, obwohl in diesem Fall die beiden funktional gleichwertig sind (vorausgesetzt, die Liste enthält keine Nullen). Es steht Entwicklern frei, das Verhalten einer toString () -Methode ohne Vorwarnung zu ändern, einschließlich der toString () -Methoden aller Klassen in der Standardbibliothek.

Machen Sie sich nicht einmal Sorgen über die Leistungsprobleme, die durch den Box- / Unboxing-Prozess verursacht werden. Wenn die Leistung kritisch ist, verwenden Sie einfach ein Array. Wenn es wirklich kritisch ist, verwenden Sie kein Java. Der Versuch, die JVM auszutricksen, führt nur zu Herzschmerz.


2

Eine Antwort nur für Experten:

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

Dies funktioniert jedoch auf Kosten der Ineffizienz. Java-Strings sind zwei Bytes pro Zeichen, daher addiert das "," vier Byte Fixkosten pro Ganzzahl, bevor die Ganzzahl selbst gezählt wird ... unter anderem.
Robert Christian

Ich denke, der reguläre Ausdruck könnte eher ein Problem in Bezug auf die Effizienz des rohen CPU-Zyklus sein. In Bezug auf den Speicher denke ich, dass eine vernünftige Implementierung (unter der Annahme, dass die "Sun" -Unvernünftige Implementierung von String) dasselbe Backing-Array (von all) verwendet, also tatsächlich sehr speichereffizient ist, was für die langfristige Leistung wichtig wäre. Es sei denn, Sie möchten natürlich nur eines der Elemente behalten ...
Tom Hawtin - Tackline

2

Lambdaj ermöglicht dies auf sehr einfache und lesbare Weise. Angenommen, Sie haben eine Liste mit Ganzzahlen und möchten diese in die entsprechende Zeichenfolgendarstellung konvertieren. Dann könnten Sie so etwas schreiben.

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 wendet die Konvertierungsfunktion nur an, während Sie das Ergebnis iterieren.


1

Sie können den "Boxaufwand" nicht vermeiden; Javas generische Faux-Container können nur Objekte speichern, daher müssen Ihre Ints in Ganzzahlen eingeschlossen sein. Im Prinzip könnte es den Downcast von Object zu Integer vermeiden (da es sinnlos ist, weil Object sowohl für String.valueOf als auch für Object.toString gut genug ist), aber ich weiß nicht, ob der Compiler dafür klug genug ist. Die Konvertierung von String zu Object sollte mehr oder weniger ein No-Op sein, daher würde ich mich nicht darum kümmern.


Der Compiler ist dafür NICHT schlau genug. Wenn javac ausgeführt wird, werden tatsächlich alle generischen Typinformationen entfernt. Die zugrunde liegende Implementierung einer generischen Sammlung speichert IMMER Objektreferenzen. Sie können die <T> -Parametrisierung tatsächlich weglassen und einen "rohen" Typ erhalten. "Liste l = neue Liste ()" versus "Liste <String> l = neue Liste <String> ()". Dies bedeutet natürlich, dass "List <String> l = (List <String>) new List <Integer> ()" tatsächlich kompiliert und ausgeführt wird, aber offensichtlich sehr gefährlich ist.
Dave Dopson

1

Ich habe keine Lösung gesehen, die dem Prinzip der Raumkomplexität folgt. Wenn die Liste der Ganzzahlen eine große Anzahl von Elementen enthält, ist dies ein großes 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>.

Wir können den Iterator verwenden, um dasselbe zu erreichen.

    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

Verwenden von Streams: Wenn das Ergebnis eine Liste von Ganzzahlen ( List<Integer> result) ist, dann:

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

Eine der Möglichkeiten, es zu lösen. Hoffe das hilft.


1

Eine etwas präzisere Lösung mit der forEach-Methode in der ursprünglichen Liste:

    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

Nur zum Spaß eine Lösung mit dem Fors-Join-Framework jsr166y, das in JDK7 verwendet werden sollte.

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

(Haftungsausschluss: Nicht kompiliert. Spezifikation ist nicht finalisiert. Usw.)

In JDK7 ist es unwahrscheinlich, dass ein bisschen Typinferenz und syntaktischer Zucker vorhanden sind, um den withMapping-Aufruf weniger ausführlich zu machen:

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

0

Dies ist eine so grundlegende Aufgabe, dass ich keine externe Bibliothek verwenden würde (dies führt zu einer Abhängigkeit in Ihrem Projekt, die Sie wahrscheinlich nicht benötigen).

Wir haben eine Klasse statischer Methoden, die speziell für diese Art von Aufgaben entwickelt wurden. Da der Code dafür so einfach ist, lassen wir Hotspot die Optimierung für uns durchführen. Dies scheint in letzter Zeit ein Thema in meinem Code zu sein: Schreiben Sie sehr einfachen (unkomplizierten) Code und lassen Sie Hotspot seine Magie wirken. Wir haben selten Leistungsprobleme mit Code wie diesem - wenn eine neue VM-Version auf den Markt kommt, erhalten Sie alle zusätzlichen Geschwindigkeitsvorteile usw.

So sehr ich Jakarta-Kollektionen liebe, sie unterstützen keine Generika und verwenden 1.4 als LCD. Ich bin vorsichtig mit Google-Sammlungen, da diese als Alpha-Support-Level aufgeführt sind!


-1

Ich wollte mich nur auf eine objektorientierte Lösung des Problems einlassen.

Wenn Sie Domänenobjekte modellieren, liegt die Lösung in den Domänenobjekten. Die Domäne hier ist eine Liste von Ganzzahlen, für die Zeichenfolgenwerte erforderlich sind.

Am einfachsten wäre es, die Liste überhaupt nicht zu konvertieren.

Um jedoch ohne Konvertierung zu konvertieren, ändern Sie die ursprüngliche Liste von Integer in List of Value, wobei Value ungefähr so ​​aussieht ...

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

Dies ist schneller und beansprucht weniger Speicher als das Kopieren der Liste.

Viel Glück!

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.