A maneira mais simples de delimitar uma lista por vírgulas?


104

Qual é a maneira mais clara de delimitar uma lista por vírgulas em Java?

Eu conheço várias maneiras de fazer isso, mas estou me perguntando qual é a melhor maneira (onde "melhor" significa mais claro e / ou mais curto, não o mais eficiente.

Tenho uma lista e quero fazer um loop, imprimindo cada valor. Quero imprimir uma vírgula entre cada item, mas não depois do último (nem antes do primeiro).

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

Solução de amostra 1:

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

Solução de amostra 2 - crie uma sublista:

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
  }
}

Solução de amostra 3:

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

Eles tratam o primeiro item de maneira especial; em vez disso, pode-se tratar o último especialmente.

Aliás, aqui está como List toString é implementado (é herdado de AbstractCollection), em 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(", ");
    }
}

Ele sai do loop mais cedo para evitar a vírgula após o último item. BTW: esta é a primeira vez que me lembro de ter visto "(esta coleção)"; aqui está o código para provocá-lo:

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

Congratulo-me com qualquer solução, mesmo que use bibliotecas inesperadas (regexp?); e também soluções em outras linguagens que não Java (por exemplo, eu acho que Python / Ruby tem uma função intercalar - como isso é implementado?).

Esclarecimento : por bibliotecas, quero dizer as bibliotecas Java padrão. Para outras bibliotecas, considero-as com outras linguagens, e tenho interesse em saber como estão implementadas.

O kit de ferramentas EDIT mencionou uma questão semelhante: Última iteração do loop for aprimorado em java

E outro: o último elemento em um loop merece um tratamento separado?


Respostas:


226

Java 8 e posterior

Usando a StringJoinerclasse:

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

Usando Stream, e Collectors:

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

Java 7 e anterior

Veja também # 285523

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

1
Uma maneira muito surpreendente de armazenar estado! É quase OO polimórfico. Não gosto da atribuição desnecessária a cada loop, mas aposto que é mais eficiente do que um if. A maioria das soluções repete um teste que sabemos não ser verdadeiro - embora ineficientes, são provavelmente as mais claras. Obrigado pelo link!
— 13ren

1
Eu gosto disso, mas por ser inteligente e surpreendente, achei que não seria apropriado usar (surpreender não é bom!). No entanto, eu não pude evitar. Ainda não tenho certeza sobre isso; no entanto, é tão curto que é fácil de descobrir, desde que haja um comentário para lhe dar uma dica.
— 13ren

6
@ 13ren: Você e o tio Bob precisam conversar sobre um código "inteligente e surpreendente".
— Stefan Kendall

Eu sinto que perdi anos sem saber disso ^^;
— Lago de

Por que o Java precisa transformar essa tarefa em um código feio?
— Eduardo

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

1
Fonte encontrada: svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/… O método de junção tem 9 sobrecargas. Os baseados em iteração são como "Solução de amostra 3"; os baseados em índice usam: if (i> startIndex) {<adicionar separador>}
— 13ren

No entanto, estou realmente atrás de implementações. É uma boa ideia envolvê-lo em uma função, mas, na prática, meu delimitador às vezes é uma nova linha e cada linha também é recuada para alguma profundidade especificada. A menos que ... join (list, "\ n" + indent) ... isso sempre funcionará? Desculpe, apenas pensando em voz alta.
— 13ren

2
Na minha opinião, esta é a solução mais bonita e curta
— Jesper Rønn-Jensen

provavelmente, list.iterator ()?
— Vanuan

32

Java 8 oferece várias novas maneiras de fazer isso:

Exemplo:

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

A abordagem usando fluxos assume import static java.util.stream.Collectors.joining;.


Este é IMHO, se você estiver usando java 8, a melhor resposta.
— scravy


7

Se você usar o Spring Framework, poderá fazê-lo com 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)

Obrigado, mas como isso é implementado?
— 13ren

6

Com base na implementação List toString do Java:

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

Ele usa uma gramática como esta:

List --> (Item , )* Item

Por ser baseado no último em vez de no primeiro, ele pode verificar a vírgula de salto com o mesmo teste para verificar o fim da lista. Eu acho que este é muito elegante, mas não tenho certeza sobre clareza.


4
String delimiter = ",";
StringBuilder sb = new StringBuilder();
for (Item i : list) {
    sb.append(delimiter).append(i);
}
sb.toString().replaceFirst(delimiter, "");

4

Há uma maneira bonita de fazer isso usando Java 8:

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

TextUtils.join (",", lista);
— Arul Pandian de

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

Sim, é um pouco enigmático, mas uma alternativa ao que já foi postado.
— cherouvim,

1
Acho que o código parece bom, IMHO é mais bonito do que sua outra resposta e dificilmente é 'enigmático'. Enrole em um método chamado Junte-se e sua risada, ainda assim eu esperaria que a linguagem tivesse uma maneira melhor de resolver o que é realmente um problema comum.
— Tarn

@tarn: você acha que parece bom porque tem experiência em Python / perl;) Eu também gosto
— cherouvim,

3

Uma opção para o loop 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());
}

Atualização : Como Dave Webb mencionou nos comentários, isso pode não produzir resultados corretos se os primeiros itens da lista forem strings vazias.


meh .. ainda tem if no loop.
— sfossen,

Você pode usar um loop foreach para torná-lo mais curto.
— Peter Lawrey,

Isso não funcionará se o primeiro item da lista for uma string vazia.
— Dave Webb

@Dave Webb: Sim, obrigado. A questão não tem tal requisito.
— cherouvim

@cherovium: A questão não diz que nenhum dos itens será a string vazia, então é um requisito implícito. Se você estiver criando um arquivo CSV, os campos vazios podem ser muito comuns.
— Dave Webb

2

Eu normalmente faço isso:

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

Embora eu ache que vou fazer essa verificação de agora em diante de acordo com a implementação Java;)


Essa é a mesma lógica do exemplo 3, gosto porque não faz nada desnecessário. No entanto, não sei se é o mais claro. BTW: é um pouco mais eficiente se você colocar o while dentro do if, evitando dois testes quando a lista está vazia. Isso também torna isso um pouco mais claro para mim.
— 13ren,

Pelo amor de Deus, use um StringBuilder
— scravy de

2

Se você pode usar Groovy (que é executado no JVM):

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

obrigado, mas estou interessado em implementações. Como este é implementado?
— 13ren

2

(Copie e cole minha própria resposta daqui .) Muitas das soluções descritas aqui são um pouco exageradas, IMHO, especialmente aquelas que dependem de bibliotecas externas. Há um idioma limpo e claro para obter uma lista separada por vírgulas que sempre usei. Ele depende do operador condicional (?):

Editar : solução original correta, mas não ideal de acordo com os comentários. Tentando uma segunda vez:

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

Aí está, em 4 linhas de código, incluindo a declaração da matriz e o StringBuilder.

2ª Edição : Se você estiver lidando com um Iterador:

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

Este e stackoverflow.com/questions/668952/… acima são semelhantes. É curto e claro, mas se você tiver apenas um iterador, primeiro precisará converter. Ineficiente, mas vale a pena pela clareza dessa abordagem?
— 13ren

Ugh, concatenação de string criando um StringBuilder temporário dentro do uso de um StringBuilder. Ineficiente. Melhor (mais parecido com Java, mas melhor executando código) seria "if (i> 0) {builder.append (',');}" seguido por builder.append (array [i]);
— Eddie

Sim, se você olhar no bytecode, você está certo. Não posso acreditar que uma pergunta tão simples possa ficar tão complicada. Gostaria de saber se o compilador pode otimizar aqui.
— Julien Chastang,

Forneceu a segunda solução, esperançosamente, melhor.
— Julien Chastang,

Pode ser melhor dividi-los (para que as pessoas possam votar em um ou outro).
— 13ren

1

Eu costumo usar algo parecido com a versão 3. Funciona bem c / c ++ / bash / ...: P


1

Não o compilei ... mas deve funcionar (ou estar perto de funcionar).

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

Eu gosto desse jeito, só prefiro outra formatação. Mas para mencionar uma pequena correção: se a lista estiver vazia, isso lançará uma exceção IndexOutOfBounds. Portanto, faça uma verificação antes de setLength ou use Math.max (0, ...)
— tb-

1

Isso é muito curto, muito claro, mas dá à minha sensibilidade os horrores arrepiantes. Também é um pouco estranho se adaptar a diferentes delimitadores, especialmente se for uma String (não char).

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

Inspirado por: Última iteração do loop for aprimorado em java


1
StringBuffer sb = new StringBuffer();

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

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

Acho que este é muito fácil de incorporar a uma classe de utilitário estático, ao mesmo tempo que é muito fácil de ler e entender. Ele também não requer nenhuma variável ou estado extra além da própria lista, o loop e o delimitador.
— Joachim H. Skeie,

Isso fornecerá algo como:Item1Item2, Item3, Item4,

Pelo amor de Deus, use um StringBuilder
— scravy de

1

Gosto um pouco dessa abordagem, que encontrei em um blog há algum tempo. Infelizmente não me lembro do nome / URL do blog.

Você pode criar uma classe de utilitário / auxiliar semelhante a esta:

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

Usar a classe auxiliar é simples assim:

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

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

Apenas uma nota: deve ser "delimitador", não "delimitador".
— Michael Myers

1

Como seu delimitador é "," você pode usar qualquer um dos seguintes:

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

Se o seu delimitador for QUALQUER OUTRA coisa, isso não vai funcionar para você.


1

Gosto desta solução:

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

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

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

Suponho que também possa usar StringBuilder.


1

Na minha opinião, este é o mais simples de ler e entender:

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

0

Em Python é fácil

",". junte-se (sua lista)

Em C #, há um método estático na classe String

String.Join (",", sua lista de strings)

Desculpe, não tenho certeza sobre o Java, mas achei melhor falar quando você perguntou sobre outras linguagens. Tenho certeza de que haveria algo semelhante em Java.


IIRC, você obtém um trailing, na versão .Net. :-(

1
Acabei de testar, sem delimitador final em .NET ou Python
— tarn

1
obrigado! Eu encontrei a fonte: svn.python.org/view/python/branches/release22-branch/Objects/… procure por "Catenate tudo". Omite o separador do último item.
— 13ren

@ 13ren Muito bem! Ajuda? Tive um bloqueio e rapidamente me lembrei por que escolhi não programar em C atualmente: P
— tarn,

@tarn, sim, é um exemplo interessante porque só adiciona o comman se não for o último item: if (i <seqlen - 1) {<adicionar separador>}
— 13ren

0

Você também pode adicionar incondicionalmente a string delimitadora e, após o loop, remover o delimitador extra no final. Então, um "se a lista está vazia, então retorne esta string" no início permitirá que você evite a verificação no final (já que você não pode remover caracteres de uma lista vazia)

Portanto, a questão realmente é:

"Dados um loop e um if, qual você acha que é a maneira mais clara de tê-los juntos?"


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
Acho que você quis dizerlistToString.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]);

Com base na maneira mais clara de delimitar uma lista por vírgulas (Java)?

Usando esta ideia: O último elemento em um loop merece um tratamento separado?


1
Para que isso funcione, você precisa saber que há pelo menos 1 elemento na lista, caso contrário, seu primeiro loop for travará com uma IndexOutOfBoundsException.
— Joachim H. Skeie,

@Joachim obrigado, você está certo, é claro. Que solução horrível eu escrevi! Eu vou mudar for (int i=0; i<1; i++)para if (array.length>0).
— 13ren 10/10/11

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
Acabei de notar que é semelhante à resposta que TofuBeer deu
— Eblis

0

Nenhuma das respostas usa recursão até agora ...

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

Método

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

Uso

Dada a matriz de [1,2,3]

join(myArray, ",") // 1,2,3
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.