Iterasi terakhir dari peningkatan untuk loop di java

140

Apakah ada cara untuk menentukan apakah loop itu berulang untuk terakhir kalinya. Kode saya terlihat seperti ini:

int[] array = {1, 2, 3...};
StringBuilder builder = new StringBuilder();

for(int i : array)
{
    builder.append("" + i);
    if(!lastiteration)
        builder.append(",");
}

Sekarang masalahnya adalah saya tidak ingin menambahkan koma di iterasi terakhir. Sekarang apakah ada cara untuk menentukan apakah itu adalah iterasi terakhir atau saya terjebak dengan for loop atau menggunakan penghitung eksternal untuk melacak.

Berbelas kasihan
sumber
1
Ya! Itu lucu, saya hanya ingin menanyakan pertanyaan yang sama persis!
PhiLho
Jenis pertanyaan yang sama kembali (dan akan demikian). Sekarang mengapa Anda ingin membuat loop jika satu elemen membutuhkan perlakuan yang berbeda? stackoverflow.com/questions/156650/…
xtofl
Karena Anda memiliki larik tetap mengapa menggunakan yang disempurnakan untuk? for (int i = 0; i <array.length; i ++ if (i <array.lenth) ,,,
AnthonyJClink

Jawaban:

223

Alternatif lain adalah menambahkan koma sebelum Anda menambahkan saya, hanya saja tidak pada iterasi pertama . (Tolong, jangan gunakan "" + i- Anda tidak benar-benar ingin penggabungan di sini, dan StringBuilder memiliki kelebihan (int) append yang sangat baik.)

int[] array = {1, 2, 3...};
StringBuilder builder = new StringBuilder();

for (int i : array) {
    if (builder.length() != 0) {
        builder.append(",");
    }
    builder.append(i);
}

Yang menyenangkan tentang ini adalah bahwa itu akan bekerja dengan apa pun Iterable- Anda tidak selalu dapat mengindeks hal-hal. ("Tambahkan koma dan kemudian hapus di bagian akhir" adalah saran yang bagus ketika Anda benar-benar menggunakan StringBuilder - tetapi itu tidak berfungsi untuk hal-hal seperti menulis ke stream. Ini mungkin merupakan pendekatan terbaik untuk masalah yang tepat ini. )

Jon Skeet
sumber
2
Pola yang bagus, tetapi builder.length ()! = 0 rapuh - bayangkan sesuatu ditambahkan (kondisional!) Ke buffer sebelum loop Anda. Sebagai gantinya, gunakan isFirstbendera boolean. Bonus: lebih cepat juga.
Jason Cohen
2
@Jason: Saya tentu saja menggunakan pola "isFirst" di mana pembangun tidak akan kosong pada iterasi pertama. Namun, ketika itu tidak diperlukan, itu menambah jumlah mengasapi untuk implementasi dalam pengalaman saya.
Jon Skeet
3
@Liverpool (dll): 1000 cek yang tidak perlu sangat, sangat tidak mungkin memiliki dampak signifikan pada kinerja. Saya mungkin sama-sama menunjukkan bahwa karakter tambahan oleh solusi Dinah yang ditambahkan mungkin menyebabkan StringBuilder harus meluas, menggandakan ukuran string terakhir dengan (lanjutan)
Jon Skeet
2
ruang buffer yang tidak perlu. Namun, yang penting adalah keterbacaan. Saya kebetulan menemukan versi saya lebih mudah dibaca daripada Dinah. Jika Anda merasakan hal sebaliknya, itu baik-baik saja. Saya hanya akan mempertimbangkan dampak kinerja dari "seandainya" yang tidak perlu setelah menemukan kemacetan.
Jon Skeet
3
Selain itu, solusi saya lebih umum berlaku, karena hanya bergantung pada kemampuan untuk menulis. Anda dapat mengambil solusi saya dan mengubahnya untuk menulis ke aliran dll - di mana Anda mungkin tidak dapat mengambil kembali data yang tidak perlu sesudahnya. Saya suka pola yang umumnya berlaku.
Jon Skeet
145

Cara lain untuk melakukan ini:

String delim = "";
for (int i : ints) {
    sb.append(delim).append(i);
    delim = ",";
}

Pembaruan: Untuk Java 8, Anda sekarang memiliki Kolektor

toolkit
sumber
1
Harus menghapus jawaban saya yang serupa - itu akan mengajari saya untuk tidak mengirim sebelum melihat dengan lebih hati-hati pada jawaban lain.
Michael Burr
1
Perhatikan juga bahwa ini menangani masalah potensial yang Robert Paulson sebutkan di utas komentar lain - teknik ini tidak bergantung pada StringBuilder yang kosong ketika nilai array ditambahkan.
Michael Burr
1
Meskipun kodenya lebih apik / rapi / asyik, namun kurang jelas dibandingkan versi if. Selalu gunakan versi yang lebih jelas / mudah dibaca.
Bill K
8
@ Bill: Itu tidak bagus sebagai aturan yang keras dan cepat; ada kalanya solusi 'pintar' adalah solusi "lebih benar". Lebih penting lagi, apakah Anda benar-benar versi ini sulit dibaca? Either way seorang pengelola perlu melangkah melewatinya - saya tidak berpikir perbedaannya signifikan.
Greg Case
Ini berfungsi meskipun Anda menulis ke sumber daya lain seperti panggilan tulis filesystem. Ide bagus.
Tuxman
39

Mungkin lebih mudah untuk selalu menambahkan. Dan kemudian, ketika Anda selesai dengan loop Anda, hapus saja karakter terakhir. Ton kondisional kurang seperti itu juga.

Anda dapat menggunakan StringBuilder's deleteCharAt(int index)dengan indeks makhluklength() - 1

Dina
sumber
5
solusi paling efisien untuk masalah ini. +1.
Merah asli.
9
Mungkin itu saya, tapi saya benar-benar tidak suka fakta bahwa Anda menghapus sesuatu yang baru saja Anda tambahkan ...
Fortega
2
Fortega: Saya tidak suka memeriksa setiap waktu untuk sesuatu yang akan saya lakukan 99% dari waktu. Tampaknya lebih logis (dan ini IS lebih cepat) untuk menerapkan solusi Dinah atau sblundy.
Kerbau
1
Solusi paling elegan menurut saya. Terima kasih!
Charles Morin
32

Mungkin Anda menggunakan alat yang salah untuk Ayub.

Ini lebih manual daripada apa yang Anda lakukan tetapi dengan cara yang lebih elegan jika tidak sedikit "jadul"

 StringBuffer buffer = new StringBuffer();
 Iterator iter = s.iterator();
 while (iter.hasNext()) {
      buffer.append(iter.next());
      if (iter.hasNext()) {
            buffer.append(delimiter);
      }
 }
Omar Kooheji
sumber
15

Ini hampir merupakan pengulangan dari pertanyaan StackOverflow ini . Yang Anda inginkan adalah StringUtils , dan untuk memanggil metode join .

StringUtils.join(strArr, ',');
Phil H
sumber
14

Solusi lain (mungkin yang paling efisien)

    int[] array = {1, 2, 3};
    StringBuilder builder = new StringBuilder();

    if (array.length != 0) {
        builder.append(array[0]);
        for (int i = 1; i < array.length; i++ )
        {
            builder.append(",");
            builder.append(array[i]);
        }
    }
bruno conde
sumber
Ini adalah solusi alami kecuali bahwa itu tergantung pada array yang tidak kosong.
orcmid
5
@orcmid: Jika array kosong, maka ini masih memberikan output yang benar - string kosong. Saya tidak yakin apa maksud Anda.
Eddie
7

tetap sederhana dan gunakan standar untuk loop:

for(int i = 0 ; i < array.length ; i ++ ){
    builder.append(array[i]);
    if( i != array.length - 1 ){
        builder.append(',');
    }
}

atau cukup gunakan apache commons-lang StringUtils.join ()

Gareth Davis
sumber
6

Loop eksplisit selalu bekerja lebih baik daripada yang implisit.

builder.append( "" + array[0] );
for( int i = 1; i != array.length; i += 1 ) {
   builder.append( ", " + array[i] );
}

Anda harus membungkus semuanya dalam if-statement untuk berjaga-jaga jika Anda berurusan dengan array panjang nol.

S.Lott
sumber
Saya suka pendekatan ini karena tidak perlu melakukan tes setiap iterasi. Satu-satunya hal yang akan saya tambahkan adalah bahwa builder dapat menambahkan bilangan bulat secara langsung sehingga tidak perlu menggunakan "" + int, cukup tambahkan (array [0]).
Josh
Anda perlu satu lagi jika di sekitar seluruh lot untuk memastikan bahwa array memiliki setidaknya satu elemen.
Tom Leys
2
mengapa semua orang tetap menggunakan contoh dengan string concatenation DI DALAM append? "," + x mengkompilasi ke dalam StringBuilder baru (",") .append (x) .toString () ...
John Gardner
@ Josh dan @ John: Hanya mengikuti contoh n00b. Tidak ingin memperkenalkan terlalu banyak hal sekaligus. Poin Anda sangat bagus.
S.Lott
@ Tom: Benar. Saya pikir saya sudah mengatakan itu. Tidak perlu diedit.
S.Lott
4

Jika Anda mengonversinya ke loop indeks klasik, ya.

Atau Anda bisa menghapus koma terakhir setelah selesai. Seperti itu:

int[] array = {1, 2, 3...};
StringBuilder

builder = new StringBuilder();

for(int i : array)
{
    builder.append(i + ",");
}

if(builder.charAt((builder.length() - 1) == ','))
    builder.deleteCharAt(builder.length() - 1);

Saya, saya hanya menggunakan StringUtils.join()dari commons-lang .

merah muda
sumber
3

Anda membutuhkan Pemisah Kelas .

Separator s = new Separator(", ");
for(int i : array)
{
     builder.append(s).append(i);
}

Implementasi kelas Separatorlurus ke depan. Itu membungkus string yang dikembalikan pada setiap panggilan toString()kecuali untuk panggilan pertama, yang mengembalikan string kosong.

akuhn
sumber
3

Berdasarkan java.util.AbstractCollection.toString (), ia keluar lebih awal untuk menghindari pembatas.

StringBuffer buffer = new StringBuffer();
Iterator iter = s.iterator();
for (;;) {
  buffer.append(iter.next());
  if (! iter.hasNext())
    break;
  buffer.append(delimiter);
}

Ini efisien dan elegan, tetapi tidak terbukti dengan sendirinya seperti beberapa jawaban lainnya.

13rn
sumber
Anda lupa menyertakan pengembalian awal saat (! i.hasNext()), yang merupakan bagian penting dari kekokohan pendekatan keseluruhan. (Solusi lain di sini menangani koleksi kosong dengan anggun, jadi milik Anda juga harus :) :)
Mike Clark
3

Ini solusinya:

int[] array = {1, 2, 3...};
StringBuilder builder = new StringBuilder();
bool firstiteration=true;

for(int i : array)
{
    if(!firstiteration)
        builder.append(",");

    builder.append("" + i);
    firstiteration=false;
}

Cari iterasi pertama :)  

FallenAvatar
sumber
3

Seperti yang disebutkan dalam toolkit, di Java 8 kami sekarang memiliki Kolektor . Seperti apa kode ini nantinya:

String joined = array.stream().map(Object::toString).collect(Collectors.joining(", "));

Saya pikir itu tepat apa yang Anda cari, dan itu pola yang bisa Anda gunakan untuk banyak hal lainnya.

b1tw153
sumber
1

Namun pilihan lain.

StringBuilder builder = new StringBuilder();
for(int i : array)
    builder.append(',').append(i);
String text = builder.toString();
if (text.startsWith(",")) text=text.substring(1);
Peter Lawrey
sumber
Saya membuat variasi pada pertanyaan lain
13ren
1

Banyak solusi yang dijelaskan di sini sedikit berlebihan, IMHO, terutama yang bergantung pada perpustakaan eksternal. Ada ungkapan bersih dan jelas yang bagus untuk mencapai daftar yang dipisahkan koma yang selalu saya gunakan. Itu bergantung pada operator bersyarat (?):

Sunting : Solusi asli yang benar, tetapi tidak optimal menurut komentar. Mencoba yang kedua kalinya:

    int[] array = {1, 2, 3};
    StringBuilder builder = new StringBuilder();
    for (int i = 0 ;  i < array.length; i++)
           builder.append(i == 0 ? "" : ",").append(array[i]); 

Ini dia, dalam 4 baris kode termasuk deklarasi array dan StringBuilder.

Julien Chastang
sumber
Tapi Anda membuat StringBuilder baru di setiap iterasi (terima kasih kepada operator +).
Michael Myers
Ya, jika Anda melihat bytecode, Anda benar. Saya tidak percaya pertanyaan sederhana seperti itu bisa sangat rumit. Saya ingin tahu apakah kompiler dapat mengoptimalkan di sini.
Julien Chastang
Disediakan 2, semoga solusi yang lebih baik.
Julien Chastang
1

Inilah patokan SSCCE yang saya jalankan (terkait dengan apa yang harus saya terapkan) dengan hasil ini:

elapsed time with checks at every iteration: 12055(ms)
elapsed time with deletion at the end: 11977(ms)

Pada contoh saya setidaknya, melompat-lompat cek pada setiap iterasi tidak terasa lebih cepat terutama untuk volume waras data, tetapi adalah lebih cepat.

import java.util.ArrayList;
import java.util.List;


public class TestCommas {

  public static String GetUrlsIn(int aProjectID, List<String> aUrls, boolean aPreferChecks)
  {

    if (aPreferChecks) {

      StringBuffer sql = new StringBuffer("select * from mytable_" + aProjectID + " WHERE hash IN ");

      StringBuffer inHashes = new StringBuffer("(");
      StringBuffer inURLs = new StringBuffer("(");

      if (aUrls.size() > 0)
      {

      for (String url : aUrls)
      {

        if (inHashes.length() > 0) {
        inHashes.append(",");
        inURLs.append(",");
        }

        inHashes.append(url.hashCode());

        inURLs.append("\"").append(url.replace("\"", "\\\"")).append("\"");//.append(",");

      }

      }

      inHashes.append(")");
      inURLs.append(")");

      return sql.append(inHashes).append(" AND url IN ").append(inURLs).toString();
    }

    else {

      StringBuffer sql = new StringBuffer("select * from mytable" + aProjectID + " WHERE hash IN ");

      StringBuffer inHashes = new StringBuffer("(");
      StringBuffer inURLs = new StringBuffer("(");

      if (aUrls.size() > 0)
      {

      for (String url : aUrls)
      {
        inHashes.append(url.hashCode()).append(","); 

        inURLs.append("\"").append(url.replace("\"", "\\\"")).append("\"").append(",");
      }

      }

      inHashes.deleteCharAt(inHashes.length()-1);
      inURLs.deleteCharAt(inURLs.length()-1);

      inHashes.append(")");
      inURLs.append(")");

      return sql.append(inHashes).append(" AND url IN ").append(inURLs).toString();
    }

  }

  public static void main(String[] args) { 
        List<String> urls = new ArrayList<String>();

    for (int i = 0; i < 10000; i++) {
      urls.add("http://www.google.com/" + System.currentTimeMillis());
      urls.add("http://www.yahoo.com/" + System.currentTimeMillis());
      urls.add("http://www.bing.com/" + System.currentTimeMillis());
    }


    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 300; i++) {
      GetUrlsIn(5, urls, true);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("elapsed time with checks at every iteration: " + (endTime-startTime) + "(ms)");

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 300; i++) {
      GetUrlsIn(5, urls, false);
    }
    endTime = System.currentTimeMillis();
    System.out.println("elapsed time with deletion at the end: " + (endTime-startTime) + "(ms)");
  }
}
Kerbau
sumber
1
Tolong jangan menggulung tolok ukur Anda sendiri dan menggunakan OpenJDK sendiri memanfaatkan benchmarking mikro (jmh) . Ini akan memanaskan kode Anda dan menghindari jebakan yang paling bermasalah.
René
0

Pendekatan lain adalah memiliki panjang array (jika tersedia) disimpan dalam variabel yang terpisah (lebih efisien daripada memeriksa ulang panjang setiap kali). Anda kemudian dapat membandingkan indeks Anda dengan panjang itu untuk menentukan apakah akan menambahkan koma akhir atau tidak.

EDIT: Pertimbangan lain adalah menimbang biaya kinerja menghapus karakter akhir (yang dapat menyebabkan salinan string) agar tidak bersyarat diperiksa di setiap iterasi.

Brian
sumber
Menghapus karakter terakhir seharusnya tidak menyebabkan salinan string dibuat. Metode deleteBarffer / deleteCharAt StringBuffer akhirnya memanggil metode berikut di dalam array kelas dan sumber tujuan yang identik dengan kelas Sistem: Sistem -> public static public void arraycopy (Object src, int srcPos, Object dest, int destPos, int length);
Kerbau
0

Jika Anda hanya mengubah array menjadi array yang dibatasi koma, banyak bahasa memiliki fungsi gabungan untuk hal ini. Ini mengubah array menjadi string dengan pembatas antara setiap elemen.

Dina
sumber
0

Dalam hal ini benar-benar tidak perlu tahu apakah ini adalah pengulangan terakhir. Ada banyak cara untuk menyelesaikannya. Salah satu caranya adalah:

String del = null;
for(int i : array)
{
    if (del != null)
       builder.append(del);
    else
       del = ",";
    builder.append(i);
}
fastcodejava
sumber
0

Dua jalur alternatif di sini:

1: Utilitas String Apache Commons

2: Tetap memanggil boolean first, atur true. Dalam setiap iterasi, jika firstsalah, tambahkan koma Anda; setelah itu, setel firstke false.

Paul Brinkley
sumber
1) Tautan sudah mati 2) Butuh waktu lebih lama untuk membaca deskripsi daripada kode :)
Buffalo
0

Karena ini adalah array tetap, akan lebih mudah hanya untuk menghindari peningkatan untuk ... Jika Objek adalah kumpulan, maka iterator akan lebih mudah.

int nums[] = getNumbersArray();
StringBuilder builder = new StringBuilder();

// non enhanced version
for(int i = 0; i < nums.length; i++){
   builder.append(nums[i]);
   if(i < nums.length - 1){
       builder.append(",");
   }   
}

//using iterator
Iterator<int> numIter = Arrays.asList(nums).iterator();

while(numIter.hasNext()){
   int num = numIter.next();
   builder.append(num);
   if(numIter.hasNext()){
      builder.append(",");
   }
}
AnthonyJClink
sumber