Mengubah Daftar <Integer> menjadi Daftar <String>

105

Saya memiliki daftar integer, List<Integer>dan saya ingin mengubah semua objek integer menjadi Strings, sehingga menyelesaikannya dengan yang baru List<String>.

Secara alami, saya dapat membuat yang baru List<String>dan melalui daftar yang memanggil String.valueOf()setiap bilangan bulat, tetapi saya bertanya-tanya apakah ada cara yang lebih baik (baca: lebih otomatis ) untuk melakukannya?

ChrisThomas123
sumber

Jawaban:

77

Sejauh yang saya tahu, iterasi dan instantiate adalah satu-satunya cara untuk melakukan ini. Sesuatu seperti (untuk bantuan potensial orang lain, karena saya yakin Anda tahu bagaimana melakukan ini):

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
sumber
Jika itu sederhana, inilah yang disebut kecantikan.
Elbek
1
Poster asli sepertinya menunjukkan bahwa dia telah memikirkan hal ini tetapi menganggap solusi ini terlalu rumit atau membosankan. Tapi saya kesulitan membayangkan apa yang bisa lebih mudah. Ya, terkadang Anda harus menulis 3 atau 4 baris kode untuk menyelesaikan pekerjaan.
Jay
Tapi itu mengikat Anda ke ArrayList. Bisakah ini dilakukan dengan menggunakan implementasi yang sama seperti daftar aslinya?
alianos-
@Andreas oldList.getClass (). NewInstance () akan dilakukan
Lluis Martinez
96

Dengan menggunakan Koleksi Google dari Guava-Project , Anda dapat menggunakan transformmetode di kelas Daftar

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

Yang Listdikembalikan oleh transformadalah tampilan di daftar cadangan - transformasi akan diterapkan pada setiap akses ke daftar yang diubah.

Sadarilah bahwa Functions.toStringFunction()akan melempar NullPointerExceptionketika diterapkan ke null, jadi gunakan hanya jika Anda yakin daftar Anda tidak akan berisi null.

Ben Lings
sumber
1
Akan lebih baik jika ada lebih banyak fungsi siap di samping Functions.toStringFunction ()
ThiamTeck
1
bersih tapi mungkin tidak secepat .. 1 panggilan fungsi tambahan per nilai?
h3xStream
3
HotSpot dapat melakukan panggilan fungsi sebaris - jadi jika dipanggil cukup, seharusnya tidak membuat perbedaan.
Ben Lings
3
Saya tidak meremehkan ini karena ini memang solusi. Tapi mendorong orang untuk menambahkan ketergantungan perpustakaan untuk menyelesaikan tugas sederhana seperti itu tidak ada jalannya bagi saya.
estani
1
Solusi bagus jika Anda sudah menggunakan Jambu biji dalam solusi kami.
dudinha-dedalus
86

Solusi untuk Java 8. Sedikit lebih lama daripada Guava, tetapi setidaknya Anda tidak perlu menginstal perpustakaan.

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
sumber
1
Meskipun ini sedikit lebih lama untuk toStringcontoh, ini akhirnya menjadi lebih pendek untuk konversi yang tidak didukung oleh pustaka Fungsi Guava. Fungsi Kustom masih mudah, tetapi secara signifikan lebih banyak kode daripada aliran Java 8 ini
lightswitch05
40

Apa yang Anda lakukan baik-baik saja, tetapi jika Anda merasa perlu 'Java-it-up' Anda dapat menggunakan Transformer dan metode kumpulkan dari Apache Commons , misalnya:

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

..lalu..

CollectionUtils.collect(
   collectionOfIntegers, 
   new IntegerToStringTransformer(), 
   newCollectionOfStrings);
SCdF
sumber
1
CollectionUtils.collect (collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer ()); Tapi, StringValueTransformer menggunakan String.valueOf ...
Kannan Ekanath
5
Kecuali pekerjaan baru telah dilakukan pada koleksi apache, mereka tidak melakukan generik.
KitsuneYMG
1
Ini benar-benar Java-ing-it down. Ini bukan Java idiomatik, dan lebih seperti pemrograman fungsional. Mungkin ketika kita mendapatkan penutupan di Java 8 bisa Anda menyebutnya Java idiomatik.
Christoffer Hammarström
Anda pasti ingin menggunakan Collections4 untuk itu (bukan Koleksi 3.x lama) untuk dukungan generik: commons.apache.org/proper/commons-collections/apidocs/org/…
JRA_TLL
Mendefinisikan kelas baru hanya untuk menjadi "lebih OOP atau idiomatik" ... Saya tidak melihat bagaimana ini lebih baik daripada yang sederhana untuk setiap loop. Ini membutuhkan lebih banyak kode dan memindahkan fungsionalitasnya (yang dapat dikurangi oleh kelas anonim, tetapi tetap saja). Gaya fungsional ini hanya mulai menjadi berguna ketika ada sintaks yang layak (yaitu ekspresi lambda sejak Java 8), seperti bahasa fungsional telah menyediakannya selama beberapa dekade.
TheOperator
9

Sumber untuk String.valueOf menunjukkan ini:

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

Bukan berarti itu terlalu penting, tapi saya akan menggunakan toString.

Mike Polen
sumber
9

Alih-alih menggunakan String.valueOf, saya akan menggunakan .toString (); ini menghindari beberapa tinju otomatis yang dijelaskan oleh @ johnathan.holland

Javadoc mengatakan bahwa valueOf mengembalikan hal yang sama seperti Integer.toString ().

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

for (Integer myInt : oldList) { 
  newList.add(myInt.toString()); 
}
ScArcher2
sumber
seperti yang ditunjukkan oleh Tom Hawtin dalam jawaban 'menang', seseorang tidak dapat contoh List <String> karena ini hanya sebuah antarmuka.
Stu Thompson
Heh aku tahu itu. Hanya saya yang menulis kode tanpa mencobanya. Saya akan memperbaikinya dalam jawaban saya.
ScArcher2
9

Berikut adalah solusi satu baris tanpa mencontek dengan pustaka non-JDK.

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

Solusi lain menggunakan Jambu Biji dan Java 8

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

Bukan inti Java, dan bukan generik-ified, tetapi perpustakaan koleksi Jakarta commons yang populer memiliki beberapa abstraksi yang berguna untuk tugas semacam ini. Secara khusus, lihat metode pengumpulan di

CollectionUtils

Sesuatu yang perlu dipertimbangkan jika Anda sudah menggunakan koleksi milik bersama dalam proyek Anda.

serg10
sumber
4
Jangan pernah menggunakan Koleksi Apache. Mereka sudah tua, ketinggalan zaman, tidak aman untuk tipe, dan ditulis dengan buruk.
KitsuneYMG
3

Untuk orang-orang yang peduli tentang "tinju", jawaban jsight: tidak ada. String.valueOf(Object)digunakan di sini, dan tidak boleh membuka kotakint tidak pernah dilakukan.

Apakah Anda menggunakan Integer.toString()atau String.valueOf(Object)bergantung pada bagaimana Anda ingin menangani kemungkinan nulls. Apakah Anda ingin membuat pengecualian (mungkin), atau memiliki String "null" dalam daftar Anda (mungkin). Jika yang pertama, apakah Anda ingin melempar NullPointerExceptionatau jenis lainnya?

Juga, satu kelemahan kecil dalam respon jsight: Listadalah sebuah antarmuka, Anda tidak dapat menggunakan operator baru di dalamnya. Saya mungkin akan menggunakan a java.util.ArrayListdalam kasus ini, terutama karena kita tahu di depan berapa panjang daftar itu.

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

@ Jonathan: Saya bisa saja salah, tapi saya yakin bahwa String.valueOf () dalam hal ini akan memanggil fungsi String.valueOf (Object) daripada dimasukkan ke dalam kotak ke String.valueOf (int). String.valueOf (Object) hanya mengembalikan "null" jika null atau memanggil Object.toString () jika bukan null, yang seharusnya tidak melibatkan tinju (meskipun jelas membuat instance objek string baru terlibat).

jsight
sumber
2

Saya pikir menggunakan Object.toString () untuk tujuan apa pun selain debugging mungkin adalah ide yang sangat buruk, meskipun dalam hal ini keduanya secara fungsional setara (dengan asumsi daftar tidak memiliki nulls). Pengembang bebas mengubah perilaku metode toString () apa pun tanpa peringatan apa pun, termasuk metode toString () kelas apa pun di pustaka standar.

Jangan khawatir tentang masalah kinerja yang disebabkan oleh proses tinju / unboxing. Jika kinerja sangat penting, gunakan saja larik. Jika sangat penting, jangan gunakan Java. Mencoba mengakali JVM hanya akan menyebabkan sakit hati.

Programmer Penjahat
sumber
2

Jawaban hanya untuk para ahli:

    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 - tackline
sumber
Ini bekerja tetapi dengan mengorbankan inefisiensi. String Java adalah dua byte per karakter, jadi "," menambahkan biaya tetap empat byte per bilangan bulat sebelum menghitung bilangan bulat itu sendiri .... antara lain.
Robert Christian
Saya pikir regex mungkin lebih menjadi masalah dalam hal efisiensi siklus CPU mentah. Dalam hal memori, saya kira implementasi yang masuk akal (dengan asumsi implementasi "Sun" yang tidak masuk akal String) akan berbagi larik dukungan yang sama (dari all), jadi sebenarnya akan sangat efisien dalam memori, yang akan menjadi penting untuk kinerja jangka panjang. Kecuali jika Anda hanya ingin menyimpan salah satu elemen saja ...
Tom Hawtin - tackline
2

Lambdaj memungkinkan untuk melakukan itu dengan cara yang sangat sederhana dan mudah dibaca. Sebagai contoh, misalkan Anda memiliki daftar Integer dan Anda ingin mengubahnya dalam representasi String yang sesuai, Anda dapat menulis sesuatu seperti itu;

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 menerapkan fungsi konversi hanya saat Anda mengulangi hasilnya.

Mario Fusco
sumber
1

Anda tidak bisa menghindari "overhead tinju"; Container faux generic Java hanya dapat menyimpan Object, jadi int Anda harus dimasukkan ke dalam kotak Integer. Pada prinsipnya itu bisa menghindari downcast dari Object ke Integer (karena tidak ada gunanya, karena Object cukup baik untuk String.valueOf dan Object.toString) tetapi saya tidak tahu apakah compiler cukup pintar untuk melakukan itu. Konversi dari String ke Object harus kurang lebih tidak ada operasi, jadi saya akan enggan untuk khawatir tentang itu.

DrPizza
sumber
kompilator TIDAK cukup pintar untuk melakukan itu. Saat dijalankan, javac menghapus semua informasi jenis generik. Implementasi yang mendasari koleksi generik SELALU menyimpan referensi Objek. Anda sebenarnya dapat menghilangkan parameterisasi <T> dan mendapatkan tipe "mentah". "Daftar l = Daftar baru ()" versus "Daftar <String> l = Daftar baru <String> ()". tentu saja, ini berarti bahwa "List <String> l = (List <String>) new List <Integer> ()" akan benar-benar dikompilasi dan dijalankan, tetapi jelas sangat berbahaya.
Dave Dopson
1

Saya tidak melihat ada solusi yang mengikuti prinsip kompleksitas ruang. Jika daftar bilangan bulat memiliki jumlah elemen yang besar maka itu masalah besar.

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

Kita bisa menggunakan iterator untuk mencapai hal yang sama.

    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
sumber
1

Menggunakan Streams: Jika katakanlah hasilnya adalah daftar integers ( List<Integer> result) maka:

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

Salah satu cara untuk mengatasinya. Semoga ini membantu.

Shirish Singh
sumber
1

Solusi yang sedikit lebih ringkas menggunakan metode forEach di daftar asli:

    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
sumber
0

Hanya untuk bersenang-senang, solusi menggunakan kerangka kerja jsr166y fork-join yang seharusnya ada di 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();

(Penafian: Tidak dikompilasi. Spesifikasi belum diselesaikan. Dll)

Tidak mungkin berada di JDK7 adalah sedikit jenis inferensi dan gula sintaksis untuk membuatnya dengan Pemetaan panggilan kurang verbose:

    .withMapping(#(Integer i) String.valueOf(i))
Tom Hawtin - tackline
sumber
0

Ini adalah hal mendasar yang harus dilakukan, saya tidak akan menggunakan pustaka eksternal (ini akan menyebabkan ketergantungan dalam proyek Anda yang mungkin tidak Anda perlukan).

Kami memiliki kelas metode statis yang dibuat khusus untuk melakukan pekerjaan semacam ini. Karena kode untuk ini sangat sederhana, kami membiarkan Hotspot melakukan optimasi untuk kami. Ini tampaknya menjadi tema dalam kode saya baru-baru ini: tulis kode yang sangat sederhana (langsung) dan biarkan Hotspot melakukan keajaibannya. Kami jarang memiliki masalah kinerja di sekitar kode seperti ini - ketika versi VM baru hadir, Anda mendapatkan semua manfaat kecepatan ekstra, dll.

Meski saya suka koleksi Jakarta, mereka tidak mendukung Generik dan menggunakan 1.4 sebagai LCD. Saya waspada terhadap Koleksi Google karena mereka terdaftar sebagai tingkat dukungan Alfa!


sumber
-1

Saya hanya ingin berpadu dengan solusi berorientasi objek untuk masalah ini.

Jika Anda memodelkan objek domain, maka solusinya ada di objek domain. Domain di sini adalah Daftar bilangan bulat yang kita ingin nilai stringnya.

Cara termudah adalah dengan tidak mengubah daftar sama sekali.

Karena itu, untuk mengonversi tanpa mengonversi, ubah daftar asli Integer ke Daftar Nilai, di mana Nilai terlihat seperti ini ...

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

Ini akan lebih cepat dan memakan lebih sedikit memori daripada menyalin Daftar.

Semoga berhasil!

Rodney P. Barbati
sumber