본문 바로가기
Baeldung번역&공부/Java-string

문자열을 잇는방법(Concatenating Strings in Java)

by ms727 2025. 1. 22.

원본글: https://www.baeldung.com/java-strings-concatenation

 

Java에서 String객체를 잇는 방법에는 여러가지가 있는데, 그 중 몇 가지를 확인하고 나쁜(?) 코드도 확인해 보겠습니다.

 

1. StringBuilder

 

StringBuilder클래스는 String을 조작할 수 있는 여러 함수들을 제공하는데, 이를 통하여 String을 이을 수 있습니다.

 

    @Test
    void StringBuilderAppendTest() {
        StringBuilder stringBuilder = new StringBuilder(100);

        stringBuilder.append("hello");
        stringBuilder.append(" my name is");
        stringBuilder.append(" minseok");

        assertEquals("hello my name is minseok", stringBuilder.toString());
    }

 

StringBuilder를 생성할때의 크기를 100으로 지정하여 배열 확장크기 비용이 줄어듭니다. 

 

다른 클래스로 StringBuffer라는 클래스가 있는데, 이 클래스는 StringBuilder의 동기화 버전입니다. 동기화된 버전이라 멀티쓰레드환경에서 안전할 수 있다고 생각할 수 있는데 그렇지 않습니다. 왜냐하면 StringBuffer가 가지고 있는 빌더패턴때문입니다. 따라서 멀티쓰레드 환경에서 추천하지 않습니다.

 

    @Test
    void StringBufferMultiThreadTest() {
        //given
        StringBuffer stringBuffer = new StringBuffer(100);

        //when
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            executorService.execute(() -> {
                for (int j = 0; j < 100; j++) {
                    stringBuffer.append(finalI);
                }
            });
        }
        executorService.shutdown();

        //then
        System.out.println(stringBuffer.length());
        assertTrue( 10 * 100 == stringBuffer.length());
    }

 

10개의 스레드를 생성하고 스레드 번호에 해당하는 값을 StringBuffer에 추가하는 예제입니다.

 

10개의 스레드에 100개의 값을 넣고 있으므로 stringBuffer의 길이는 1000이되어야합니다(10 * 100)

 

이 테스트는 통과할 때도 있고 통과하지 않을때도 있습니다.

 

성공할때
실패할때

 

이렇듯 StiringBuilder, StringBuffer는 멀티쓰레드환경에서 적합하지 않음을 확인하였습니다.

 

2. '+'연산자

 

'+'연산자를 통해서 문자열을 이을 수 있습니다.

 

    @Test
    void Operator연산자확인() {
        String myString = "hello " + "my " + "name " + "is " + "minseok";

        assertEquals("hello my name is minseok", myString);
    }

StringBuilder와는 달리 확실히 간결하고 직관적입니다.

다만, 이 방식에는 단점이 있는데 컴파일 과정에서 결국 '+'연산자는 StringBuilder.append() 메서드를 호출하는거나 다름없어집니다. 때문에 '+' 연산자와 StringBuilder를 같이 쓰는건 굉장히 안 좋은 습관으로 통용됩니다.

 

또한 for문과 같은 루프 내에서 '+'연산자를 통해 문자열을 잇는다느건 그만큼 String 객체를 생성한다는것과 같기때문에 주의해야합니다. 이를 방지하기위해서는 내부 버퍼를 조작하는StringBuilder를 사용하는게 낫습니다.

 

3. String.concat()

    @Test
    void String_concat() {
        String myString = "hello ".concat("my ")
                .concat("name ")
                .concat("is ")
                .concat("minseok");
        assertEquals("hello my name is minseok", myString);
    }

 

해당 메서드는 불변성을 가진 String 객체를 반환하므로 멀티쓰레드 환경에서 안전하게 동작합니다.

 

4. String.fotmat()

 

해당 메서드는 Java의 다양한 자료형들을 해당 메서드 템플릿에 배치할 수 있습니다.

    @Test
    void String_format() {
        String myString = String.format("%s %s %.2f %s %s, %s...", "I",
                "ate",
                2.5056302,
                "blueberry",
                "pies",
                "oops");

        assertEquals("I ate 2.51 blueberry pies, oops...", myString);
    }

 

보시는것처럼 해당 메서드는 직관적인 부분도 있습니다.

 

5. String.join(Java8+)

 

해당 메서드는 코드를 보는게 더 편합니다.

    @Test
    void String_join_test() {
        String [] strings = {"hello", "my", "name", "is", "minseok"};

        String resultString = String.join(" ", strings);

        assertEquals("hello my name is minseok", resultString);
    }

 

보시면은 문자열 배열 중간중간에 특정 문자열을 넣을 수 있는 함수입니다. 

이 메서드의 장점은 문자열 배열 사이에 구분기호를 어떻게 넣을지 고민을 안해도 된다는 점입니다.

 

6. StringJoiner(Java8+)

StringJoiner는 String.join의 기능을 사용하기 쉬운 클래스로 추상화한 것입니다.

생성자에서 구분자만 받거나 구분자, 접두사, 접미사를 받을 수 있습니다.

 


    @Test
    void StringJoiner_test() {
        StringJoiner fruitJoiner = new StringJoiner(", ");
        StringJoiner fruitJoiner2 = new StringJoiner(", ", "fruit: ", " end");

        fruitJoiner.add("Apples");
        fruitJoiner.add("Oranges");
        fruitJoiner.add("Bananas");

        fruitJoiner2.add("Apples");
        fruitJoiner2.add("Oranges");
        fruitJoiner2.add("Bananas");

        assertEquals("Apples, Oranges, Bananas", fruitJoiner.toString());
        assertEquals("fruit: Apples, Oranges, Bananas end", fruitJoiner2.toString());

    }

 

String.join()과 달리 이 메서드는 배열을 먼저 생성할 필요가 없습니다.

 

7. Arrays.toString()

    @Test
    void arrayToString_test() {
        String[] myFavouriteLanguages = {"Java", "JavaScript", "Python"};

        String toString = Arrays.toString(myFavouriteLanguages);

        assertEquals("[Java, JavaScript, Python]", toString);
    }

Arrays.toString()은 String배열을 하나의 문자열로 만들어버립니다.

 

사용시에는 주의할 점이 2가지 있습니다.

- 첫 번째로는 배열안의 객체들이 toString()이 모두 정의되어 있어야합니다.

- 두번째로는 '[]'와 같은 대괄호가 포함된 문자열로만 리턴되어서 커스텀하기에는 불편할 수 있습니다.

 

8. Collectors.joining(Java 8+)

 

해당 메서드는 Stream의 출력을 하나의 문자열로 만들어버립니다.

 

    @Test
    void collector_join() {
        List<String> awesomeAnimals = Arrays.asList("Shark", "Panda", "Armadillo");

        String animalString = awesomeAnimals.stream().collect(Collectors.joining(", "));

        assertEquals("Shark, Panda, Armadillo", animalString);
    }

 

해당 메서드는 String.join()과 달리, 리스트형태의 값을 하나의 문자열로 바꿔줄 수 있습니다.

 

 

이렇듯 다양하게 문자열을 잇는 방법을 알수 있었습니다.

 

가변문자열인지, 고정문자열인지, 멀티쓰레드 환경에서는 안전한지, input값이 리스트형태인지 배열인지 등에 따라 다양하게 조합하여 사용할 수 있음을 알 수 있었습니다.