Java'da döngü için son yineleme


140

Döngünün son kez yinelenip yinelenmediğini belirlemenin bir yolu var mı? Kodum şuna benzer:

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

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

Şimdi, son yinelemede virgül eklemek istemiyorum. Şimdi son yineleme olup olmadığını belirlemek için bir yolu var mı yoksa for döngüsü veya takip etmek için harici bir sayaç kullanarak sıkışıp kaldım.


1
Evet! Komik, sadece aynı soruyu sormak istedim!
PhiLho

Aynı tür bir soru geri döner (ve olacaktır). Şimdi neden bir elementin farklı bir tedaviye ihtiyacı varsa bir döngü oluşturmak istesin ki? stackoverflow.com/questions/156650/…
xtofl

Sabit bir diziniz olduğundan neden gelişmiş? için (int i = 0; i <array.length; i ++ if (i <array.lenth) ,,,
AnthonyJClink

Yanıtlar:


223

Başka bir alternatif, ilk yinelemede değil, i eklemeden önce virgül eklemektir. (Bu arada lütfen kullanmayın "" + i- burada gerçekten birleştirme istemezsiniz ve StringBuilder mükemmel bir ek (int) aşırı yüklemeye sahiptir.)

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

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

Bununla ilgili güzel olan şey, herhangi biriyle çalışacağıdır Iterable- her zaman bir şeyler dizine ekleyemezsiniz. ("Virgül ekleyin ve sonra sonunda kaldırın" gerçekten StringBuilder kullandığınızda hoş bir öneridir - ancak akışlara yazma gibi şeyler için işe yaramaz. )


2
İyi desen, ancak builder.length ()! = 0 kırılgandır - döngünüzden önce arabelleğe bir şey eklendiğini (koşullu olarak!) Düşünün. Bunun yerine, bir isFirstboole bayrağı kullanın . Bonus: daha hızlı.
Jason Cohen

2
@ Jason: Kesinlikle ilk yinelemede inşaatçının boş kalmayacağı "isFirst" kalıbını kullanıyorum. Ancak, gerekli olmadığında, tecrübelerime göre uygulamaya oldukça fazla miktarda şişkinlik katıyor.
Jon Skeet

3
@Liverpool (vb.): 1000 gereksiz kontrolün performans üzerinde önemli bir etkisi olması çok olası değildir. Dinah'ın eklediği ekstra karakterin StringBuilder'ın genişlemesine neden olabileceğini ve son dizenin boyutunu iki katına (cont) iki katına çıkarabileceğini belirtebilirim
Jon Skeet

2
gereksiz arabellek alanı. Ancak, önemli olan okunabilirliktir. Sürümü Dinah'dan daha okunabilir buluyorum. Eğer tam tersini hissediyorsanız, sorun değil. Sadece bir darboğaz bulduktan sonra gereksiz "ifs" in performans etkisini düşünürdüm.
Jon Skeet

3
Buna ek olarak, benim çözümüm daha genel olarak uygulanabilir bir çözümdür, çünkü sadece yazmaya muktedirdir. Çözümümü alabilir ve daha sonra gereksiz verileri geri alamayacağınız bir akışa vb. Yazmak için değiştirebilirsiniz. Genel olarak uygulanabilir kalıpları severim.
Jon Skeet

145

Bunu yapmanın başka bir yolu:

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

Güncelleme: Java 8 için artık Koleksiyonerleriniz var


1
Benzer cevabımı silmek zorunda kaldım - diğer cevaplara daha dikkatli bakmadan önce bana yazmamayı öğretecek.
Michael Burr

1
Ayrıca, Robert Paulson'un başka bir yorum dizisinde bahsettiği olası sorunu ele aldığını unutmayın - bu teknik, dizi değerleri eklendiğinde StringBuilder'ın boş olmasına bağlı değildir.
Michael Burr

1
Kod daha düzgün / düzgün / eğlenceli olmasına rağmen, if sürümünden daha az belirgindir. Her zaman daha belirgin / okunabilir versiyonu kullanın.
Bill K

8
@Bill: Bu zor ve hızlı bir kural olarak harika değil; 'akıllı' çözümün “daha ​​doğru” çözüm olduğu zamanlar vardır. Daha da önemlisi, bu sürümün okunması gerçekten zor mu? Her iki durumda da bir bakım görevlisinin adım atması gerekir - farkın önemli olduğunu düşünmüyorum.
Greg Case

Bu, dosya sisteminin yazma çağrıları gibi başka bir kaynağa yazmanıza rağmen çalışır. İyi bir fikir.
Tuxman

39

Her zaman eklemek daha kolay olabilir. Ve sonra, döngü ile işiniz bittiğinde, son karakteri kaldırın. Bu şekilde ton daha az koşullu.

Sen kullanabilirsiniz StringBuilders' deleteCharAt(int index)endeks varlık ilelength() - 1


5
bu sorun için en verimli çözüm. +1.
Gerçek Kırmızı.

9
Belki benim, ama yeni eklediğiniz bir şeyi kaldırdığınızdan gerçekten hoşlanmıyorum ...
Fortega

2
Fortega: Her seferinde% 99 oranında yapacağım bir şeyi kontrol etmekten hoşlanmıyorum. Dinah veya sblundy'nin çözümünü uygulamak daha mantıklı (ve daha hızlı IS).
Buffalo

1
Bence en şık çözüm. Teşekkür ederim!
Charles Morin

32

Belki de İş için yanlış aracı kullanıyorsunuzdur.

Bu, yaptığınızdan daha el kitabı ama biraz "eski okul" olmasa bile bir şekilde daha zarif

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


14

Başka bir çözüm (belki de en verimli)

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

Bu, dizinin boş olmamasına bağlı olması dışında doğal bir çözümdür.
orcmid

5
@orcmid: Dizi boşsa, bu hala doğru çıktıyı verir - boş bir dize. Ne demek istediğinden emin değilim.
Eddie

7

basit tutun ve döngü için bir standart kullanın:

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

veya sadece apache commons-lang kullanın StringUtils.join ()


6

Açık döngüler her zaman örtük olanlardan daha iyi çalışır.

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

Sıfır uzunlukta bir dizi ile uğraşıyorsanız, her şeyi bir if ifadesine sarmalısınız.


Bu yaklaşımı seviyorum çünkü gereksiz yere her yinelemeyi test etmiyor. Ekleyeceğim tek şey, "" + int, sadece append (dizi [0]) kullanmaya gerek kalmadan oluşturucunun doğrudan tamsayı ekleyebilmesidir.
Josh

Dizinin en az bir öğesi olduğundan emin olmak için tüm lot etrafında bir tane daha gerekir.
Tom Leys

2
neden herkes INSIDE dize birleştirmesiyle örnekleri kullanmaya devam ediyor? "," + x yeni StringBuilder (",") olarak derlenir. append (x) .toString () ...
John Gardner

@Josh ve @John: Sadece n00b örneğini takip ediyorum. Aynı anda çok fazla şey tanıtmak istemiyorum. Bununla birlikte, görüşünüz çok iyi.
S.Lott

@ Tom: Doğru. Sanırım zaten söyledim. Düzenleme gerekmez.
S.Lott

4

Klasik bir dizin döngüsüne dönüştürürseniz, evet.

Veya bittikten sonra son virgülleri silebilirsiniz. Bunun gibi:

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

Ben, ben sadece kullanmak StringUtils.join()gelen commons-lang .


3

Sınıf Ayırıcıya ihtiyacınız var .

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

Sınıfın uygulanması Separatorçok basittir. toString()Boş bir dize döndüren ilk çağrı dışında her çağrıda döndürülen bir dize sarar .


3

Java.util.AbstractCollection.toString () öğesine dayanarak, sınırlayıcıdan kaçınmak için erken çıkar.

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

Etkili ve zariftir, ancak diğer bazı cevaplar kadar açık değildir.


(! i.hasNext())Genel yaklaşımın sağlamlığının önemli bir parçası olan erken dönüşü eklemeyi unuttunuz . (Buradaki diğer çözümler boş koleksiyonları incelikle ele alıyor, bu yüzden sizin de olmalı! :)
Mike Clark

3

İşte bir çözüm:

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

İlk yinelemeyi arayın :)  


3

Araç setinde belirtildiği gibi, Java 8'de şimdi Koleksiyonerlerimiz var . Kod şöyle görünecektir:

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

Bence bu tam olarak aradığınızı yapıyor ve bu birçok şey için kullanabileceğiniz bir model.


1

Yine başka bir seçenek.

StringBuilder builder = new StringBuilder();
for(int i : array)
    builder.append(',').append(i);
String text = builder.toString();
if (text.startsWith(",")) text=text.substring(1);

Diğer soruda bir varyasyon yaptım
13ren

1

Burada açıklanan çözümlerin birçoğu üstte IMHO'nun, özellikle de dış kütüphanelere dayanan çözümlerin biraz üzerindedir. Her zaman kullandığım virgülle ayrılmış bir liste elde etmek için güzel, temiz ve net bir deyim var. Koşullu (?) Operatöre dayanır:

Düzenleme : Orijinal çözüm doğru, ancak yorumlara göre optimal değil. İkinci kez denemek:

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

İşte dizi ve StringBuilder bildirimi de dahil olmak üzere 4 kod satırında gidin.


Ancak her yinelemede yeni bir StringBuilder oluşturuyorsunuz (+ operatörü sayesinde).
Michael Myers

Evet, bayt koduna bakarsanız haklısınız. Böyle basit bir sorunun bu kadar karmaşık olabileceğine inanamıyorum. Derleyicinin burada optimizasyon yapıp yapamayacağını merak ediyorum.
Julien Chastang

Sağlanan 2., umarım daha iyi bir çözüm.
Julien Chastang

1

İşte bu sonuçlarla çalıştırdığım (uygulamak zorunda olduğum şeyle ilgili) bir SSCCE karşılaştırması:

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

En azından benim bilgisayarımda her işleminde çek atlama özellikle verilerin aklı başında hacimleri için fark daha hızlı değil, ama olup daha hızlı.

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

1
Lütfen kendi kriterlerinizi yuvarlamayın ve OpenJDK'lerin kendi mikro kıyaslama koşum takımını (jmh) kullanın . Kodunuzu ısıtacak ve en sorunlu tuzaklardan kaçınacaktır.
René

0

Başka bir yaklaşım, dizinin uzunluğunun (varsa) ayrı bir değişkende saklanmasıdır (her seferinde uzunluğun yeniden kontrol edilmesinden daha etkilidir). Son olarak, virgülün eklenip eklenmeyeceğini belirlemek için dizininizi bu uzunlukla karşılaştırabilirsiniz.

DÜZENLEME: Başka bir husus, her yinelemede bir koşulun denetlenmesine karşı son karakteri (dize kopyasına neden olabilir) kaldırmanın performans maliyetini tartmaktır.


Son bir karakterin kaldırılması, dize kopyasının yapılmasına neden olmamalıdır. StringBuffer delete / deleteCharAt yöntemleri sonunda System sınıfı özdeş kaynak ve hedef dizilerinde aşağıdaki yöntemi çağırır: Sistem -> genel statik yerel void dizisi (Object src, int srcPos, Object dest, int destPos, int length);
Buffalo

0

Bir diziyi yalnızca virgülle ayrılmış bir diziye dönüştürüyorsanız, birçok dilde tam olarak bunun için birleştirme işlevi bulunur. Bir diziyi, her öğe arasında sınırlayıcı olan bir dizeye dönüştürür.


0

Bu durumda, son tekrar olup olmadığını gerçekten bilmeye gerek yoktur. Bunu çözebilmemiz için birçok yol var. Bunun bir yolu:

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

0

Burada iki alternatif yol var:

1: Apache Commons String Utils

2: firsttrue olarak ayarlanmış bir boole çağrısı yapın . Her yinelemede firstyanlışsa virgül ekleyin; bundan sonra, firstfalse olarak ayarlayın .


1) Bağlantı öldü 2) Kod yerine açıklamayı okumak daha uzun sürdü :)
Buffalo

0

Sabit bir dizi olduğundan, gelişmiş için kaçınmak daha kolay olurdu ... Nesne bir koleksiyon ise, bir yineleyici daha kolay olurdu.

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(",");
   }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.