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?

13ren
fonte

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 = ",";
}
kit de ferramentas
fonte
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,",");
troncoc
fonte
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;.

Lii
fonte
Este é IMHO, se você estiver usando java 8, a melhor resposta.
scravy
18
Joiner.on(",").join(myList)

Marceneiro .

Stefan Kendall
fonte
Vim aqui procurando algo que faça exatamente isso. Obrigado! +1
javamonkey79
2
Mesmo. Obrigado por isso. Finalmente, alguém sensato.
TWR Cole de
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)
Henryk Konsek
fonte
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.

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

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

List<String> list = Arrays.asList(array);
String joinedString = String.join(",", list);
Krishnanunni PV
fonte
TextUtils.join (",", lista);
Arul Pandian de
3
for(int i=0, length=list.size(); i<length; i++)
  result+=(i==0?"":", ") + list.get(i);
Cherouvim
fonte
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);
}
BAH
fonte
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.

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

Alex Marshall
fonte
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(',')
kungfoo
fonte
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() ? "," : "");
Julien Chastang
fonte
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

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

13ren
fonte
1
StringBuffer sb = new StringBuffer();

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

    sb.append(myList.get(i)); 
}
Áries McRae
fonte
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);
}
Tobias Müller
fonte
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ê.

br3nt
fonte
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.

br3nt
fonte
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();
SergeyB
fonte
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.

Tarn
fonte
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?"

Thorbjørn Ravn Andersen
fonte
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;
}
user441737
fonte
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?

13ren
fonte
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();
}
Eblis
fonte
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, ','));
    }

}
Langsweirdt
fonte
0
private String betweenComma(ArrayList<String> strings) {
    String united = "";
    for (String string : strings) {
        united = united + "," + string;
    }
    return united.replaceFirst(",", "");
}
Samet ÖZTOPRAK
fonte
-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
Ilya Gazman
fonte