Odpowiedniki Java C # String.Format () i String.Join ()


Odpowiedzi:


92

Obiekt Java String ma formatmetodę (od 1.5), ale nie ma żadnej joinmetody.

Aby uzyskać kilka przydatnych metod narzędziowych typu String, które nie zostały jeszcze uwzględnione, możesz użyć org.apache.commons.lang.StringUtils .


13
Kolekcje google: google-collections.googlecode.com ma również Joiner.
Ron

10
FYI na komentarz Rona, kolekcje google zostały niedawno przemianowane na Guava.
Kevin Bourrillion

3
Należy zaktualizować tę odpowiedź, aby odzwierciedlić, że Java 8 wprowadza String.join()metodę.
Duncan Jones

46

String.format . Jeśli chodzi o dołączanie, musisz napisać własne:

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

Powyższe pochodzi z http://snippets.dzone.com/posts/show/91


6
Dokładniej: StringBuffer dla jdk1.4 i niższych, StringBuilder dla jdk1.5 i późniejszych, ponieważ ta ostatnia nie jest zsynchronizowana, przez co jest trochę szybsza.
VonC

2
Zamiast dwóch wywołań iter.hasNext () zwykle dołączam delimeter, a następnie „zwracam buf.substring (0, buf.length () - delimeter.length ())”.
Vilmantas Baranauskas

2
Możesz to trochę usprawnić, wychodząc wcześniej, aby uniknąć separatora: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren


29

Od wersji Java 8 join()jest teraz dostępna jako dwie metody klasowe w klasie String. W obu przypadkach ogranicznikiem jest pierwszy argument.

Możesz przekazać poszczególne CharSequenceargumenty jako dodatkowe argumenty :

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

Lub możesz zdaćIterable<? 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 dodaje również nową klasę StringJoiner, której możesz użyć w następujący sposób:

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"


12

Możesz również użyć zmiennych argumentów dla ciągów w następujący sposób:

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

4

Jeśli chodzi o dołączanie, wydaje mi się, że może to wyglądać trochę mniej skomplikowane:

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

Nie mogę używać składni Java 5 tak bardzo, jak bym chciał (wierz lub nie, ostatnio używam 1.0.x), więc może jestem trochę zardzewiały, ale jestem pewien, że koncepcja jest poprawna .

dodawanie do edycji: Dołączanie ciągów może być powolne, ale jeśli pracujesz nad kodem GUI lub jakąś krótką procedurą, naprawdę nie ma znaczenia, czy zajmie ci to 0,005 sekundy czy .006, więc jeśli masz kolekcję o nazwie „joinMe” że chcesz dołączyć do istniejącego ciągu „target”, nie byłoby przerażające, gdybyśmy po prostu wstawili to:

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

Jest to dość nieefektywne (i zły nawyk), ale nic nie będziesz w stanie dostrzec, chyba że istnieją tysiące ciągów lub jest to wewnątrz ogromnej pętli lub twój kod jest naprawdę krytyczny dla wydajności.

Co ważniejsze, jest łatwy do zapamiętania, krótki, szybki i bardzo czytelny. Wydajność nie zawsze jest automatycznym zwycięzcą w wyborach projektowych.


Pętla for jest poprawna, ale należy również ustawić ją jako Collection <String>. Jeśli tego nie zrobisz, musisz powiedzieć „for (Object o: c)”, ponieważ nie możesz zagwarantować, że wszystko w c jest ciągiem.
Michael Myers

Słuszna uwaga, jestem jeszcze bardziej zardzewiały z generykami. Zmontuję to za.
Bill K,

Podczas łączenia inline: string + string + string, kompilator w rzeczywistości używa StringBuilder do dołączenia wartości. Zastanawiam się, czy w metodzie for-loop masz drugie miejsce, czy kompilator zrobiłby to samo.
Spencer Kormos

@Spencer K: Używa StringBuildera, ale tworzy nową dla każdej iteracji (nie jest to najbardziej wydajna metoda, ale skąd kompilator powinien wiedzieć?). Nie wiem, czy kompilator JIT może to zoptymalizować w czasie wykonywania.
Michael Myers

2
Zaczekaj. Czy mówisz + =? Tak, to jest do bani. Pętla i dołączanie aktualnie w mojej odpowiedzi używa StringBuilder i właśnie o tym mówię. Chociaż + = znacznie się poprawiło, naprawdę nie chcesz go używać w pętli - nie tyle, że jest błędny, ale może działać jak gówno (Bug nie jest terminem ogólnie używanym do problemów z wydajnością - przynajmniej nie przez 20 lat programowania). Również JIT może to znacznie poprawić - ale nie polegałbym na tym.
Bill K

4

Oto dość prosta odpowiedź. Używaj, +=ponieważ jest mniej kodu i pozwól optymalizatorowi przekonwertować go na plik StringBuilder. Używając tej metody, nie musisz wykonywać żadnych sprawdzeń „jest ostatnia” w pętli (poprawa wydajności) i nie musisz się martwić usuwaniem na końcu żadnych ograniczników.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }

1
Bardzo eleganckie rozwiązanie, niewymagające bibliotek zewnętrznych!
Denis Itskovich

2

Nie chciałem importować całej biblioteki Apache, aby dodać prostą funkcję łączenia, więc oto mój 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();
        }
    }

@MrSnowflake Czy standardowy konstruktor ciągów ma metodę „removeCharAt (int index)” Nie pojawia się ona w wersji, w której działa
NSjonas

@NSjonas - tak, jego edycja mojej odpowiedzi jest błędna. Element removeCharAtnie istnieje, a cała funkcja nie zwraca już ciągu znaków ... Naprawi to.
Martin Konecny

skoro już to robisz ... to obecne rozwiązanie wyrzuci „indeks poza granicami wyjątku, jeśli przejdziesz do pustej listy”
NSjonas

dokonał edycji, aby uzyskać przycięcie odpowiedniej długości, jeśli separator ma więcej niż 1 znak. Naprawiono również problem
polegający na tym,

Dzięki za opinie. Zaktualizowano.
Martin Konecny

1

Jeśli chcesz połączyć (połączyć) kilka ciągów w jeden, powinieneś użyć StringBuilder. Jest to o wiele lepsze niż używanie

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

Występuje również niewielka przewaga wydajności nad StringBuffer, ponieważ StringBuilder nie używa synchronizacji.

W przypadku metody użyteczności ogólnego przeznaczenia, takiej jak ta, będzie ona (ostatecznie) wywoływana wiele razy w wielu sytuacjach, więc należy ją wydajnie i nie przydzielać wielu obiektów przejściowych. Zaprofilowaliśmy wiele, wiele różnych aplikacji Java i prawie zawsze stwierdzamy, że konkatenacja ciągów i alokacje ciągów / znaków [] zajmują znaczną ilość czasu / pamięci.

Nasza kolekcja wielokrotnego użytku -> metoda string najpierw oblicza rozmiar wymaganego wyniku, a następnie tworzy StringBuilder o tym początkowym rozmiarze; pozwala to uniknąć niepotrzebnego podwajania / kopiowania wewnętrznego znaku char [] używanego podczas dołączania ciągów.


re: wstępne obliczenie rozmiaru: Kiedy to działa, jest świetne. Nie zawsze jest to możliwe. Pamiętam, że czytałem gdzieś, że podwojenie rozmiaru bufora za każdym razem (lub naprawdę pomnożenie przez dowolny ustalony współczynnik) daje coś w rodzaju wydajności n log n, co nie jest wcale takie złe. W ogólnym przypadku alternatywą jest sporządzenie listy wszystkich ciągów, które planujesz połączyć. Jeśli używasz ArrayList, musisz ponownie kopiować (chyba że znasz wcześniej długość swojej sekwencji), a jeśli używasz LinkedList, to używa więcej pamięci RAM i czyszczenia pamięci z obiektami węzłów. Czasami nie możesz wygrać. Spróbuj!
Ian

Powyższe przykłady / zapytania zakładały uporządkowaną kolekcję lub tablicę ciągów do połączenia. Ponadto, obliczając wstępnie rozmiar, unikasz dodatkowych nieużywanych znaków, które zwykle powodują wzrost wewnętrznej tablicy char [].
djb

1

Napisałem własne:

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

ale Collectionnie jest obsługiwany przez JSP, więc dla funkcji tagów napisałem:

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

i wstaw do .tldakt:

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

i używaj go w plikach JSP jako:

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



0

Widzę wiele nadmiernie złożonych implementacji String.Join tutaj. Jeśli nie masz Javy 1.8 i nie chcesz importować nowej biblioteki, poniższa implementacja powinna wystarczyć.

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

-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Zasadniczo String ntop przechowuje wartość całej kolekcji z separatorami przecinkowymi i nawiasami.


1
Nie jestem pewien, na jaką część pytania odpowiada ta odpowiedź? To nie jest String.format, chyba że planujesz dodawać bity ciągu bit po bicie do tablicy, a [1,0.92,3] nie jest tak wszechstronny jak zwykły ciąg .join.
Omar Kooheji

-8

Po prostu użyłbym operatora konkatenacji ciągów „+”, aby połączyć dwa ciągi. s1 += s2;


Ponieważ jest to zła praktyka i powolna.
MrSnowflake
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.