Java로 구분 된 항목 문자열을 작성하는 가장 좋은 방법은 무엇입니까?


317

Java 앱에서 작업하는 동안 최근에 쉼표로 구분 된 값 목록을 어셈블하여 얼마나 많은 요소가 사전에 있는지 모르면서 다른 웹 서비스에 전달해야했습니다. 내가 머리 꼭대기에서 내릴 수있는 최선은 다음과 같습니다.

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

나는 모든 곳에서 문자열이 생성되기 때문에 이것이 효율적이지 않다는 것을 알고 있지만 최적화보다 명확성을 추구했습니다.

루비에서는 대신 다음과 같이 할 수 있습니다.

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

그러나 Java에는 join 명령이 없기 때문에 동등한 것을 알아낼 수 없었습니다.

Java에서 이것을 수행하는 가장 좋은 방법은 무엇입니까?


StringbBilder는 java.lang.StringBuilder입니다.
Yusubov December

Java 8의 경우이 답변을 살펴보십시오. stackoverflow.com/a/22577623/1115554
micha

답변:


542

프리 자바 8 :

Apache의 commons lang은 여기에 당신의 친구입니다-그것은 당신이 Ruby에서 참조하는 것과 매우 유사한 join 메소드를 제공합니다 :

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


자바 8 :

자바 (8)을 통해 밖으로 상자의 결합을 제공 StringJoiner하고 String.join(). 아래 스 니펫은 사용 방법을 보여줍니다.

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"

2
궁금합니다-Collection에있는 Object의 String 표현에 구분 문자 자체가 포함되어 있는지 고려합니까?
GreenieMeanie

4
정확히 내가 찾은 것 : org.apache.commons.lang3.StringUtils 패키지의 StringUtils.join (java.util.Collection, String) jar 파일은 commons-lang3-3.0.1.jar
Umar

108
Android에서는 TextUtils.join ()도 사용할 수 있습니다.
James Wald

3
가장 좋은 방법은 아닙니다. Collection의 Object가 null / empty 인 경우에도 항상 char를 추가합니다. 멋지고 깨끗하지만 때로는 이중 구분 문자를 인쇄합니다.
Stephan Schielke

52

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

그런 다음 다음과 같이 사용하십시오.

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

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

    join(list, ",");

2
적어도 두 가지 구현 (apache 및 guava)이 이미 존재하는 경우 왜 자신의 방법을 작성해야합니까?
Timofey

29
예를 들어, 외부 종속성을 줄이고 자하는 경우 유용 할 수 있습니다.
Thomas Zumbrunn

2
조건 대신 loopDelim을 사용하는 방법과 비슷합니다. 그것은 일종의 해킹이지만 사이클에서 조건문을 제거합니다. 나는 여전히 반복자를 사용하고주기 전에 첫 번째 요소를 추가하는 것을 선호하지만 실제로 좋은 해킹입니다.
Vlasec

List<String>intitializer Iterator<?>에서 동일한 효과 로 변경할 수 없습니까?
k_g

@Tim Eg 아파치는 빈 문자열을 건너 뛰지 않습니다.
banterCZ


31

구글의 구아바 라이브러리com.google.common.base.Joiner의 같은 작업을 해결하는 데 도움이 클래스를.

샘플:

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

다음은 구아바의 문자열 유틸리티에 대한 기사 입니다.


26

Java 8에서는 다음을 사용할 수 있습니다 String.join().

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

또한 Stream API 예제에 대한 이 답변 을 살펴보십시오 .


20

당신은 그것을 일반화 할 수 있지만 잘 말한 것처럼 Java에는 참여하지 않습니다.

이것은 더 잘 작동 할 수 있습니다.

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

이 답변에 동의하지만 누군가가 AbstractCollection <String> 대신 Collection <String>을 허용하도록 서명을 편집 할 수 있습니까? 나머지 코드는 동일해야하지만 AbstractCollection은 여기서 중요하지 않은 구현 세부 사항이라고 생각합니다.
무법자 프로그래머

더 나은 방법 Iterable<String>은 반복해서 사용할 수 있다는 사실을 사용 하고 사용하는 것입니다. 귀하의 예에서 컬렉션의 항목 수는 필요하지 않으므로 더 일반적입니다.
Jason Cohen

1
또는 Iterable <? Charsequence>를 확장하면 StringBuilders 및 Strings 컬렉션과 스트림 및 기타 문자열과 같은 것을 받아 들일 수 있습니다. :)
Jason Cohen

2
StringBuilder대신 에 사용하고 싶습니다 . StringBuffer불필요한 나사산 안전을 제공 한다는 점을 제외하면 동일 합니다. 누군가 편집 해주세요!
Casebash

1
이 코드에는 몇 가지 오류가 있습니다. 1. CharSequence는 자본을 가지고 있습니다. 2. s.iterator ()는 Iterator <?를 반환합니다. CharSequence>를 확장합니다. 3. Iterable에는 isEmpty()메소드 가 없습니다. next()대신 메소드를 사용하십시오
Casebash

17

Java 8에서는 다음과 같이 할 수 있습니다.

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

list에 null이 있으면 다음을 사용할 수 있습니다.

list.stream().map(String::valueOf)
        .collect(Collectors.joining(delimiter))

또한 접두사와 접미사를 지원합니다.

list.stream().map(String::valueOf)
        .collect(Collectors.joining(delimiter, prefix, suffix));

15

에 기반한 접근 방식을 사용하십시오 java.lang.StringBuilder! ( "변경 가능한 문자 시퀀스.")

앞에서 언급했듯이 모든 문자열 연결은 모든 문자열을 생성합니다. StringBuilder그렇게하지 않습니다.

StringBuilder대신에 StringBuffer? 로부터 StringBuilder의 javadoc :

가능한 경우이 클래스는 대부분의 구현에서 더 빠를 것이기 때문에 StringBuffer보다 우선적으로 사용하는 것이 좋습니다.


4
네. 또한 StringBuffer는 스레드로부터 안전하지만 StringBuilder는 안전하지 않습니다.
Jon Onstott

10

Google 컬렉션을 사용합니다. 좋은 조인 시설이 있습니다.
http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/base/Join.html

하지만 제가 직접 작성하고 싶다면

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

객체 컬렉션에서 더 잘 작동한다고 생각합니다. 이제는 객체를 결합하기 전에 객체를 문자열로 변환 할 필요가 없기 때문입니다.


8

Apache commons StringUtils 클래스에는 조인 메소드가 있습니다.




3

최소한의 것 (Apache Commons 또는 Gauva를 문자열 결합을 위해 프로젝트 종속성에 포함시키지 않으려는 경우)

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

File.separator 대신 delim을 사용하십시오.
Pradeep Kumar

3

StringBuilder 및 클래스 사용 Separator

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

구분 기호를 구분 기호로 감 쌉니다. toString빈 문자열을 반환하는 첫 번째 호출이 아닌 한 구분 기호는 Separator의 메서드에 의해 반환됩니다 !

클래스 소스 코드 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;
    }

}

연결 Separator하는 StringBuilder대신 사용하도록 수정 하는 것은 Strings어떻습니까?
Mohamed Nuur

@Mohamed Separator는 구분 기호를 반환하며 문자열 자체를 연결하지 않습니다.
akuhn

이 수업은 무엇입니까 Separator??? 단순한 String을 사용하지 않겠습니까?
maxxyme

2

왜 자신의 join () 메소드를 작성하지 않습니까? 문자열 및 구분 기호 문자열의 매개 변수 컬렉션으로 사용됩니다. 메소드 내에서 콜렉션을 반복하고 결과를 StringBuffer에 빌드하십시오.


2

Spring MVC를 사용하는 경우 다음 단계를 시도 할 수 있습니다.

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

결과는 a,b,c


2

당신이 사용하는 경우 이클립스 컬렉션 , 당신은 사용할 수 있습니다 makeString()또는 appendString().

makeString()String와 유사한 표현을 반환합니다 toString().

세 가지 형태가 있습니다

  • makeString(start, separator, end)
  • makeString(separator) 기본값은 빈 문자열로 시작하고 끝납니다.
  • makeString()구분 기호의 기본값은 ", "(쉼표 및 공백)

코드 예 :

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()makeString()는와 비슷 하지만 Appendable(처럼 StringBuilder)에 추가 되고입니다 void. 추가 세 번째 인수 인 Appendable과 함께 동일한 세 가지 형식이 있습니다.

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

콜렉션을 Eclipse 콜렉션 유형으로 변환 할 수없는 경우 관련 어댑터로 조정하십시오.

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

참고 : 저는 Eclipse 컬렉션의 커미터입니다.


2

자바 8 네이티브 타입

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

자바 8 커스텀 객체 :

List<Person> person;
...
person.stream().map(Person::getAge).collect(Collectors.joining(","));

1

아마도 결과 StringBuilder와 함께 append메소드를 사용 하여 메소드를 사용해야 하지만 그렇지 않은 경우 Java가 제공하는 솔루션만큼 좋습니다.


1

왜 루비에서하고있는 것과 같은 일을 Java에서하지 않습니까? 즉, 배열에 모든 조각을 추가 한 후에 만 ​​구분 기호로 구분 된 문자열을 생성합니까?

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

별도의 도우미 메서드에서 for 루프를 이동하고 StringBuffer 대신 StringBuilder를 사용할 수도 있습니다 ...

편집 : 추가 순서를 수정했습니다.


예, Java 1.5 이상을 사용할 때 StringBuilder 대신 StringBuffer를 사용하는 이유는 무엇입니까? 당신은 또한 당신의 주위에 잘못된 길을 추가했습니다.
Tom Hawtin-tackline

1

Java 5 변수 인수를 사용하면 모든 문자열을 컬렉션이나 배열에 명시 적으로 넣을 필요가 없습니다.

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

1

Spring 컨텍스트에있는 사람들에게는 StringUtils 클래스가 유용합니다.

다음과 같은 유용한 단축키가 많이 있습니다.

  • collectionToCommaDelimitedString (컬렉션 콜)
  • collectionToDelimitedString (컬렉션 콜, 문자열 delim)
  • arrayToDelimitedString (Object [] arr, 문자열 delim)

그리고 많은 다른 사람들.

Java 8을 아직 사용하지 않고 이미 Spring 컨텍스트에있는 경우 유용합니다.

다음과 같이 더 쉬운 Collection 지원을 위해 Apache Commons (아주 훌륭하지만)에 비해 선호합니다.

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

0

다음과 같이 시도해보십시오.

StringBuilder sb = new StringBuilder();
if (condition) { sb.append("elementName").append(","); }
if (anotherCondition) { sb.append("anotherElementName").append(","); }
String parameterString = sb.toString();

이것은 문자열 끝에 쉼표가 남는 것처럼 보이므로 피하고 싶었습니다. (물론, 그것이 있다는 것을 알고 있기 때문에 그것을 다듬을 수는 있지만 약간의 냄새가납니다.)
Sean McMains

0

기본적으로 다음과 같은 것이 있습니다.

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

여기서 문제는 그가 appendWithDelimiter () allot를 호출하는 것 같습니다. 솔루션은 하나의 StringBuffer 만 인스턴스화하고 해당 단일 인스턴스로 작업해야합니다.
Stu Thompson

0

이것이 실제로 더 나은지 모르겠지만 적어도 StringBuilder를 사용하고 있습니다.

아래는 매개 변수 구분을 수행하기 전에 매개 변수 목록을 작성할 수있는 경우보다 일반적인 방법입니다.

// 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");
}

0

접근 방식이 나쁘지는 않지만 + 기호 대신 StringBuffer를 사용해야합니다. +는 단일 작업마다 새로운 String 인스턴스가 생성된다는 큰 단점이 있습니다. 줄이 길수록 오버 헤드가 커집니다. 따라서 StringBuffer를 사용하는 것이 가장 빠른 방법이어야합니다.

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

문자열 생성을 마치면 반환 된 StringBuffer에서 toString ()을 호출하기 만하면됩니다.


1
여기서 StringBuffer를 사용하면 아무런 이유없이 속도가 느려집니다. + 연산자를 사용하면 내부적으로 더 빠른 StringBuilder를 사용하므로 StringBuffer를 사용하여 필요하지 않은 스레드 안전성 만 얻습니다.
Fredrik

또한 ... "+에는 각 단일 작업에 대해 새 String 인스턴스가 생성된다는 큰 단점이 있습니다"라는 문구가 잘못되었습니다. 컴파일러는 StringBuilder.append () 호출을 생성합니다.
Fredrik

0

문자열 연결을 사용하는 대신 코드가 스레드되지 않은 경우 StringBuilder를 사용하고, 그렇지 않으면 StringBuffer를 사용해야합니다.


0

당신은 이것을 이전보다 조금 더 복잡하게 만들고 있습니다. 예제의 끝에서 시작합시다.

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

String 대신 StringBuilder를 약간 변경하면 다음과 같이됩니다.

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

완료되면 (다른 조건도 확인해야한다고 가정) 다음과 같은 명령으로 꼬리 쉼표를 제거하십시오.

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

마지막으로 원하는 문자열을 가져옵니다.

parameterString.toString();

또한 두 번째 호출에서 ","를 대체하여 임의의 값으로 설정할 수있는 일반 구분 기호 문자열을 추가 할 수 있습니다. 무조건 추가해야하는 항목 목록이있는 경우이 코드를 문자열 목록을 사용하는 메소드에 넣을 수 있습니다.


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

0

따라서 원하는 것 같은 느낌을 얻기 위해 할 수있는 몇 가지 작업은 다음과 같습니다.

1) 목록 클래스 확장-조인 메소드를 추가하십시오. join 메소드는 단순히 분리 문자를 연결하고 추가하는 작업을 수행합니다 (join 메소드의 매개 변수 일 수 있음).

2) Java 7이 java에 확장 메소드를 추가 할 것 같습니다-클래스에 특정 메소드를 첨부 할 수 있습니다. 따라서 해당 조인 메소드를 작성하고 확장 메소드로 List 또는 수집.

Java 1이 아직 나오지 않았기 때문에 솔루션 1은 아마도 유일한 현실적인 것입니다.) 그러나 제대로 작동합니다.

이 두 가지를 모두 사용하려면 평소와 같이 모든 항목을 목록 또는 컬렉션에 추가 한 다음 새로운 사용자 지정 방법을 호출하여 '가입'하면됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.