Javaで拡張されたforループの最後の反復


140

ループが最後に繰り返されているかどうかを判断する方法はありますか?私のコードは次のようになります:

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

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

さて、最後の反復でコンマを追加したくないのです。これが最後の反復かどうかを判断する方法がありますか、それともforループでスタックしているのか、外部カウンタを使用して追跡しているのかを確認する方法があります。


1
うん!おかしい、まったく同じ質問をしたかっただけです!
PhiLho 2008年

同じ種類の質問が返されます(返されます)。ここで、1つの要素に異なる処理が必要な場合にループを作成する理由は何でしょうか。 stackoverflow.com/questions/156650/...
xtofl

固定配列があるので、なぜ拡張機能を使用するのですか?for(int i = 0; i <array.length; i ++ if(i <array.lenth),,,
AnthonyJClink

回答:


223

もう1つの方法は、最初の反復ではなく、iを追加する前にコンマを追加することです。("" + iちなみに、は使用しないでください。ここでは連結は必要ありません。StringBuilderには、append(int)オーバーロードが完全に備わっています。)

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

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

これの良い点は、どのIterableファイルでも機能することです。常にインデックスを付けることはできません。(「コンマを追加して、最後に削除する」は、実際にStringBuilderを使用している場合の良い提案ですが、ストリームへの書き込みなどには機能しません。この正確な問題に対しては、おそらく最善の方法です。 )


2
良いパターンですが、builder.length()!= 0は脆弱です-ループの前に何かが(条件付きで)バッファーに追加されることを想像してください。代わりに、isFirstブールフラグを使用します。ボーナス:速いです。
Jason Cohen

2
@ジェイソン:確かに、最初の反復でビルダーが空にならない「isFirst」パターンを使用しています。しかし、それが不要な場合、私の経験では、実装にかなりの肥大が加わります。
Jon Skeet

3
@Liverpool(ETC):1000の不要なチェックは非常に、ある非常にパフォーマンスに重大な影響を与える可能性は低いです。Dinahのソリューションによって追加された文字が追加されると、StringBuilderが拡張し、最終的な文字列のサイズが(cont)で2倍になる可能性があることも同様に指摘します
Jon Skeet

2
不要なバッファ領域。ただし、重要なのは読みやすさです。私のバージョンはDinahよりも読みやすくなっています。逆に感じる場合はそれで結構です。ボトルネックを発見した後で、不要な「if」のパフォーマンスへの影響のみを検討します。
ジョンスキート

3
さらに、私のソリューションは、書き込みができることだけに依存しているため、より一般的に適用可能なソリューションです。私の解決策を取り、それをストリームなどに書き込むように変更することができます-不要なデータを後で取り戻すことができない場合があります。一般的に適用できるパターンが好きです。
ジョンスキート

145

これを行う別の方法:

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

更新:Java 8の場合、現在コレクターがあります


1
私の同様の答えを削除しなければならなかった-それは私が他の答えをより注意深く見る前に投稿しないことを教えてくれるでしょう。
マイケル・バー、

1
また、これは、別のコメントスレッドで言及されているRobert Paulsonの潜在的な問題を処理することに注意してください。この手法は、配列値が追加されたときにStringBuilderが空であることには依存しません。
マイケル・バー

1
コードはより洗練された/きちんとした/楽しいですが、ifバージョンほど明白ではありません。常により明確で読みやすいバージョンを使用してください。
ビルK

8
@ビル:それは難しいと速いルールとしては良くありません。「賢い」ソリューションが「より正確な」ソリューションである場合があります。もっと端的に言うと、このバージョンは読みにくいのですか?どちらの方法でも、メンテナがそれを実行する必要があります-違いは重要ではないと思います。
グレッグケース

ファイルシステムの書き込み呼び出しのような他のリソースに書き込んでも、これは機能します。良いアイデア。
Tuxman

39

常に追加する方が簡単かもしれません。そして、ループが完了したら、最後の文字を削除します。そのようにして、条件文も少なくなります。

あなたは使用することができるStringBuilderのがdeleteCharAt(int index)インデックスされた状態でlength() - 1


5
この問題の最も効率的な解決策。+1。
リアルレッド。

9
多分それは私ですが、あなたが追加したばかりのものを削除しているという事実は本当に好きではありません...
Fortega

2
フォルテガ:99%の頻度で何かをチェックするのは好きではありません。Dinahのソリューションまたはsblundyのソリューションを適用する方がより論理的(かつ高速)です。
バッファロー

1
私の意見では最もエレガントなソリューションです。ありがとうございました!
Charles Morin

32

多分あなたは仕事のために間違ったツールを使用しています。

これはあなたがやっていることよりも手作業ですが、少し「古い学校」ではないにしても、よりエレガントな方法です

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


14

別のソリューション(おそらく最も効率的)

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

これは、配列が空でないことに依存することを除いて、自然な解決策です。
orcmid 2008年

5
@orcmid:配列が空の場合でも、これは正しい出力、つまり空の文字列を提供します。あなたの言いたいことがよくわかりません。
Eddie、


6

明示的なループは常に暗黙的なループよりもうまく機能します。

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

長さ0の配列を処理する場合に備えて、すべてをifステートメントでラップする必要があります。


このアプローチが好きなのは、反復ごとに不必要にテストを実行する必要がないためです。私が追加する唯一のものは、ビルダーが整数を直接追加できるため、 "" + intを使用する必要がなく、append(array [0])だけであるということです。
Josh、

配列全体に少なくとも1つの要素があることを確認するために、ロット全体の場合はもう1つ必要です。
Tom Leys

2
なぜ誰もが追加の文字列連結を含む例を使い続けるのですか?" "+ Xコンパイル新しいStringBuilderの("、").append(x)の.toString()...に
ジョン・ガードナー

@Joshおよび@John:n00bの例の直後。一度に多くのことを紹介したくない。しかし、あなたの主張はとても良いです。
S.Lott、2008年

@トム:そうです。私はすでにそれを言ったと思います。編集は必要ありません。
S.Lott、2008年

4

それをクラシックインデックスループに変換する場合、そうです。

または、最後のカンマを削除した後で削除することもできます。そのようです:

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

私、私StringUtils.join()commons-langから使用しています。


3

Class Separatorが必要です。

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

クラスの実装Separatorは簡単です。toString()空の文字列を返す最初の呼び出しを除いて、すべての呼び出しで返される文字列をラップします。


3

java.util.AbstractCollection.toString()に基づいて、デリミタを回避するために早期に終了します。

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

効率的でエレガントですが、他の回答ほど自明ではありません。


早期リターンを含めるのを忘れていました(! i.hasNext())。これは、全体的なアプローチの堅牢性の重要な部分です。(ここにある他のソリューションは空のコレクションを適切に処理するので、あなたもそうする必要があります!:)
Mike Clark

3

これが解決策です:

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

最初の反復を探します:)  


3

ツールキットで述べたように、Java 8では現在コレクターがあります。コードは次のようになります。

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

それはまさにあなたが探しているものだと思います、そしてそれはあなたが他の多くのものに使うことができるパターンです。


1

さらに別のオプション。

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

私は他の質問でバリエーションを作りました
13ren 2009年

1

ここで説明するソリューションの多くは、特に外部ライブラリに依存するものであるIMHOを少し超えています。私がいつも使ってきたコンマ区切りのリストを実現するためのすっきりとした明確なイディオムがあります。条件付き(?)演算子に依存します。

編集:元のソリューションは正しいが、コメントによると最適ではない。もう一度試す:

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

それでは、配列の宣言とStringBuilderを含む4行のコードを書きます。


しかし、毎回新しいStringBuilderを作成しています(+演算子のおかげです)。
マイケルマイヤーズ

はい、バイトコードを見ると正しいです。私はそのような単純な質問がそれほど複雑になるとは信じられません。コンパイラがここで最適化できるかどうか。
Julien Chastang 2009年

第2の提供、うまくいけばより良いソリューション。
Julien Chastang 2009年

1

以下に、私が実行したSSCCEベンチマーク(実装しなければならないものに関連)とこれらの結果を示します。

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

少なくとも私の例では、すべての反復でチェックをスキップすること、特に正常なデータ量の場合、それほど速くはありませんが、より高速です。

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
独自のベンチマークをロールバックせず、OpenJDK独自のマイクロベンチマークハーネス(jmh)を使用してください。コードをウォームアップし、最も問題の多い落とし穴を回避します。
ルネ

0

別の方法は、配列の長さ(利用可能な場合)を別の変数に格納することです(毎回長さを再確認するよりも効率的です)。次に、インデックスをその長さと比較して、最後のコンマを追加するかどうかを決定できます。

編集:別の考慮事項は、最後の文字(文字列のコピーを引き起こす可能性があります)を削除するパフォーマンスコストと、各反復で条件付きのチェックが行われることの比較検討です。


最後の文字を削除しても、文字列のコピーは作成されません。StringBufferのdelete / deleteCharAtメソッドは、最終的に、Systemクラス内の次のメソッドを同一のソース配列と宛先配列に呼び出します。
バッファロー

0

配列をコンマ区切りの配列に変換するだけの場合は、多くの言語でこれを行うための結合関数があります。これは、配列を各要素間に区切り文字を含む文字列に変換します。


0

この場合、それが最後の繰り返しかどうかを知る必要はありません。これを解決する方法はたくさんあります。1つの方法は次のとおりです。

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

0

ここに2つの代替パス:

1:Apache Commons String Utils

2:firsttrue というブール値を保持します。各反復でfirstfalseの場合は、コンマを追加します。その後、firstfalse に設定します。


1)リンクが無効になっている2)コードではなく説明を読むのに時間がかかった:)
Buffalo

0

固定配列であるため、拡張機能を回避するだけで簡単です...オブジェクトがコレクションの場合、イテレータの方が簡単です。

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(",");
   }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.