Java中增强的for循环的最后一次迭代

147

有没有办法确定循环是否正在执行最后一次迭代。我的代码看起来像这样:

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

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

现在的问题是我不想在最后一次循环中添加逗号。现在是否有方法确定它是否为最后一次迭代,或者我只能使用for循环或外部计数器来跟踪。


3
是啊!真有趣,我也正想问同样的问题呢! - PhiLho
同样类型的问题会一再出现。那么,如果一个元素需要不同的处理,为什么要创建一个循环呢?https://dev59.com/AHVC5IYBdhLWcg3w4VVz - xtofl
既然你有一个固定的数组,为什么要使用增强型for循环?for(int i = 0; i< array.length; i++ if(i < array.lenth)... - AnthonyJClink
22个回答

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
你需要使用类别分隔符
Separator s = new Separator(", ");
for(int i : array)
{
     builder.append(s).append(i);
}

Separator类的实现很简单。它包装了一个字符串,在每次调用toString()时返回该字符串,除了第一次调用,它返回一个空字符串。


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

1

在我看来,这里描述的许多解决方案都有点过头了,特别是那些依赖于外部库的解决方案。实现逗号分隔列表的一个不错、清晰的习惯用语,我一直在使用它。它依赖于条件(?)运算符:

编辑:根据评论,原始解决方案正确但不够优化。尝试第二次:

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

这就是了,只用4行代码,包括数组的声明和StringBuilder。


但是你在每次迭代中都创建了一个新的 StringBuilder(感谢 + 操作符)。 - Michael Myers
是的,如果你查看字节码,你是对的。我无法相信这样一个简单的问题会变得如此复杂。我想知道编译器是否可以在这里进行优化。 - Julien Chastang
提供第二种解决方案,希望更好。 - Julien Chastang

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

2
请不要自己编写基准测试,并使用OpenJDK自带的微基准测试工具(jmh)。它可以预热您的代码并避免最常见的问题。 - René

0
在这种情况下,实际上没有必要知道它是否是最后一次重复。我们有很多方法可以解决这个问题。其中一种方法是:
String del = null;
for(int i : array)
{
    if (del != null)
       builder.append(del);
    else
       del = ",";
    builder.append(i);
}

0

由于它是一个固定数组,最好避免使用增强型for循环...如果对象是一个集合,那么使用迭代器会更容易。

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

0

您可以使用StringJoiner

int[] array = { 1, 2, 3 };
StringJoiner stringJoiner = new StringJoiner(",");

for (int i : array) {
    stringJoiner.add(String.valueOf(i));
}

System.out.println(stringJoiner);

0
另一种方法是将数组的长度(如果可用)存储在单独的变量中(比每次重新检查长度更有效)。然后,您可以将索引与该长度进行比较,以确定是否添加最后一个逗号。
编辑:另一个考虑因素是权衡删除最后一个字符(可能会导致字符串复制)的性能成本,与在每次迭代中检查条件。

删除最后一个字符不应该导致字符串复制。StringBuffer的delete/deleteCharAt方法最终会调用System类内部的以下方法,它们具有相同的源和目标数组:System -> public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); - Buffalo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接