我在进行简单的文本处理和打印语句时经常遇到这种情况,我循环遍历一个集合,我想要特殊情况下的最后一个元素(例如,除了最后一个元素之外,每个普通元素都将以逗号分隔情况)。
是否有一些最佳实践习惯或优雅形式,不需要复制代码或推迟if,否则在循环中。
例如,我有一个字符串列表,我想在逗号分隔列表中打印。 (虽然解决方案已经假设列表中有2个或更多元素,否则它就像使用条件的更正确的循环一样糟糕。)
e.g。 List =(“dog”,“cat”,“bat”)
我想打印“[狗,猫,蝙蝠]”
我提出了两种方法
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;
}
while循环启动循环
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;
}
测试员类:
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(", ");
}
}
答案 0 :(得分:43)
我通常这样写:
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();
}
答案 1 :(得分:24)
这些答案中有很多for循环,但我发现Iterator和while循环读取起来要容易得多。 E.g:
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在Google集合中采用的方法,我发现它非常易读。
答案 2 :(得分:14)
string value = "[" + StringUtils.join( items, ',' ) + "]";
答案 3 :(得分:7)
我通常的做法是测试索引变量是否为零,例如:
var result = "[ ";
for (var i = 0; i < list.length; ++i) {
if (i != 0) result += ", ";
result += list[i];
}
result += " ]";
但是,当然,只有当我们谈论没有一些Array.join(“,”)方法的语言时才会这样。 ; - )
答案 4 :(得分:6)
我认为将第一个元素视为特殊情况更容易,因为更容易知道迭代是第一个而不是最后一个。它不需要任何复杂或昂贵的逻辑来知道第一次是否正在做某事。
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;
}
答案 5 :(得分:2)
我喜欢为第一项使用标志。
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 += "]";
答案 6 :(得分:2)
Java 8解决方案,以防有人正在寻找它:
String res = Arrays.stream(items).reduce((t, u) -> t + "," + u).get();
答案 7 :(得分:2)
我会选择你的第二个例子,即。处理循环外的特殊情况,只需写得更简单一点:
String itemOutput = "[";
if (items.length > 0) {
itemOutput += items[0];
for (int i = 1; i < items.length; i++) {
itemOutput += ", " + items[i];
}
}
itemOutput += "]";
答案 8 :(得分:2)
由于您的案例只是处理文本,因此您不需要循环内的条件。一个例子:
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] = ']';
不是添加条件以避免生成尾随逗号,而是继续生成它(以保持循环简单且无条件)并在最后简单地覆盖它。很多时候,我发现生成特殊情况就像任何其他循环迭代更清楚,然后在最后手动替换它(尽管如果“替换它”代码多于几行,这种方法实际上可能变得更难读取)。
答案 9 :(得分:1)
在这种情况下,您实际上是使用某个分隔符字符串连接字符串列表。你也许可以自己写一些这样的东西。然后你会得到类似的东西:
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()
方法检查这是否是最后一个。
答案 10 :(得分:1)
如果要像这样动态构建字符串,则不应使用+ =运算符。
StringBuilder
类对于重复的动态字符串连接效果更好。
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());
答案 11 :(得分:1)
一般来说,我最喜欢的是多级退出。变化
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
就是这种情况。
答案 12 :(得分:1)
我认为这个问题有两个答案:任何语言中这个问题的最佳成语,以及java中这个问题的最佳习语。我也认为这个问题的目的不是将字符串连接在一起的任务,而是一般的模式,所以它显示的库函数并没有真正帮助。
首先,用[]包围字符串并创建用逗号分隔的字符串的动作是两个独立的动作,理想情况下是两个独立的动作。
对于任何语言,我认为递归和模式匹配的组合效果最好。例如,在haskell中,我会这样做:
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]"
这样编写它的好处是它清楚地列举了函数将面临的不同情况,以及它将如何处理它。
另一种非常好的方法是使用累加器类型函数。例如:
join [] = ""
join strings = foldr1 (\a b -> 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无法使用这两种方法。所以在这种情况下,我认为最好的方法是在函数顶部检查异常情况(0或1个元素),然后使用for循环来处理包含多个元素的情况:
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();
}
此功能清楚地显示了两个边缘情况(0或1个元素)中发生的情况。然后它为除了最后一个元素之外的所有元素使用循环,最后在没有逗号的情况下添加最后一个元素。在开始时处理非逗号元素的相反方式也很容易。
请注意,if (items.length == 1) return items[0];
行实际上并不是必需的,但我认为它使得函数更容易一目了然。
(请注意,如果有人想要更多关于haskell / c#函数的解释请问我将其添加进去)
答案 13 :(得分:1)
...
String[] items = { "dog", "cat", "bat" };
String res = "[";
for (String s : items) {
res += (res.length == 1 ? "" : ", ") + s;
}
res += "]";
左右是可读的。当然,您可以将条件放在单独的if
子句中。它的惯用性(至少我认为是这样)是它使用foreach循环并且不使用复杂的循环头。
此外,没有逻辑重复(即只有一个地方items
的项目实际上附加到输出字符串 - 在现实世界的应用程序中,可能更多复杂而冗长的格式化操作,所以我不想重复代码。)
答案 14 :(得分:1)
可以使用Java 8 lambda和Collectors.joining()作为 -
来实现List<String> items = Arrays.asList("dog", "cat", "bat");
String result = items.stream().collect(Collectors.joining(", ", "[", "]"));
System.out.println(result);
答案 15 :(得分:0)
我通常会像这样写一个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();
}
答案 16 :(得分:0)
如果你只是在寻找一个像这样的逗号分隔列表:“[The,Cat,in,the,Hat]”,甚至不要浪费时间编写自己的方法。只需使用List.toString:
List<String> strings = Arrays.asList("The", "Cat", "in", "the", "Hat);
System.out.println(strings.toString());
如果List的泛型类型具有要显示的值的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);
你会得到: [弗兰克,哈尔]
答案 17 :(得分:0)
第三种选择是以下
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() method(第3232行以后)确实在循环中使用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();
}