Konwertowanie listy <Integer> na List <String>

105

Mam listę liczb całkowitych List<Integer>i chciałbym przekonwertować wszystkie obiekty typu integer na ciągi, kończąc w ten sposób na nowym List<String>.

Oczywiście mógłbym utworzyć nową List<String>i zapętlić listę wywołującą String.valueOf()każdą liczbę całkowitą, ale zastanawiałem się, czy istnieje lepszy (czytaj: bardziej automatyczny ) sposób zrobienia tego?

ChrisThomas123
źródło

Odpowiedzi:

77

O ile wiem, iteracja i tworzenie instancji to jedyny sposób, aby to zrobić. Coś takiego (dla innych potencjalnej pomocy, ponieważ jestem pewien, że wiesz, jak to zrobić):

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)); 
}
jsight
źródło
Kiedy jest proste, nazywa się to pięknem.
Elbek,
1
Oryginalny plakat zdawał się wskazywać, że myślał o tym, ale uważał to rozwiązanie za zbyt skomplikowane lub nużące. Ale trudno mi sobie wyobrazić, co mogłoby być łatwiejsze. Tak, czasami trzeba napisać 3 lub 4 linie kodu, aby wykonać zadanie.
Jay
Ale to wiąże cię z ArrayList. Czy można to zrobić za pomocą tej samej implementacji, co oryginalna lista?
alianos-
@Andreas oldList.getClass (). NewInstance () zrobi
Lluis Martinez
96

Korzystając z Google Collections z Guava-Project , można użyć transformmetody z klasy 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());

ListZwrócony przez transformto widok na liście podkładowej - transformacja będzie stosowana na każdym wejściu do przekształconej listy.

Należy pamiętać, że po zastosowaniu do wartości null Functions.toStringFunction()zostanie wyrzucony a NullPointerException, więc używaj go tylko wtedy, gdy masz pewność, że lista nie będzie zawierać wartości null.

Ben Lings
źródło
1
Będzie miło, jeśli będzie więcej gotowych funkcji obok Functions.toStringFunction ()
ThiamTeck
1
czysty, ale może nie tak szybki ... 1 dodatkowe wywołanie funkcji na wartość?
h3xStream
3
HotSpot może wbudować wywołania funkcji - więc jeśli jest wystarczająco wywołany, nie powinno to robić różnicy.
Ben Lings,
3
Nie odrzucam tego, ponieważ jest to rzeczywiście rozwiązanie. Ale zachęcanie ludzi do dodawania zależności od biblioteki w celu rozwiązania tak prostego zadania jest dla mnie nie do przyjęcia.
estani
1
Miłe rozwiązanie, jeśli już używasz guawy w naszym rozwiązaniu.
dudinha-dedalus
86

Rozwiązanie dla Java 8. Trochę dłuższe niż Guava, ale przynajmniej nie musisz instalować biblioteki.

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());
Trejkaz
źródło
1
Chociaż w tym przykładzie jest to trochę dłużej toString, kończy się to krócej w przypadku konwersji nieobsługiwanych przez bibliotekę funkcji Guava. Funkcje niestandardowe są nadal łatwe, ale zawiera znacznie więcej kodu niż ten strumień Java 8
lightswitch05
40

To, co robisz, jest w porządku, ale jeśli czujesz potrzebę „Java-it-up”, możesz użyć Transformera i metody zbierania z Apache Commons , np .:

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

..i wtedy..

CollectionUtils.collect(
   collectionOfIntegers, 
   new IntegerToStringTransformer(), 
   newCollectionOfStrings);
SCdF
źródło
1
CollectionUtils.collect (collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer ()); Ale StringValueTransformer używa String.valueOf ...
Kannan Ekanath
5
O ile nie wykonano nowej pracy na kolekcjach Apache, nie robią one typów ogólnych.
KitsuneYMG
1
To naprawdę działa Java-it down. To nie jest idiomatyczna Java, a bardziej przypomina programowanie funkcjonalne. Może kiedy otrzymamy zamknięcia w Javie 8, możesz nazwać to idiomatyczną Javą.
Christoffer Hammarström
Zdecydowanie chcesz użyć do tego Kolekcji4 (nie starej kolekcji 3.x) do obsługi leków generycznych: commons.apache.org/proper/commons-collections/apidocs/org/…
JRA_TLL
Definiowanie nowej klasy tylko po to, aby była „bardziej OOP lub idiomatyczna” ... Nie widzę, jak to jest lepsze niż prosta pętla for-each. Wymaga więcej kodu i przenosi funkcjonalność (co może być złagodzone przez klasy anonimowe, ale nadal). Ten styl funkcjonalny zaczyna być przydatny tylko wtedy, gdy istnieje przyzwoita składnia (tj. Wyrażenia lambda od czasu Java 8), tak jak języki funkcyjne zapewniały go od dziesięcioleci.
TheOperator
9

Źródło String.valueOf pokazuje to:

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

Nie żeby to miało duże znaczenie, ale użyłbym toString.

Mike Polen
źródło
9

Zamiast używać String.valueOf użyłbym .toString (); pozwala uniknąć niektórych automatycznych boksów opisanych przez @ johnathan.holland

Javadoc mówi, że valueOf zwraca to samo co Integer.toString ().

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

for (Integer myInt : oldList) { 
  newList.add(myInt.toString()); 
}
ScArcher2
źródło
jak wskazał Tom Hawtin w odpowiedzi „wygrywającej”, nie można przykładać List <String>, ponieważ jest to tylko interfejs.
Stu Thompson,
Heh, wiedziałem o tym. Po prostu napisałem kod bez próbowania. Naprawię to w mojej odpowiedzi.
ScArcher2
9

Oto jedno-liniowe rozwiązanie bez oszukiwania z biblioteką inną niż JDK.

List<String> strings = Arrays.asList(list.toString().replaceAll("\\[(.*)\\]", "$1").split(", "));
Garrett Hall
źródło
7

Kolejne rozwiązanie wykorzystujące Guava i Java 8

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<String> strings = Lists.transform(numbers, number -> String.valueOf(number));
sandrozbinden
źródło
3

Nie jest to rdzeń Java i nie jest to ogólna wersja, ale popularna biblioteka Jakarta commons zawiera kilka przydatnych abstrakcji dla tego rodzaju zadań. W szczególności zapoznaj się z metodami zbierania danych

CollectionUtils

Coś do rozważenia, jeśli już korzystasz z kolekcji Common w swoim projekcie.

serg10
źródło
4
Nigdy nie używaj kolekcji Apache. Są stare, nieaktualne, nie są bezpieczne dla typów i są źle napisane.
KitsuneYMG
3

Osobom zaniepokojonym „boksem” jsight odpowiada: nie ma. String.valueOf(Object)jest tutaj używany i nigdy nie intjest wykonywane rozpakowywanie .

Czy używasz, Integer.toString()czy String.valueOf(Object)zależy od tego, jak chcesz obsłużyć możliwe wartości null. Chcesz zgłosić wyjątek (prawdopodobnie), czy mieć na liście ciągi „zerowe” (być może). Jeśli pierwszy, chcesz wrzucić NullPointerExceptionjakiś inny typ?

Jedna mała wada w odpowiedzi jsight: Listjest to interfejs, nie możesz na nim użyć nowego operatora. java.util.ArrayListW tym przypadku prawdopodobnie użyłbym litery a, zwłaszcza, że ​​z góry wiemy, jak długa może być lista.

erickson
źródło
3
List<String> stringList = integerList.stream().map((Object s)->String.valueOf(s)).collect(Collectors.toList())
Mahesh Yadav
źródło
2

@Jonathan: Mogę się mylić, ale uważam, że String.valueOf () w tym przypadku wywoła funkcję String.valueOf (Object), zamiast zostać zapakowaną w String.valueOf (int). String.valueOf (Object) po prostu zwraca „null”, jeśli ma wartość null, lub wywołuje Object.toString (), jeśli nie jest równe null, co nie powinno obejmować boksu (chociaż oczywiście wymaga to tworzenia nowych obiektów łańcuchowych).

jsight
źródło
2

Myślę, że użycie Object.toString () w jakimkolwiek celu innym niż debugowanie jest prawdopodobnie naprawdę złym pomysłem, nawet jeśli w tym przypadku oba są funkcjonalnie równoważne (zakładając, że lista nie ma wartości null). Deweloperzy mogą dowolnie zmieniać zachowanie dowolnej metody toString () bez żadnego ostrzeżenia, w tym metod toString () dowolnych klas w bibliotece standardowej.

Nie martw się nawet o problemy z wydajnością spowodowane procesem pakowania / rozpakowywania. Jeśli wydajność jest krytyczna, po prostu użyj macierzy. Jeśli jest to naprawdę ważne, nie używaj Javy. Próba przechytrzenia JVM doprowadzi tylko do bólu serca.

Wyjęty spod prawa programista
źródło
2

Odpowiedź tylko dla ekspertów:

    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);
Tom Hawtin - haczyk
źródło
To działa, ale kosztem nieefektywności. Ciągi Java to dwa bajty na znak, więc znak „,” dodaje czterobajtowy koszt stały na liczbę całkowitą przed zliczeniem samej liczby całkowitej ... między innymi.
Robert Christian
Myślę, że wyrażenie regularne może stanowić większy problem pod względem wydajności cyklu procesora. Jeśli chodzi o pamięć, myślę, że rozsądna implementacja (zakładając nierozsądną implementację "Sun" String) będzie korzystać z tej samej tablicy bazowej (od all), więc faktycznie będzie całkiem wydajna pod względem pamięci, co byłoby ważne dla długoterminowej wydajności. Chyba że chcesz zachować tylko jeden z elementów, oczywiście ...
Tom Hawtin - tackline
2

Lambdaj pozwala to zrobić w bardzo prosty i czytelny sposób. Na przykład, zakładając, że masz listę liczb całkowitych i chcesz przekonwertować je na odpowiednią reprezentację typu String, możesz napisać coś takiego;

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 stosuje funkcję konwersji tylko podczas iteracji wyniku.

Mario Fusco
źródło
1

Nie da się uniknąć „kosztu boksu”; Faux generyczne kontenery Java mogą przechowywać tylko obiekty, więc twoje int muszą być umieszczone w pudełkach w liczbach całkowitych. W zasadzie mógłby uniknąć downcast z Object do Integer (ponieważ jest to bezcelowe, ponieważ Object jest wystarczająco dobry zarówno dla String.valueOf, jak i Object.toString), ale nie wiem, czy kompilator jest wystarczająco inteligentny, aby to zrobić. Konwersja ze String na Object powinna być mniej więcej bezczynna, więc nie byłbym skłonny się tym martwić.

DrPizza
źródło
kompilator NIE jest wystarczająco inteligentny, aby to zrobić. Po uruchomieniu javac w rzeczywistości usuwa wszystkie informacje o typach ogólnych. Podstawowa implementacja kolekcji generycznej ZAWSZE przechowuje odwołania do obiektów. W rzeczywistości można pominąć parametryzację <T> i uzyskać „surowy” typ. "List l = new List ()" kontra "List <String> l = new List <String> ()". oczywiście oznacza to, że "List <String> l = (List <String>) new List <Integer> ()" faktycznie skompiluje się i uruchomi, ale jest oczywiście bardzo niebezpieczne.
Dave Dopson
1

Nie widziałem rozwiązania, które byłoby zgodne z zasadą złożoności przestrzeni. Jeśli lista liczb całkowitych ma dużą liczbę elementów, to jest to duży 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>.

Możemy użyć iteratora, aby osiągnąć to samo.

    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();
    }
nagendra547
źródło
1

Korzystanie ze strumieni: jeśli powiedzmy, że wynikiem jest lista liczb całkowitych ( List<Integer> result), to:

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

Jeden ze sposobów rozwiązania tego problemu. Mam nadzieję że to pomoże.

Shirish Singh
źródło
1

Nieco bardziej zwięzłe rozwiązanie wykorzystujące metodę forEach z oryginalnej listy:

    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)));
Solubris
źródło
0

Dla zabawy, rozwiązanie wykorzystujące framework jsr166y fork-join, które powinno być w 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();

(Zastrzeżenie: nieskompilowane. Specyfikacja nie jest sfinalizowana. Itp.)

Jest mało prawdopodobne, aby w JDK7 było trochę wnioskowania o typie i cukru składniowego, aby uczynić to z wywołaniem Mapping mniej szczegółowym:

    .withMapping(#(Integer i) String.valueOf(i))
Tom Hawtin - haczyk
źródło
0

To jest tak podstawowa rzecz do zrobienia, że ​​nie użyłbym zewnętrznej biblioteki (spowoduje to zależność w twoim projekcie, której prawdopodobnie nie potrzebujesz).

Mamy klasę metod statycznych stworzonych specjalnie do tego typu zadań. Ponieważ kod jest tak prosty, pozwalamy Hotspotowi przeprowadzić optymalizację za nas. Wydaje się, że jest to ostatnio motyw w moim kodzie: pisz bardzo prosty (bezpośredni) kod i pozwól Hotspotowi wykonać swoją magię. Rzadko mamy problemy z wydajnością związane z takim kodem - gdy pojawi się nowa wersja maszyny wirtualnej, uzyskasz wszystkie dodatkowe korzyści związane z prędkością itp.

Chociaż uwielbiam kolekcje Dżakarty, nie obsługują one Generics i używają 1.4 jako wyświetlacza LCD. Obawiam się kolekcji Google, ponieważ są one wymienione jako poziom pomocy Alfa!


źródło
-1

Chciałem tylko zająć się rozwiązaniem problemu zorientowanym obiektowo.

Jeśli modelujesz obiekty domeny, rozwiązanie znajduje się w obiektach domeny. Domena tutaj to lista liczb całkowitych, dla których chcemy mieć wartości łańcuchowe.

Najłatwiej byłoby w ogóle nie konwertować listy.

Biorąc to pod uwagę, aby przekonwertować bez konwersji, zmień oryginalną listę liczb całkowitych na listę wartości, gdzie wartość wygląda mniej więcej tak ...

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

Będzie to szybsze i zajmie mniej pamięci niż kopiowanie listy.

Powodzenia!

Rodney P. Barbati
źródło