最好的循环成语专门套最后一个元素

在进行简单的文本处理和打印语句的时候,我经常遇到这种情况,我正在循环一个集合,我想特殊情况下的最后一个元素(例如,除了最后一种情况,每个普通的元素将被逗号分隔)。

是否有一些最好的习惯用法或优雅的forms,不需要重复的代码或推在if,否则在循环中。

例如,我有一个string列表,我想用逗号分隔列表打印。 (当解决scheme已经假定列表中有2个或更多的元素,否则它会像条件更正确的循环一样坏)。

例如List =(“狗”,“猫”,“蝙蝠”)

我想打印“[狗,猫,蝙蝠]”

我介绍2种方法

  1. For循环与条件

    public static String forLoopConditional(String[] items) { String itemOutput = "["; for (int i = 0; i < items.length; i++) { // Check if we're not at the last element if (i < (items.length - 1)) { itemOutput += items[i] + ", "; } else { // last element itemOutput += items[i]; } } itemOutput += "]"; return itemOutput; } 
  2. 循环启动循环

     public static String doWhileLoopPrime(String[] items) { String itemOutput = "["; int i = 0; itemOutput += items[i++]; if (i < (items.length)) { do { itemOutput += ", " + items[i++]; } while (i < items.length); } itemOutput += "]"; return itemOutput; } 

    testing人员类别:

     public static void main(String[] args) { String[] items = { "dog", "cat", "bat" }; System.out.println(forLoopConditional(items)); System.out.println(doWhileLoopPrime(items)); } 

在Java AbstractCollection类中它有以下实现(有点冗长,因为它包含所有的边界大小写错误检查,但并不坏)。

 public String toString() { Iterator<E> i = iterator(); if (! i.hasNext()) return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = i.next(); sb.append(e == this ? "(this Collection)" : e); if (! i.hasNext()) return sb.append(']').toString(); sb.append(", "); } } 

这些答案中有很多for循环,但是我发现Iterator和while循环的读取更容易。 例如:

 Iterator<String> itemIterator = Arrays.asList(items).iterator(); if (itemIterator.hasNext()) { // special-case first item. in this case, no comma while (itemIterator.hasNext()) { // process the rest } } 

这是Joiner在Googlecollections中采取的方法,我觉得它非常可读。

我通常这样写:

 static String commaSeparated(String[] items) { StringBuilder sb = new StringBuilder(); String sep = ""; for (String item: items) { sb.append(sep); sb.append(item); sep = ","; } return sb.toString(); } 
 string value = "[" + StringUtils.join( items, ',' ) + "]"; 

我通常的做法是testing索引variables是否为零,例如:

 var result = "[ "; for (var i = 0; i < list.length; ++i) { if (i != 0) result += ", "; result += list[i]; } result += " ]"; 

但是,当然,只有当我们谈论没有一些Array.join(“,”)方法的语言。 😉

我认为把第一个元素当作特例比较容易,因为知道迭代是第一个而不是最后一个是比较容易的。 它不需要任何复杂或昂贵的逻辑来知道是否第一次做什么。

 public static String prettyPrint(String[] items) { String itemOutput = "["; boolean first = true; for (int i = 0; i < items.length; i++) { if (!first) { itemOutput += ", "; } itemOutput += items[i]; first = false; } itemOutput += "]"; return itemOutput; } 

我喜欢用第一个项目的标志。

  ArrayList<String> list = new ArrayList()<String>{{ add("dog"); add("cat"); add("bat"); }}; String output = "["; boolean first = true; for(String word: list){ if(!first) output += ", "; output+= word; first = false; } output += "]"; 

由于你的情况是简单的处理文本,你不需要循环内的条件。 AC示例:

 char* items[] = {"dog", "cat", "bat"}; char* output[STRING_LENGTH] = {0}; char* pStr = &output[1]; int i; output[0] = '['; for (i=0; i < (sizeof(items) / sizeof(char*)); ++i) { sprintf(pStr,"%s,",items[i]); pStr = &output[0] + strlen(output); } output[strlen(output)-1] = ']'; 

不要添加一个条件来避免生成尾随逗号,而是生成它(保持简单且无条件的循环),并在最后简单地覆盖它。 很多时候,我发现生成特殊情况就像任何其他循环迭代一样清楚,然后在最后手动replace它(尽pipe如果“replace它”的代码不止一两行,这种方法实际上可能变得更难读)。

 String[] items = { "dog", "cat", "bat" }; String res = "["; for (String s : items) { res += (res.length == 1 ? "" : ", ") + s; } res += "]"; 

或者是非常可读的。 当然,你可以把条件放在一个单独的if子句中。 它是什么使地道(至less我这么认为)是它使用foreach循环,并没有使用复杂的循环头。

此外,没有逻辑是重复的(即只有一个地方的项目实际上是附加到输出string – 在现实世界的应用程序这可能是一个更复杂和冗长的格式化操作,所以我不想重复代码)。

在这种情况下,您基本上使用某个分隔符string连接了一个string列表。 你可以写一些你自己的东西。 那么你会得到像这样的东西:

 String[] items = { "dog", "cat", "bat" }; String result = "[" + joinListOfStrings(items, ", ") + "]" 

 public static String joinListOfStrings(String[] items, String sep) { StringBuffer result; for (int i=0; i<items.length; i++) { result.append(items[i]); if (i < items.length-1) buffer.append(sep); } return result.toString(); } 

如果你有一个Collection而不是一个String[]你也可以使用迭代器和hasNext()方法来检查这是否是最后一个。

如果您正在像这样dynamic创buildstring,则不应使用+ =运算符。 StringBuilder类对于重复的dynamicstring连接效果更好。

 public String commaSeparate(String[] items, String delim){ StringBuilder bob = new StringBuilder(); for(int i=0;i<items.length;i++){ bob.append(items[i]); if(i+1<items.length){ bob.append(delim); } } return bob.toString(); } 

那么打电话就是这样

 String[] items = {"one","two","three"}; StringBuilder bob = new StringBuilder(); bob.append("["); bob.append(commaSeperate(items,",")); bob.append("]"); System.out.print(bob.toString()); 

我会跟你的第二个例子,即。 处理循环之外的特殊情况,直接写一下:

 String itemOutput = "["; if (items.length > 0) { itemOutput += items[0]; for (int i = 1; i < items.length; i++) { itemOutput += ", " + items[i]; } } itemOutput += "]"; 

一般来说,我最喜欢的是多层次的出口。 更改

 for ( s1; exit-condition; s2 ) { doForAll(); if ( !modified-exit-condition ) doForAllButLast(); } 

 for ( s1;; s2 ) { doForAll(); if ( modified-exit-condition ) break; doForAllButLast(); } 

它消除了任何重复的代码或冗余检查。

你的例子:

 for (int i = 0;; i++) { itemOutput.append(items[i]); if ( i == items.length - 1) break; itemOutput.append(", "); } 

它比别人更有效。 对于这个具体的例子,我不是一个很大的粉丝。

当然,对于退出条件取决于doForAll()发生的情况而不仅仅是s2 ,情况变得非常棘手。 使用Iterator就是这样的情况。

这里有一位教授的文章,无耻地推广给他的学生:-)。 阅读第5节,确切地说你在说什么。

我认为这个问题有两个答案:任何语言的这个问题的最佳成语,以及这个问题在Java中最好的成语。 我也认为这个问题的意图不是把string连接在一起的任务,而是一般的模式,所以它不能真正帮助显示可以做到这一点的库函数。

首先,虽然用[]围绕string并创build用逗号分隔的string的动作是两个单独的动作,理想情况下是两个单独的function。

对于任何语言,我认为recursion和模式匹配的组合效果最好。 例如,在哈斯克尔,我会这样做:

 join [] = "" join [x] = x join (x:xs) = concat [x, ",", join xs] surround before after str = concat [before, str, after] yourFunc = surround "[" "]" . join -- example usage: yourFunc ["dog", "cat"] will output "[dog,cat]" 

这样写的好处是清楚地列举了函数将面临的不同情况,以及如何处理它。

另一个很好的方法是使用累加器types的函数。 例如:

 join [] = "" join strings = foldr1 (\ab -> concat [a, ",", b]) strings 

这可以用其他语言来完成,比如c#:

 public static string Join(List<string> strings) { if (!strings.Any()) return string.Empty; return strings.Aggregate((acc, val) => acc + "," + val); } 

在这种情况下效率不高,但在其他情况下可能有用(或效率可能不重要)。

不幸的是,java不能使用这两种方法之一。 所以在这种情况下,我认为最好的方法是在函数的顶部检查exception情况(0或1个元素),然后使用for循环来处理具有多于1个元素的情况:

 public static String join(String[] items) { if (items.length == 0) return ""; if (items.length == 1) return items[0]; StringBuilder result = new StringBuilder(); for(int i = 0; i < items.length - 1; i++) { result.append(items[i]); result.append(","); } result.append(items[items.length - 1]); return result.toString(); } 

此function清楚地显示了两个边缘情况(0或1个元素)中发生的情况。 然后使用一个循环,除了最后一个元素,最后添加最后一个元素,而不用逗号。 在开始处理非逗号元素的反方式也很容易做到。

请注意, if (items.length == 1) return items[0]; 线实际上是不必要的,但是我认为这使得function更容易一目了然确定。

(请注意,如果有人想要更多的解释haskell / c#函数问,我会添加它)

Java 8解决scheme,以防有人在寻找它:

 String res = Arrays.stream(items).reduce((t, u) -> t + "," + u).get(); 

我通常写这样一个for循环:

 public static String forLoopConditional(String[] items) { StringBuilder builder = new StringBuilder(); builder.append("["); for (int i = 0; i < items.length - 1; i++) { builder.append(items[i] + ", "); } if (items.length > 0) { builder.append(items[items.length - 1]); } builder.append("]"); return builder.toString(); } 

如果你只是在寻找一个像这样的逗号分隔列表:“[,,猫,在,帽子]”,甚至不要浪费时间写自己的方法。 只要使用List.toString:

 List<String> strings = Arrays.asList("The", "Cat", "in", "the", "Hat); System.out.println(strings.toString()); 

假如列表的通用types有一个toString和要显示的值,只需调用List.toString:

 public class Dog { private String name; public Dog(String name){ this.name = name; } public String toString(){ return name; } } 

那你可以这样做:

 List<Dog> dogs = Arrays.asList(new Dog("Frank"), new Dog("Hal")); System.out.println(dogs); 

你会得到:[Frank,Hal]

第三个select是以下

 StringBuilder output = new StringBuilder(); for (int i = 0; i < items.length - 1; i++) { output.append(items[i]); output.append(","); } if (items.length > 0) output.append(items[items.length - 1]); 

但最好是使用join()方法。 对于Java,在第三方库中有一个String.join ,这样你的代码就变成了:

 StringUtils.join(items,','); 

FWIW,Apache Commons中的join()方法 (从3232行开始)在if循环中使用if:

 public static String join(Object[] array, char separator, int startIndex, int endIndex) { if (array == null) { return null; } int bufSize = (endIndex - startIndex); if (bufSize <= 0) { return EMPTY; } bufSize *= ((array[startIndex] == null ? 16 : array[startIndex].toString().length()) + 1); StringBuilder buf = new StringBuilder(bufSize); for (int i = startIndex; i < endIndex; i++) { if (i > startIndex) { buf.append(separator); } if (array[i] != null) { buf.append(array[i]); } } return buf.toString(); }