Equivalentes Java de C # String.Format () e String.Join ()

111

Eu sei que esta é uma pergunta um pouco para iniciantes, mas existem equivalentes às operações de string do C # em Java?

Especificamente, estou falando sobre String.Formate String.Join.

Omar Kooheji
fonte
Portanto, há um String.format, mas terei que lançar minha própria junção.
Omar Kooheji,
1
Para join (), gosto desta resposta: stackoverflow.com/a/6116469/562139
scorpiodawg

Respostas:

92

O objeto Java String tem um formatmétodo (a partir de 1.5), mas nenhum joinmétodo.

Para obter vários métodos de utilitário String úteis ainda não incluídos, você pode usar org.apache.commons.lang.StringUtils .

Grant Wagner
fonte
13
coleções do google: google-collections.googlecode.com também tem um Joiner.
Ron,
10
Para sua informação, sobre o comentário de Ron, as coleções do google foram renomeadas para Guava há um tempo.
Kevin Bourrillion 01 de
3
Você deve atualizar esta resposta para refletir que o Java 8 apresenta um String.join()método.
Duncan Jones
46

String.format . Para participar, você precisa escrever o seu próprio:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

O texto acima vem de http://snippets.dzone.com/posts/show/91

Allain Lalonde
fonte
6
Mais precisamente: StringBuffer para jdk1.4 e abaixo, StringBuilder para jdk1.5 e posteriores, já que o último não está sincronizado, portanto, um pouco mais rápido.
VonC,
2
Em vez de duas invocações iter.hasNext (), geralmente acrescento delimeter e, em seguida, "return buf.substring (0, buf.length () - delimeter.length ())".
Vilmantas Baranauskas,
2
Você pode simplificá-lo um pouco saindo mais cedo para evitar o delimitador: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren
29

A partir do Java 8, join()agora está disponível como dois métodos de classe na classe String. Em ambos os casos, o primeiro argumento é o delimitador.

Você pode passar CharSequences individuais como argumentos adicionais :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

Ou você pode passar umIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 também adiciona uma nova classe StringJoiner, que você pode usar assim:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"
qntm
fonte
12

Você também pode usar argumentos variáveis ​​para strings da seguinte maneira:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }
sgsweb
fonte
4

Quanto à adesão, acredito que isso possa parecer um pouco menos complicado:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

Não consigo usar a sintaxe Java 5 tanto quanto gostaria (acredite ou não, tenho usado 1.0.x ultimamente), então posso estar um pouco enferrujado, mas tenho certeza de que o conceito está correto .

adição de edição: acréscimos de string podem ser lentos, mas se você estiver trabalhando em código GUI ou alguma rotina de execução curta, realmente não importa se você leva 0,005 segundos ou 0,006, então se você tinha uma coleção chamada "joinMe" que você deseja anexar a uma string existente "target", não seria horrível apenas embutir isto:

for(String s : joinMe)
    target += s;

É bastante ineficiente (e um mau hábito), mas não é nada que você seja capaz de perceber, a menos que haja milhares de strings ou que esteja dentro de um loop enorme ou que seu código seja realmente crítico para o desempenho.

Mais importante, é fácil de lembrar, curto, rápido e muito legível. O desempenho nem sempre é o vencedor automático nas escolhas de design.

Bill K
fonte
O loop for está correto, mas você também deve torná-lo uma Coleção <String>. Caso contrário, você teria que dizer "for (Object o: c)", porque você não poderia garantir que tudo em c era uma String.
Michael Myers
Bom ponto, estou ainda mais enferrujado com os genéricos. Vou editá-lo em.
Bill K,
Ao concatear sequencialmente: string + string + string, o compilador usa StringBuilder para acrescentar os valores. Eu me pergunto se no método for-loop você tem o segundo lugar, se o compilador faria o mesmo.
Spencer Kormos,
@Spencer K: Ele usa um StringBuilder, mas cria um novo para cada iteração (dificilmente o método mais eficiente, mas como o compilador deveria saber?). Não sei se o compilador JIT pode otimizar isso em tempo de execução.
Michael Myers
2
Oh espere. Você está falando + =? Sim, isso é péssimo. O loop e append atualmente em minha resposta usa StringBuilder e é disso que estou falando. Embora + = tenha melhorado muito, você realmente não quer usá-lo dentro de um loop - não tanto que seja bugado, mas que pode funcionar como uma merda (Bug não é um termo geralmente usado para problemas de desempenho - pelo menos não em meus 20 anos de programação). Além disso, o JIT pode melhorar isso imensamente - mas eu não confiaria nisso.
Bill K
4

Aqui está uma resposta muito simples. Use +=uma vez que tem menos código e deixe o otimizador convertê-lo em um StringBuilderpara você. Usando esse método, você não precisa fazer nenhuma verificação "é a última" em seu loop (melhoria de desempenho) e não precisa se preocupar em remover quaisquer delimitadores no final.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }
Jess
fonte
1
Solução muito elegante, não envolvendo bibliotecas de terceiros!
Denis Itskovich
2

Eu não queria importar uma biblioteca Apache inteira para adicionar uma função de junção simples, então aqui está meu hack.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }
Martin Konecny
fonte
@MrSnowflake O construtor de string padrão tem um método "removeCharAt (int index)" Não está aparecendo na versão que estou executando
NSjonas
@NSjonas - sim, a edição dele para minha resposta está errada. O removeCharAtnão existe e a função inteira não retorna mais uma String ... Corrigirá isso.
Martin Konecny
enquanto você está nisso ... esta solução atual lançará uma "exceção de índice fora dos limites se você passar em uma lista vazia"
NSjonas
fez uma edição para obter o comprimento correto do corte se o delim tiver mais de 1 caractere. Também foi corrigido um problema em que você estava cortando o último caractere e não o primeiro como realmente precisava
NSjonas
Obrigado pelo feedback. Atualizada.
Martin Konecny
1

Se você deseja unir (concatenar) várias strings em uma, você deve usar um StringBuilder. É muito melhor do que usar

for(String s : joinMe)
    target += s;

Há também uma pequena vitória de desempenho sobre StringBuffer, já que StringBuilder não usa sincronização.

Para um método utilitário de propósito geral como este, ele (eventualmente) será chamado muitas vezes em muitas situações, portanto, você deve torná-lo eficiente e não alocar muitos objetos transitórios. Fizemos o perfil de muitos aplicativos Java diferentes e quase sempre descobrimos que a concatenação de string e as alocações de string / char [] ocupam uma quantidade significativa de tempo / memória.

Nossa coleção reutilizável -> método string primeiro calcula o tamanho do resultado necessário e, em seguida, cria um StringBuilder com esse tamanho inicial; isso evita duplicação / cópia desnecessária do char interno [] usado ao anexar strings.

djb
fonte
re: pré-cálculo do tamanho: quando funciona, é ótimo. Nem sempre é possível. Lembro-me de ter lido em algum lugar que dobrar o tamanho do buffer a cada vez (ou realmente multiplicar por qualquer fator fixo) dá algo como n log n desempenho, o que não é realmente tão ruim. No caso geral, a alternativa é fazer uma lista de todas as strings que você planeja concatenar. Se você usar um ArrayList, terá de copiar novamente (a menos que você saiba o comprimento de sua sequência com antecedência) e se usar LinkedList, ele usa mais memória ram e coleta de lixo com os objetos de nó. Às vezes você não pode vencer. Tente!
Ian
Os exemplos / consultas acima presumiram alguma coleção ordenada ou matriz de Strings a serem unidas. Além disso, ao pré-calcular o tamanho, você evita caracteres extras não utilizados que o crescimento interno da matriz char [] geralmente resulta.
djb
1

Eu escrevi:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

mas Collectionnão é compatível com JSP, portanto, para a função de tag, escrevi:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

e .tldarquivado:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

e usá-lo em arquivos JSP como:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}
Gavenkoa
fonte
0

Vejo muitas implementações excessivamente complexas de String.Join aqui. Se você não tiver o Java 1.8 e não quiser importar uma nova biblioteca, a implementação abaixo deve ser suficiente.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}
Brandon Dutton
fonte
-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Então, basicamente, o String ntop armazena o valor de toda a coleção com separadores de vírgula e colchetes.

Edward Karak
fonte
1
Não tenho certeza de que parte da pergunta isso responde? Não é um String.format a menos que você planeje adicionar os bits da string bit a bit ao array, e [1,0.92,3] não é tão versátil quanto uma string genérica .join.
Omar Kooheji de
-8

Eu usaria apenas o operador de concatenação de string "+" para juntar duas strings. s1 += s2;

Amir Bashir
fonte
Porque é uma prática ruim e lenta.
MrSnowflake