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

Posted

技术标签:

【中文标题】java中增强for循环的最后一次迭代【英文标题】:Last iteration of enhanced for loop in java 【发布时间】:2010-09-22 01:56:15 【问题描述】:

有没有办法确定循环是否是最后一次迭代。我的代码如下所示:

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

for(int i : array)

    builder.append("" + i);
    if(!lastiteration)
        builder.append(",");

现在问题是我不想在最后一次迭代中附加逗号。现在有没有办法确定它是最后一次迭代,还是我坚持使用 for 循环或使用外部计数器来跟踪。

【问题讨论】:

是的!很有趣,我只是想问完全相同的问题! 同样的问题会回来(而且会如此)。现在,如果一个元素需要不同的处理,为什么要创建一个循环? ***.com/questions/156650/… 既然你有一个固定的数组,为什么要使用增强型呢? for(int i = 0; i 【参考方案1】:

总是追加可能更容易。然后,当您完成循环后,只需删除最后一个字符。这样也少了很多条件。

您可以使用StringBuilderdeleteCharAt(int index),索引为length() - 1

【讨论】:

这个问题最有效的解决方案。 +1。 也许是我,但我真的不喜欢你正在删除你刚刚添加的东西...... Fortega:我不喜欢每次都检查我 99% 都会做的事情。应用 Dinah 或 sblundy 的解决方案似乎更合乎逻辑(而且速度更快)。 我认为最优雅的解决方案。谢谢!【参考方案2】:

如果你把它转换成一个经典的索引循环,是的。

或者您可以在完成后删除最后一个逗号。像这样:

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

我,我只是使用commons-lang 中的StringUtils.join()

【讨论】:

【参考方案3】:

保持简单并使用标准的 for 循环:

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

或者只使用 apache commons-lang StringUtils.join()

【讨论】:

【参考方案4】:

显式循环总是比隐式循环更好。

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

您应该将整个内容包装在一个 if 语句中,以防万一您正在处理一个零长度数组。

【讨论】:

我喜欢这种方法,因为它不需要在每次迭代中执行测试。我唯一要补充的是builder可以直接附加整数,因此不需要使用“”+ int,只需附加(数组[0])。 如果围绕整个批次,您还需要一个以确保数组至少包含一个元素。 为什么每个人都在 append 的内部继续使用字符串连接的示例? ", " + x 编译成 new StringBuilder(", ").append( x ).toString()... @Josh 和@John:按照 n00b 示例。不想一次介绍太多东西。但是,您的观点非常好。 @Tom:对。我想我已经说过了。无需修改。【参考方案5】:

另一种选择是在附加 i 之前附加逗号,而不是在 first 迭代中。 (顺便说一句,请不要使用"" + 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 时,“添加逗号然后在末尾删除它”是一个不错的建议 - 但它不适用于写入流之类的事情。不过,这可能是解决这个确切问题的最佳方法。 )

【讨论】:

很好的模式,但是 builder.length() != 0 很脆弱——想象一下在循环之前将某些东西(有条件地!)添加到缓冲区中。相反,请使用 isFirst 布尔标志。奖励:也更快。 @Jason:我当然使用“isFirst”模式,其中构建器在第一次迭代时不会为空。但是,根据我的经验,当不需要它时,它会给实现增加相当多的臃肿。 @Liverpool (etc):1000 次不必要的检查非常,非常不太可能对性能产生任何重大影响。我可能同样指出,Dinah 的解决方案添加的额外字符 可能 导致 StringBuilder 必须扩展,最终字符串的大小加倍 (cont) 不必要的缓冲空间。然而,重要的一点是可读性。我碰巧发现我的版本比 Dinah 的更具可读性。如果你有相反的感觉,那很好。在发现瓶颈后,我只会考虑不必要的“ifs”对性能的影响。 另外,我的解决方案是一个更普遍适用的解决方案,因为它只依赖于能够写。您可以采用我的解决方案并将其更改为写入流等 - 之后您可能无法取回不必要的数据。我喜欢普遍适用的模式。【参考方案6】:

另一种方法是将数组的长度(如果可用)存储在单独的变量中(比每次重新检查长度更有效)。然后,您可以将索引与该长度进行比较,以确定是否添加最后一个逗号。

编辑:另一个考虑因素是权衡删除最终字符(可能导致字符串复制)与在每次迭代中检查条件的性能成本。

【讨论】:

删除最后一个字符不应导致复制字符串。 StringBuffer 的 delete/deleteCharAt 方法最终调用 System 类中相同的源和目标数组中的以下方法: System -> public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);【参考方案7】:

这里有一个解决方案:

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;

寻找第一次迭代 :)

【讨论】:

【参考方案8】:

这里有两条替代路径:

1:Apache Commons String Utils

2:保留一个名为 first 的布尔值,设置为 true。在每次迭代中,如果first 为假,则附加逗号;之后,将first 设置为 false。

【讨论】:

1) 链接失效 2) 我花了更长的时间阅读描述而不是代码:)【参考方案9】:

也许您在作业中使用了错误的工具。

这比你正在做的更手动,但它在某种程度上更优雅,如果不是有点“老派”

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

【讨论】:

【参考方案10】:

另一种解决方案(也许是最有效的)

    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:如果数组是空的,那么这仍然会给出正确的输出——一个空字符串。我不确定你的意思是什么。【参考方案11】:

另一种方法:

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

更新:对于 Java 8,您现在拥有 Collectors

【讨论】:

不得不删除我的类似答案 - 这将教会我在更仔细地查看其他答案之前不要发布。 另请注意,这可以处理 Robert Paulson 在另一个评论线程中提到的潜在问题 - 此技术不依赖于在附加数组值时 StringBuilder 为空。 虽然代码更流畅/整洁/有趣,但不如if版本明显。始终使用更明显/可读的版本。 @Bill:这不是一个硬性规定;有时“聪明”的解决方案是“更正确”的解决方案。更重要的是,你真的认为这个版本很难阅读吗?无论哪种方式,维护人员都需要逐步完成 - 我认为差异并不显着。 尽管您写入文件系统的写入调用等其他资源,但它仍然有效。好主意。【参考方案12】:

如果您只是将数组转换为逗号分隔的数组,许多语言都有专门的连接函数。它将一个数组变成一个字符串,每个元素之间都有一个分隔符。

【讨论】:

【参考方案13】:

你需要Class Separator。

Separator s = new Separator(", ");
for(int i : array)

     builder.append(s).append(i);

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

【讨论】:

【参考方案14】:

基于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()) 时忘记包含早期返回,这是整体方法稳健性的重要组成部分。 (这里的其他解决方案可以优雅地处理空集合,所以你也应该这样做!:)【参考方案15】:

另一种选择。

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

【讨论】:

我在另一个问题上做了修改【参考方案16】:

这几乎是this *** question 的重复。你想要的是 StringUtils,并调用 join 方法。

StringUtils.join(strArr, ',');

【讨论】:

【参考方案17】:

这里描述的许多解决方案有点过头了,恕我直言,尤其是那些依赖外部库的解决方案。有一个很好的干净、清晰的习惯用法来实现我一直使用的逗号分隔列表。它依赖于条件 (?) 运算符:

编辑:原始解决方案正确,但根据 cmets 不是最佳的。第二次尝试:

    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(感谢 + 运算符)。 是的,如果您查看字节码,您是对的。我不敢相信这么简单的问题会变得如此复杂。不知编译器能不能在这里优化。 提供了第二个,希望是更好的解决方案。【参考方案18】:

在这种情况下,真的不需要知道它是否是最后一次重复。 我们有很多方法可以解决这个问题。一种方法是:

String del = null;
for(int i : array)

    if (del != null)
       builder.append(del);
    else
       del = ",";
    builder.append(i);

【讨论】:

【参考方案19】:

这是我运行的 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)");
  

【讨论】:

请不要推出自己的基准测试并使用OpenJDKs own micro benchmarking harness (jmh)。它将预热您的代码并避免最有问题的陷阱。【参考方案20】:

正如工具包所述,在 Java 8 中,我们现在拥有 Collectors。代码如下所示:

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

我认为这正是您正在寻找的,并且它是您可以用于许多其他事情的模式。

【讨论】:

【参考方案21】:

由于它是一个固定数组,因此更容易避免增强 for... 如果 Object 是一个集合,则迭代器会更容易。

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

【讨论】:

【参考方案22】:

您可以使用StringJoiner。

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

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


System.out.println(stringJoiner);

【讨论】:

以上是关于java中增强for循环的最后一次迭代的主要内容,如果未能解决你的问题,请参考以下文章

java基础-迭代器(Iterator)与增强for循环

java基础40 增强for循环(也叫foreach循环)

增强for循环

java中使用增强型的for循环有啥好处?

Java——集合(Collection接口),迭代器,增强for循环,泛型

如何正确遍历删除List中的元素(普通for循环增强for循环迭代器iteratorremoveIf+方法引用)