用逗号分隔列表的最简单方法?


104

用Java逗号分隔列表的最清晰方法是什么?

我知道这样做的几种方法,但是我想知道最好的方法是什么(“最好”是指最清晰和/或最短而不是最有效的方法。

我有一个列表,我想遍历它,打印每个值。我想在每个项目之间打印一个逗号,但不要在最后一个项目之后(也不在第一个项目之前)。

List --> Item ( , Item ) *
List --> ( Item , ) * Item

样品溶液1:

boolean isFirst = true;
for (Item i : list) {
  if (isFirst) {
    System.out.print(i);        // no comma
    isFirst = false;
  } else {
    System.out.print(", "+i);   // comma
  }
}

示例解决方案2-创建一个子列表:

if (list.size()>0) {
  System.out.print(list.get(0));   // no comma
  List theRest = list.subList(1, list.size());
  for (Item i : theRest) {
    System.out.print(", "+i);   // comma
  }
}

样品溶液3:

  Iterator<Item> i = list.iterator();
  if (i.hasNext()) {
    System.out.print(i.next());
    while (i.hasNext())
      System.out.print(", "+i.next());
  }

这些特别对待第一项;而是可以特别对待最后一个。

顺便说一句,这里是如何List toString 实现的(它是从继承AbstractCollection),在Java 1.6:

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

它尽早退出循环以避免最后一项后面的逗号。顺便说一句:这是我第一次记得看到“(本收藏集)”;这是激发它的代码:

List l = new LinkedList();
l.add(l);
System.out.println(l);

我欢迎任何解决方案,即使它们使用意外的库(regexp?);在Java以外的语言也解决方案(例如,我认为Python / Ruby的有点缀作用-怎么?实现)。

澄清:通过库,我的意思是标准Java库。对于其他库,我认为它们与其他语言一起使用,并且想知道它们是如何实现的。

EDIT工具包中提到了一个类似的问题:Java中for循环的最新迭代

还有另一个: 循环中的最后一个元素是否应该单独处理?


Answers:


226

Java 8及更高版本

使用StringJoiner类:

StringJoiner joiner = new StringJoiner(",");
for (Item item : list) {
    joiner.add(item.toString());
}
return joiner.toString();

使用StreamCollectors

return list.stream().
       map(Object::toString).
       collect(Collectors.joining(",")).toString();

Java 7及更早版本

另请参阅#285523

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

1
一种非常令人惊讶的状态存储方式!它几乎是OO多态的。我不喜欢每个循环不必要的分配,但我敢打赌它比if效率更高。大多数解决方案会重复测试,我们知道这是不正确的-尽管效率低下,但它们可能是最清晰的。感谢您的链接!
2009年

1
我喜欢这个,但是因为它很聪明而且令人惊讶,所以我认为它不合适使用(令人惊讶的是不好!)。但是,我无能为力。我仍然不确定。但是,它太短了,只要有注释可以提示您,就很容易解决。
2009年

6
@ 13ren:您和Bob叔叔需要谈论“聪明而令人惊讶”的代码。
Stefan Kendall

我觉得我浪费了几年不知道这个^^;

为什么Java需要将此类任务转换为丑陋的代码?
爱德华多

82
org.apache.commons.lang3.StringUtils.join(list,",");

1
找到了源:svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org / ... join方法有9个重载。基于迭代的方法类似于“样本解决方案3”;基于索引的索引使用:if(i> startIndex){<添加分隔符>}
13ren

我真的很追求实现。将其包装在函数中是个好主意,但实际上,我的定界符有时是换行符,并且每行还缩进到指定的深度。除非... join(list,“ \ n” + indent)...将始终有效吗?对不起,想一想。
13ren

2
我认为,这是最漂亮,最短的解决方案
JesperRønn-Jensen2011年

可能是list.iterator()?
Vanuan

32

Java 8提供了几种新方法来实现此目的:

例:

// Util method for strings and other char sequences
List<String> strs = Arrays.asList("1", "2", "3");
String listStr1 = String.join(",", strs);

// For any type using streams and collectors
List<Object> objs = Arrays.asList(1, 2, 3);
String listStr2 = objs.stream()
    .map(Object::toString)
    .collect(joining(",", "[", "]"));

// Using the new StringJoiner class
StringJoiner joiner = new StringJoiner(", ", "[", "]");
joiner.setEmptyValue("<empty>");
for (Integer i : objs) {
  joiner.add(i.toString());
}
String listStr3 = joiner.toString();

使用流的方法假定为import static java.util.stream.Collectors.joining;


这是恕我直言,如果您使用的是Java 8,则是最佳答案。
2015年


7

如果您使用Spring Framework,则可以使用StringUtils进行操作

public static String arrayToDelimitedString(Object[] arr)
public static String arrayToDelimitedString(Object[] arr, String delim)
public static String collectionToCommaDelimitedString(Collection coll)
public static String collectionToCommaDelimitedString(Collection coll, String delim)

谢谢,但是如何实现呢?
2009年

6

基于Java的List toString实现:

Iterator i = list.iterator();
for (;;) {
  sb.append(i.next());
  if (! i.hasNext()) break;
  ab.append(", ");
}

它使用如下语法:

List --> (Item , )* Item

通过基于最后一个而不是首先,它可以使用相同的测试来检查跳过逗号,以检查列表的结尾。我认为这是非常优雅的,但是我不确定清晰度。



4

有一种使用Java 8达到此目的的不错方法:

List<String> list = Arrays.asList(array);
String joinedString = String.join(",", list);

TextUtils.join(“,”,list);
阿鲁尔·潘甸

3
for(int i=0, length=list.size(); i<length; i++)
  result+=(i==0?"":", ") + list.get(i);

是的,它有点神秘,但是可以替代已经发布的内容。
cherouvim

1
我认为代码看起来不错,恕我直言,它比您的其他答案更漂亮,并且几乎没有“神秘性”。用一种名为Join和您的笑声的方法将其包装起来,但我仍然希望该语言具有解决真正常见问题的更好方法。
塔恩

@tarn:您认为它看起来不错,因为您具有Python / perl背景;)我也喜欢它
cherouvim

3

foreach循环的一种选择是:

StringBuilder sb = new StringBuilder();
for(String s:list){
  if (sb.length()>0) sb.append(",");
  sb.append(s);
}

2
StringBuffer result = new StringBuffer();
for(Iterator it=list.iterator; it.hasNext(); ) {
  if (result.length()>0)
    result.append(", ");
  result.append(it.next());
}

更新:正如Dave Webb在评论中提到的,如果列表中的第一项为空字符串,则可能无法产生正确的结果。


meh ..仍然有if在循环中。
sfossen,2009年

您可以使用foreach循环使其更短。
彼得·劳瑞

如果列表中的第一项是一个空字符串,那将不起作用。
Dave Webb

@戴夫韦伯:是的,谢谢。但是问题没有这样的要求。
cherouvim

@cherovium:问题不是说所有项目都不是空字符串,所以这是一个隐含的要求。如果您要创建CSV文件,则空白字段很常见。
戴夫·韦伯

2

我通常这样做:

StringBuffer sb = new StringBuffer();
Iterator it = myList.iterator();
if (it.hasNext()) { sb.append(it.next().toString()); }
while (it.hasNext()) { sb.append(",").append(it.next().toString()); }

虽然我认为从现在开始按照Java实现进行检查;)


逻辑与示例3相同,我喜欢它,因为它没有做任何不必要的事情。但是,我不知道它是否最清楚。顺便说一句:如果将while放在if内,则效率会略高一些,这可以避免在列表为空时进行两次测试。对我来说,这也变得更加清晰。
2009年

为了天堂,使用StringBuilder
令人生畏的2015年

2

如果可以使用Groovy(在JVM上运行):

def list = ['a', 'b', 'c', 'd']
println list.join(',')

谢谢,但是我对实现感兴趣。如何实施?
2009年

2

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

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

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。

第二编辑:如果您正在处理迭代器:

    List<Integer> list = Arrays.asList(1, 2, 3);
    StringBuilder builder = new StringBuilder();
    for (Iterator it = list.iterator(); it.hasNext();)
        builder.append(it.next()).append(it.hasNext() ? "," : "");

这个和上面的stackoverflow.com/questions/668952/…相似。它简短明了,但是如果您只有一个迭代器,则首先需要进行转换。效率低下,但对于这种方法的清晰性也许值得吗?
2009年

string,字符串连接可在使用StringBuilder的情况下创建一个临时StringBuilder。效率低下。更好的(更喜欢Java,但是运行的代码更好)是“ if(i> 0){builder.append(',');}”,然后是builder.append(array [i]);。
Eddie

是的,如果您查看字节码,那是正确的。我不敢相信如此简单的问题会变得如此复杂。我想知道编译器是否可以在这里进行优化。
朱利安·查斯当

提供了第二个,希望是更好的解决方案。
朱利安·查斯顿

最好将它们分开(这样人们就可以对其中之一进行投票)。
2009年

1

我通常使用与版本3类似的东西。它在c / c ++ / bash / ...下工作良好:P


1

我没有编译它...但是应该可以工作(或接近工作)。

public static <T> String toString(final List<T> list, 
                                  final String delim)
{
    final StringBuilder builder;

    builder = new StringBuilder();

    for(final T item : list)
    {
        builder.append(item);
        builder.append(delim);
    }

    // kill the last delim
    builder.setLength(builder.length() - delim.length());

    return (builder.toString());
}

我喜欢这种方式,我只希望使用其他格式。但要提一个小修正:如果列表为空,这将引发IndexOutOfBounds异常。因此,要么在setLength之前进行检查,要么使用Math.max(0,...)
tb- 2012年

1

这很简短,很清楚,但让我感到不安。适应不同的分隔符也有些尴尬,尤其是在使用String(不是char)的情况下。

for (Item i : list)
  sb.append(',').append(i);
if (sb.charAt(0)==',') sb.deleteCharAt(0);

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


1
StringBuffer sb = new StringBuffer();

for (int i = 0; i < myList.size(); i++)
{ 
    if (i > 0) 
    {
        sb.append(", ");
    }

    sb.append(myList.get(i)); 
}

我发现该代码很容易嵌入到静态实用程序类中,同时也非常易于阅读和理解。除了列表本身,循环和定界符之外,它也不需要任何其他变量或状态。
Joachim H. Skeie 2011年

这将提供以下内容:Item1Item2, Item3, Item4,

为了天堂,使用StringBuilder
令人生畏的2015年

1

我有点喜欢这种方法,这是我前一段时间在博客上发现的。不幸的是,我不记得博客的名称/ URL。

您可以创建如下所示的实用程序/帮助程序类:

private class Delimiter
{
    private final String delimiter;
    private boolean first = true;

    public Delimiter(String delimiter)
    {
        this.delimiter = delimiter;
    }

    @Override
    public String toString()
    {
        if (first) {
            first = false;
            return "";
        }

        return delimiter;
    }
}

使用helper类非常简单,如下所示:

StringBuilder sb = new StringBuilder();
Delimiter delimiter = new Delimiter(", ");

for (String item : list) {
    sb.append(delimiter);
    sb.append(item);
}

请注意:它应该是“定界符”,而不是“定界符”。
迈克尔·迈尔斯

1

由于您的定界符为“,”,因此您可以使用以下任意一种:

public class StringDelim {

    public static void removeBrackets(String string) {
        System.out.println(string.substring(1, string.length() - 1));
    }

    public static void main(String... args) {
        // Array.toString() example
        String [] arr = {"Hi" , "My", "name", "is", "br3nt"};
        String string = Arrays.toString(arr);
        removeBrackets(string);

        // List#toString() example
        List<String> list = new ArrayList<String>();
        list.add("Hi");
        list.add("My");
        list.add("name");
        list.add("is");
        list.add("br3nt");
        string = list.toString();
        removeBrackets(string);

        // Map#values().toString() example
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("1", "Hi");
        map.put("2", "My");
        map.put("3", "name");
        map.put("4", "is");
        map.put("5", "br3nt");
        System.out.println(map.values().toString());
        removeBrackets(string);

        // Enum#toString() example
        EnumSet<Days> set = EnumSet.allOf(Days.class);
        string = set.toString();
        removeBrackets(string);
    }

    public enum Days {
        MON("Monday"),
        TUE("Tuesday"),
        WED("Wednesday"),
        THU("Thursday"),
        FRI("Friday"),
        SAT("Saturday"),
        SUN("Sunday");

        private final String day;

        Days(String day) {this.day = day;}
        public String toString() {return this.day;}
    }
}

如果您的定界符是其他任何内容,那么这对您将不起作用。


1

我喜欢这个解决方案:

String delim = " - ", string = "";

for (String item : myCollection)
    string += delim + item;

string = string.substring(delim.length());

我认为它也可以利用StringBuilder。


1

我认为,这是最容易阅读和理解的:

StringBuilder sb = new StringBuilder();
for(String string : strings) {
    sb.append(string).append(',');
}
sb.setLength(sb.length() - 1);
String result = sb.toString();

0

在Python中很容易

“,”。join(yourlist)

在C#中,String类上有一个静态方法

String.Join(“,”,yourlistofstrings)

抱歉,不确定Java,但以为我会在您询问其他语言时加入。我确定Java中会有类似的东西。


IIRC,您会在.Net版本中看到尾随。:-(

1
刚刚经过测试,.NET或Python中没有尾随定界符
Tarn

1
谢谢!我找到了它的源代码:svn.python.org/view/python/branches/release22-branch/Objects/… 搜索“全部分类”。它省略了最后一项的分隔符。
2009年

@ 13ren做得好!有帮助吗?我锁好了,很快想起了为什么我现在不选择使用C编程:P
tarn

@tarn,是的,这是一个有趣的示例,因为它仅在不是最后一项的情况下才添加逗号:if(i <seqlen-1){<添加分隔符>}
13ren 2009年

0

您也可以无条件地添加定界符字符串,并在循环后最后删除多余的定界符。然后,开头的“如果列表为空,则返回此字符串”将使您避免在末尾进行检查(因为您无法从空白列表中删除字符)

所以问题确实是:

“给定一个循环,如果有的话,您认为将这些组合在一起的最清晰方法是什么?”


0
public static String join (List<String> list, String separator) {
  String listToString = "";

  if (list == null || list.isEmpty()) {
   return listToString;
  }

  for (String element : list) {
   listToString += element + separator;
  }

  listToString = listToString.substring(0, separator.length());

  return listToString;
}

1
我想您是想说listToString.substring(0, listToString.length()-separator.length());
13ren

0
if (array.length>0)          // edited in response Joachim's comment
  sb.append(array[i]);
for (int i=1; i<array.length; i++)
  sb.append(",").append(array[i]);

基于最清晰的方式用逗号分隔列表(Java)?

使用这个想法: 循环中的最后一个元素是否值得单独对待?


1
为此,您需要知道列表中至少有一个元素,否则第一个for循环将因IndexOutOfBoundsException而崩溃。
Joachim H. Skeie 2011年

@Joachim谢谢,您当然是对的。我写的这个丑陋的解决方案!我将更for (int i=0; i<1; i++)改为if (array.length>0)
2011年

0
public String toString(List<Item> items)
{
    StringBuilder sb = new StringBuilder("[");

    for (Item item : items)
    {
        sb.append(item).append(", ");
    }

    if (sb.length() >= 2)
    {
        //looks cleaner in C# sb.Length -= 2;
        sb.setLength(sb.length() - 2);
    }

    sb.append("]");

    return sb.toString();
}

1
刚注意到它类似于TofuBeer给出
Eblis

0

到目前为止,没有答案使用递归...

public class Main {

    public static String toString(List<String> list, char d) {
        int n = list.size();
        if(n==0) return "";
        return n > 1 ? Main.toString(list.subList(0, n - 1), d) + d
                  + list.get(n - 1) : list.get(0);
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList(new String[]{"1","2","3"});
        System.out.println(Main.toString(list, ','));
    }

}

0
private String betweenComma(ArrayList<String> strings) {
    String united = "";
    for (String string : strings) {
        united = united + "," + string;
    }
    return united.replaceFirst(",", "");
}

-1

方法

String join(List<Object> collection, String delimiter){
    StringBuilder stringBuilder = new StringBuilder();
    int size = collection.size();
    for (Object value : collection) {
        size --;
        if(size > 0){
            stringBuilder.append(value).append(delimiter);
        }
    }

    return stringBuilder.toString();
}

用法

给定[1,2,3]的数组

join(myArray, ",") // 1,2,3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.