Jaki jest najlepszy sposób na zbudowanie ciąg oddzielonych przedmiotów w Java?

głosy
240

Podczas pracy w aplikacji Java, niedawno potrzebne do zmontowania listę oddzielonych przecinkami wartości przejść do innego serwisu WWW bez wiedząc, jak wiele elementów nie byłoby z góry. Najlepszym mogę wymyślić od szczytu mojej głowie było coś takiego:

public String appendWithDelimiter( String original, String addition, String delimiter ) {
    if ( original.equals(  ) ) {
        return addition;
    } else {
        return original + delimiter + addition;
    }
}

String parameterString = ;
if ( condition ) parameterString = appendWithDelimiter( parameterString, elementName, , );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, anotherElementName, , );

Zdaję sobie sprawę, to nie jest szczególnie efektywne, ponieważ istnieją ciągi tworzone w każdym miejscu, ale ja jechałem dla jasności większej niż optymalizacji.

W Ruby, mogę zrobić coś takiego zamiast, który czuje się znacznie bardziej eleganckie:

parameterArray = [];
parameterArray << elementName if condition;
parameterArray << anotherElementName if anotherCondition;
parameterString = parameterArray.join(,);

Ale ponieważ Java brakuje polecenia przyłączenia, nie mogłem zorientować się równowartość cokolwiek.

Więc, co jest najlepszym sposobem, aby to zrobić w Java?

Utwórz 15/09/2008 o 15:02
źródło użytkownik
W innych językach...                            


35 odpowiedzi

głosy
423

Pre Java 8:

Apache Commons lang jest Twój przyjaciel tutaj - to jest sposób dołączenia bardzo podobną do tej, którą można znaleźć w Ruby:

StringUtils.join(java.lang.Iterable,char)


Java 8:

Java 8 zapewnia połączenia z pudełka poprzez StringJoinera String.join(). Poniższe fragmenty pokazują, w jaki sposób można z nich korzystać:

StringJoiner

StringJoiner joiner = new StringJoiner(",");
joiner.add("01").add("02").add("03");
String joinedString = joiner.toString(); // "01,02,03"

String.join(CharSequence delimiter, CharSequence... elements))

String joinedString = String.join(" - ", "04", "05", "06"); // "04 - 05 - 06"

String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)

List<String> strings = new LinkedList<>();
strings.add("Java");strings.add("is");
strings.add("cool");
String message = String.join(" ", strings);
//message returned is: "Java is cool"
Odpowiedział 15/09/2008 o 15:13
źródło użytkownik

głosy
49

Można napisać trochę dołączyć stylu metodę narzędziowy, który działa na java.util.Lists

public static String join(List<String> list, String delim) {

    StringBuilder sb = new StringBuilder();

    String loopDelim = "";

    for(String s : list) {

        sb.append(loopDelim);
        sb.append(s);            

        loopDelim = delim;
    }

    return sb.toString();
}

Następnie używać go tak:

    List<String> list = new ArrayList<String>();

    if( condition )        list.add("elementName");
    if( anotherCondition ) list.add("anotherElementName");

    join(list, ",");
Odpowiedział 15/09/2008 o 15:15
źródło użytkownik

głosy
41

W przypadku Androida, klasa StringUtils ze świetlicy nie jest dostępny, więc dla tego użyłem

android.text.TextUtils.join(CharSequence delimiter, Iterable tokens)

http://developer.android.com/reference/android/text/TextUtils.html

Odpowiedział 15/05/2011 o 23:16
źródło użytkownik

głosy
31

Przez biblioteki Guava Google'a ma com.google.common.base.Joiner klasę, która pomaga rozwiązać takie zadania.

próbki:

"My pets are: " + Joiner.on(", ").join(Arrays.asList("rabbit", "parrot", "dog")); 
// returns "My pets are: rabbit, parrot, dog"

Joiner.on(" AND ").join(Arrays.asList("field1=1" , "field2=2", "field3=3"));
// returns "field1=1 AND field2=2 AND field3=3"

Joiner.on(",").skipNulls().join(Arrays.asList("London", "Moscow", null, "New York", null, "Paris"));
// returns "London,Moscow,New York,Paris"

Joiner.on(", ").useForNull("Team held a draw").join(Arrays.asList("FC Barcelona", "FC Bayern", null, null, "Chelsea FC", "AC Milan"));
// returns "FC Barcelona, FC Bayern, Team held a draw, Team held a draw, Chelsea FC, AC Milan"

Oto artykuł o użyteczności smyczkowych guawy jest .

Odpowiedział 18/09/2012 o 07:55
źródło użytkownik

głosy
22

W Javie 8 można użyć String.join():

List<String> list = Arrays.asList("foo", "bar", "baz");
String joined = String.join(" and ", list); // "foo and bar and baz"

Mają również do obejrzenia tej odpowiedzi dla przykładu Stream API.

Odpowiedział 22/03/2014 o 13:30
źródło użytkownik

głosy
20

Można go uogólniać, ale nie ma dołączyć w Javie, jak dobrze powiedzieć.

To może działać lepiej.

public static String join(Iterable<? extends CharSequence> s, String delimiter) {
    Iterator<? extends CharSequence> iter = s.iterator();
    if (!iter.hasNext()) return "";
    StringBuilder buffer = new StringBuilder(iter.next());
    while (iter.hasNext()) buffer.append(delimiter).append(iter.next());
    return buffer.toString();
}
Odpowiedział 15/09/2008 o 15:07
źródło użytkownik

głosy
15

Stosowanie podejścia opartego na java.lang.StringBuilder! ( „A zmienny ciąg znaków”).

Tak jak wspomniano, wszystkie te powiązań, smyczkowych tworzą ciągi całego. StringBuilderNie zrobi tego.

Dlaczego StringBuilderzamiast StringBuffer? Z StringBuilderjavadoc:

Jeżeli jest to możliwe, zaleca się, że ta klasa pierwszeństwo do StringBuffer jak to będzie szybciej w większości wdrożeń.

Odpowiedział 15/09/2008 o 15:05
źródło użytkownik

głosy
11

8 w Javie można zrobić to tak:

list.stream().map(Object::toString)
                        .collect(Collectors.joining(delimiter));

jeśli lista zawiera wartości null można użyć:

list.stream().map(String::valueOf)
                .collect(Collectors.joining(delimiter))
Odpowiedział 22/12/2014 o 09:23
źródło użytkownik

głosy
10

Użyłbym Kolekcje Google. Jest miły Dołącz siłownia.
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

Ale jeśli chciałem napisać to na własną rękę,

package util;

import java.util.ArrayList;
import java.util.Iterable;
import java.util.Collections;
import java.util.Iterator;

public class Utils {
    // accept a collection of objects, since all objects have toString()
    public static String join(String delimiter, Iterable<? extends Object> objs) {
        if (objs.isEmpty()) {
            return "";
        }
        Iterator<? extends Object> iter = objs.iterator();
        StringBuilder buffer = new StringBuilder();
        buffer.append(iter.next());
        while (iter.hasNext()) {
            buffer.append(delimiter).append(iter.next());
        }
        return buffer.toString();
    }

    // for convenience
    public static String join(String delimiter, Object... objs) {
        ArrayList<Object> list = new ArrayList<Object>();
        Collections.addAll(list, objs);
        return join(delimiter, list);
    }
}

Myślę, że działa lepiej z kolekcji obiektów, ponieważ teraz nie trzeba konwertować obiekty ciągów zanim je dołączyć.

Odpowiedział 15/09/2008 o 15:21
źródło użytkownik

głosy
8

Apache Commons StringUtils klasa ma metodę przyłączyć.

Odpowiedział 15/09/2008 o 15:17
źródło użytkownik

głosy
4

Java 8

stringCollection.stream().collect(Collectors.joining(", "));
Odpowiedział 31/10/2015 o 11:38
źródło użytkownik

głosy
3

Zastosowanie StringBuilder i klasa Separator

StringBuilder buf = new StringBuilder();
Separator sep = new Separator(", ");
for (String each : list) {
    buf.append(sep).append(each);
}

Separator owija separatora. Separator jest zwracany przez separator w toStringsposób, chyba że na pierwsze wezwanie, która zwraca pusty łańcuch!

Kod źródłowy dla klasy Separator

public class Separator {

    private boolean skipFirst;
    private final String value;

    public Separator() {
        this(", ");
    }

    public Separator(String value) {
        this.value = value;
        this.skipFirst = true;
    }

    public void reset() {
        skipFirst = true;
    }

    public String toString() {
        String sep = skipFirst ? "" : value;
        skipFirst = false;
        return sep;
    }

}
Odpowiedział 17/11/2008 o 23:07
źródło użytkownik

głosy
3

Można użyć Javy StringBuildertyp na to. Jest też StringBuffer, ale zawiera dodatkową logikę bezpieczeństwa wątku, który jest często zbędne.

Odpowiedział 15/09/2008 o 15:06
źródło użytkownik

głosy
2

Jeśli używasz MVC wiosny to można spróbować następujących czynności.

import org.springframework.util.StringUtils;

List<String> groupIds = new List<String>;   
groupIds.add("a");    
groupIds.add("b");    
groupIds.add("c");

String csv = StringUtils.arrayToCommaDelimitedString(groupIds.toArray());

Będzie to prowadzić do a,b,c

Odpowiedział 25/04/2016 o 11:55
źródło użytkownik

głosy
2

Oraz minimalny jeden (jeśli nie chcą zawierać Apache Commons lub Gauva do projektu Zależności tylko przez wzgląd na łączenie ciągów)

/**
 *
 * @param delim : String that should be kept in between the parts
 * @param parts : parts that needs to be joined
 * @return  a String that's formed by joining the parts
 */
private static final String join(String delim, String... parts) {
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < parts.length - 1; i++) {
        builder.append(parts[i]).append(delim);
    }
    if(parts.length > 0){
        builder.append(parts[parts.length - 1]);
    }
    return builder.toString();
}
Odpowiedział 26/03/2014 o 12:21
źródło użytkownik

głosy
2

Jeśli używasz Eclipse Kolekcje , można użyć makeString()albo appendString().

makeString()zwraca Stringreprezentację, podobny do toString().

Posiada trzy formy

  • makeString(start, separator, end)
  • makeString(separator) domyślnie rozpocząć i zakończyć na pustych strunach
  • makeString()Ustawienia domyślne separujące ", "(przecinek i przestrzeni)

Przykładowy kod:

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
assertEquals("[1/2/3]", list.makeString("[", "/", "]"));
assertEquals("1/2/3", list.makeString("/"));
assertEquals("1, 2, 3", list.makeString());
assertEquals(list.toString(), list.makeString("[", ", ", "]"));

appendString()jest podobny do makeString(), ale nie dodaje się Appendable(na przykład StringBuilder) i jest void. Ma te same trzy formy z dodatkową pierwszego argumentu Appendable.

MutableList<Integer> list = FastList.newListWith(1, 2, 3);
Appendable appendable = new StringBuilder();
list.appendString(appendable, "[", "/", "]");
assertEquals("[1/2/3]", appendable.toString());

Jeśli nie można przekonwertować swoją kolekcję do typu Eclipse Collections, po prostu dostosować go z odpowiednim adapterem.

List<Object> list = ...;
ListAdapter.adapt(list).makeString(",");

Uwaga: Jestem committer dla zbiorów Eclipse.

Odpowiedział 16/11/2013 o 03:15
źródło użytkownik

głosy
2

Dlaczego nie napisać własny przyłączenia () metoda? zajęłoby jako parametry zbierania smyczki i ciąg ogranicznika. W ramach metody iteracyjne nad kolekcji i budować swoją pozycję w StringBuffer.

Odpowiedział 15/09/2008 o 15:06
źródło użytkownik

głosy
1

Dla tych, którzy są w kontekście wiosennego ich StringUtils klasa jest przydatna, a także:

Istnieje wiele przydatnych skrótów, takich jak:

  • collectionToCommaDelimitedString (Collection coll)
  • collectionToDelimitedString (Collection coll, ogranicznik String)
  • arrayToDelimitedString (Object [] ARR ogranicznik string)

i wiele innych.

Może to być pomocne, jeśli nie korzysta już z Java 8 i już jesteś w kontekście wiosennego.

Wolę ją przed Commons Apache (chociaż również bardzo dobre) za wsparcie Collection, która jest łatwiejsza w ten sposób:

// Encoding Set<String> to String delimited 
String asString = org.springframework.util.StringUtils.collectionToDelimitedString(codes, ";");

// Decoding String delimited to Set
Set<String> collection = org.springframework.util.StringUtils.commaDelimitedListToSet(asString);
Odpowiedział 21/01/2015 o 09:44
źródło użytkownik

głosy
1

Z Java 5 args zmiennej, więc nie mają do rzeczy wszystkie sznurki do kolekcji lub tablicy extenso:

import junit.framework.Assert;
import org.junit.Test;

public class StringUtil
{
    public static String join(String delim, String... strings)
    {
        StringBuilder builder = new StringBuilder();

        if (strings != null)
        {
            for (String str : strings)
            {
                if (builder.length() > 0)
                {
                    builder.append(delim).append(" ");
                }
                builder.append(str);
            }
        }           
        return builder.toString();
    }
    @Test
    public void joinTest()
    {
        Assert.assertEquals("", StringUtil.join(",", null));
        Assert.assertEquals("", StringUtil.join(",", ""));
        Assert.assertEquals("", StringUtil.join(",", new String[0]));
        Assert.assertEquals("test", StringUtil.join(",", "test"));
        Assert.assertEquals("foo, bar", StringUtil.join(",", "foo", "bar"));
        Assert.assertEquals("foo, bar, x", StringUtil.join(",", "foo", "bar", "x"));
    }
}
Odpowiedział 16/09/2008 o 20:36
źródło użytkownik

głosy
1

Dlaczego nie można zrobić w Javie samo robisz w Ruby, który tworzy ogranicznik oddzielone ciąg dopiero po dodaniu wszystkich elementów do tablicy?

ArrayList<String> parms = new ArrayList<String>();
if (someCondition) parms.add("someString");
if (anotherCondition) parms.add("someOtherString");
// ...
String sep = ""; StringBuffer b = new StringBuffer();
for (String p: parms) {
    b.append(sep);
    b.append(p);
    sep = "yourDelimiter";
}

Może chcesz przenieść że dla pętli w oddzielnej metody pomocnika, a także używać StringBuilder zamiast StringBuffer ...

Edit : ustalona kolejność dopisuje.

Odpowiedział 15/09/2008 o 15:10
źródło użytkownik

głosy
1

Powinieneś użyć StringBuilderz appendmetodą skonstruować swój wynik, ale poza tym to jest tak dobre roztworu jak Java ma do zaoferowania.

Odpowiedział 15/09/2008 o 15:10
źródło użytkownik

głosy
0

Język Java 8 Rodzaj

List<Integer> example;
example.add(1);
example.add(2);
example.add(3);
...
example.stream().collect(Collectors.joining(","));

Java 8 niestandardowego obiektu:

List<Person> person;
...
person.stream().map(Person::getAge).collect(Collectors.joining(","));
Odpowiedział 16/04/2018 o 15:10
źródło użytkownik

głosy
0

Ja osobiście bardzo często korzystają następujące proste rozwiązanie dla celów rejestrowania:

List lst = Arrays.asList("ab", "bc", "cd");
String str = lst.toString().replaceAll("[\\[\\]]", "");
Odpowiedział 21/08/2016 o 17:46
źródło użytkownik

głosy
0

Lekka poprawa [prędkość] od wersji z IZB:

public static String join(String[] strings, char del)
{
    StringBuilder sb = new StringBuilder();
    int len = strings.length;

    if(len > 1) 
    {
       len -= 1;
    }else
    {
       return strings[0];
    }

    for (int i = 0; i < len; i++)
    {
       sb.append(strings[i]).append(del);
    }

    sb.append(strings[i]);

    return sb.toString();
}
Odpowiedział 26/07/2010 o 20:19
źródło użytkownik

głosy
0

stosując Dolar jest wpisanie:

String joined = $(aCollection).join(",");

Uwaga: to działa również na tablicy i innych typów danych

Realizacja

Wewnętrznie używa bardzo schludny sztuczkę:

@Override
public String join(String separator) {
    Separator sep = new Separator(separator);
    StringBuilder sb = new StringBuilder();

    for (T item : iterable) {
        sb.append(sep).append(item);
    }

    return sb.toString();
}

klasa Separatorzwróci pusty ciąg dopiero po raz pierwszy, że jest wywoływany, a następnie wraca do separatora:

class Separator {

    private final String separator;
    private boolean wasCalled;

    public Separator(String separator) {
        this.separator = separator;
        this.wasCalled = false;
    }

    @Override
    public String toString() {
        if (!wasCalled) {
            wasCalled = true;
            return "";
        } else {
            return separator;
        }
    }
}
Odpowiedział 01/02/2010 o 20:24
źródło użytkownik

głosy
0

Więc kilka rzeczy, które możesz zrobić, aby poczuć, że wydaje się, że szukasz:

1) Extend klasa List - i dodać metodę dołączenia do niego. Sposób przyłączenia po prostu wykonać pracę łącząc i dodając separator (co mogłoby być param metody join)

2) To wygląda jak Java 7 będzie dodanie metody rozszerzenie do java - co pozwala po prostu dołączyć do konkretnej metody do klasy: tak można napisać, że dołączyć metody i dodać go jako metodę rozszerzenia do listy lub nawet Kolekcja.

Rozwiązanie 1 to prawdopodobnie jedyny realistyczny, teraz, choć od Java 7 nie jest obecnie jeszcze :) Ale to powinno działać dobrze.

Aby korzystać z obu z nich, to po prostu dodać wszystkie elementy do listy lub Collection, jak zwykle, a następnie wywołać nową niestandardową metodę „przyłączenia” im.

Odpowiedział 16/09/2008 o 20:50
źródło użytkownik

głosy
0
//Note: if you have access to Java5+, 
//use StringBuilder in preference to StringBuffer.  
//All that has to be replaced is the class name.  
//StringBuffer will work in Java 1.4, though.

appendWithDelimiter( StringBuffer buffer, String addition, 
    String delimiter ) {
    if ( buffer.length() == 0) {
        buffer.append(addition);
    } else {
        buffer.append(delimiter);
        buffer.append(addition);
    }
}


StringBuffer parameterBuffer = new StringBuffer();
if ( condition ) { 
    appendWithDelimiter(parameterBuffer, "elementName", "," );
}
if ( anotherCondition ) {
    appendWithDelimiter(parameterBuffer, "anotherElementName", "," );
}

//Finally, to return a string representation, call toString() when returning.
return parameterBuffer.toString(); 
Odpowiedział 15/09/2008 o 16:16
źródło użytkownik

głosy
0

Robisz to trochę bardziej skomplikowane niż to musi być. Zacznijmy od końca swojej przykład:

String parameterString = "";
if ( condition ) parameterString = appendWithDelimiter( parameterString, "elementName", "," );
if ( anotherCondition ) parameterString = appendWithDelimiter( parameterString, "anotherElementName", "," );

Dzięki małej zmianie przy użyciu StringBuilder zamiast String, to staje się:

StringBuilder parameterString = new StringBuilder();
if (condition) parameterString.append("elementName").append(",");
if (anotherCondition) parameterString.append("anotherElementName").append(",");
...

Kiedy skończysz (zakładam trzeba sprawdzić kilka innych warunków, jak również), po prostu upewnij się usunąć przecinek zstępujące z polecenia jak poniżej:

if (parameterString.length() > 0) 
    parameterString.deleteCharAt(parameterString.length() - 1);

I wreszcie uzyskać ciąg, który ma z

parameterString.toString();

Można też wymienić „” w ramach drugiego zaproszenia do dołączenia do rodzajowego ciąg ogranicznika, który może być ustawiony na cokolwiek. Jeśli masz listę rzeczy, wiesz, musisz dołączyć (bez warunkowo), można umieścić ten kod wewnątrz metody, która bierze listę ciągów.

Odpowiedział 15/09/2008 o 15:30
źródło użytkownik

głosy
0

Zamiast korzystać ciąg konkatenacji, należy użyć StringBuilder jeżeli kod nie jest gwintowany i StringBuffer jeśli to jest.

Odpowiedział 15/09/2008 o 15:29
źródło użytkownik

głosy
0

Twoje podejście nie jest tak źle, ale należy używać StringBuffer zamiast używania znak +. Znak + ma dużą wadę, że nowa instancja String jest tworzony dla każdej pojedynczej operacji. Im dłuższy ciąg dostaje, tym większy narzut. Więc używając StringBuffer powinien być najszybszy sposób:

public StringBuffer appendWithDelimiter( StringBuffer original, String addition, String delimiter ) {
        if ( original == null ) {
                StringBuffer buffer = new StringBuffer();
                buffer.append(addition);
                return buffer;
        } else {
                buffer.append(delimiter);
                buffer.append(addition);
                return original;
        }
}

Po zakończeniu tworzenia ciąg wystarczy zadzwonić toString () na zwróconej StringBuffer.

Odpowiedział 15/09/2008 o 15:19
źródło użytkownik

głosy
0

Nie wiem, czy to naprawdę jest lepsza, ale przynajmniej jest za pomocą StringBuilder, co może być nieco bardziej wydajne.

Na dole jest bardziej ogólne podejście, czy można zbudować listę parametrów przed wykonaniem jakichkolwiek ograniczającej parametru.

// Answers real question
public String appendWithDelimiters(String delimiter, String original, String addition) {
    StringBuilder sb = new StringBuilder(original);
    if(sb.length()!=0) {
        sb.append(delimiter).append(addition);
    } else {
        sb.append(addition);
    }
    return sb.toString();
}


// A more generic case.
// ... means a list of indeterminate length of Strings.
public String appendWithDelimitersGeneric(String delimiter, String... strings) {
    StringBuilder sb = new StringBuilder();
    for (String string : strings) {
        if(sb.length()!=0) {
            sb.append(delimiter).append(string);
        } else {
            sb.append(string);
        }
    }

    return sb.toString();
}

public void testAppendWithDelimiters() {
    String string = appendWithDelimitersGeneric(",", "string1", "string2", "string3");
}
Odpowiedział 15/09/2008 o 15:17
źródło użytkownik

głosy
0

Więc w zasadzie coś takiego:

public static String appendWithDelimiter(String original, String addition, String delimiter) {

if (original.equals("")) {
    return addition;
} else {
    StringBuilder sb = new StringBuilder(original.length() + addition.length() + delimiter.length());
        sb.append(original);
        sb.append(delimiter);
        sb.append(addition);
        return sb.toString();
    }
}
Odpowiedział 15/09/2008 o 15:15
źródło użytkownik

głosy
0

Można spróbować czegoś takiego:

StringBuilder sb = new StringBuilder();
if (condition) { sb.append("elementName").append(","); }
if (anotherCondition) { sb.append("anotherElementName").append(","); }
String parameterString = sb.toString();
Odpowiedział 15/09/2008 o 15:09
źródło użytkownik

głosy
-1

Fix odpowiedzieć Rob Dickerson.

Jest to łatwiejsze w użyciu:

public static String join(String delimiter, String... values)
{
    StringBuilder stringBuilder = new StringBuilder();

    for (String value : values)
    {
        stringBuilder.append(value);
        stringBuilder.append(delimiter);
    }

    String result = stringBuilder.toString();

    return result.isEmpty() ? result : result.substring(0, result.length() - 1);
}
Odpowiedział 19/03/2015 o 01:30
źródło użytkownik

głosy
-2
public static String join(String[] strings, char del)
{
    StringBuffer sb = new StringBuffer();
    int len = strings.length;
    boolean appended = false;
    for (int i = 0; i < len; i++)
    {
        if (appended)
        {
            sb.append(del);
        }
        sb.append(""+strings[i]);
        appended = true;
    }
    return sb.toString();
}
Odpowiedział 15/09/2008 o 15:09
źródło użytkownik

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more