-
🔗 Java 문자열와 구분자☕️ java 2023. 10. 30. 15:56
우아한테크코스의 프리코스 자동차 경주 게임 미션에 아래와 같은 요구사항이 있었다.
- 자동차 경주 게임을 완료한 후 누가 우승했는지를 알려준다. 우승자는 한 명 이상일 수 있다.
- 우승자가 여러 명일 경우 쉼표(,)를 이용하여 구분한다.
이렇게 문자열을 연결해야하는 요구사항에 대해 해결하고자 여러 방법을 찾아보았다.
🧩 StringBuilder
⚠️ String + 연산의 문제
자바에서 String은 불변이라 문자열에 대해 수정을 할 수 없다.
String str1 = "abc"; String str2 = "def"; String resulta = str1 + str2; // abcdef
위와 같은 코드가 있을 때, 자바에서는 str1의 길이 + str2의 길이를 가지는 새로운 String을 만들어 낸다.
따라서, 여러 String을 연결하는데 String의 + 연산을 사용하게 되면 새로운 String을 계속해서 생성해내고 이에 걸리는 시간도 매우 오래 걸린다.
이런 문제를 해결하고자 StringBuilder를 사용해보았다.
💡 StringBuilder
자바에서는 StringBuilder 클래스를 제공해 String + 연산에 대해 빠르고 효율적으로 처리할 수 있도록 한다.
- StringBuilder는 불변이 아니고 미리 일정한 크기의 배열을 잡아 거기에 String 값을 붙여나가는 방식이다.
🆚 비교
JMH를 이용해 아래 코드에 대한 성능을 비교해보았다.
@State(Scope.Benchmark) public class Sample { private static final int N = 1000; @Benchmark public void testStringBuilder(Blackhole blackhole) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < N; i++) { stringBuilder.append(String.valueOf(i)); } } @Benchmark public void testString(Blackhole blackhole) { String str = ""; for (int i = 0; i < N; i++) { str += String.valueOf(i); } } }
- - StringBuilder를 사용한 경우 더 좋은 성능을 보여준다.
🧑🏻💻 실제 코드 작성
StringBuilder를 통해 요구사항을 처리한 코드
StringBuilder stringBuilder = new StringBuilder(); for(String winner : winnerList) { stringBuilder.append(winner); stringBuilder.append(", "); } stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
🤩 StringJoiner
StringJoiner는 Java 8에서 java.util.package 에 추가된 새로운 기능이다.
- 구분 기호, 접두사, 접미사를 사용해 문자열을 연결하는데 사용할 수 있다.
🧪 test
@Test public void whenAddingListElements_thenJoinedListElements() { List<String> fruitList = new ArrayList<>(); fruitList.add("Apple"); fruitList.add("Banana"); fruitList.add("Peach"); StringJoiner fruitJoiner = new StringJoiner(","); for (String fruit : fruitList) { fruitJoiner.add(fruit); } assertEquals(fruitJoiner.toString(), "Apple,Banana,Peach"); }
🧑🏻💻 요구사항에 대해 StringJoiner를 적용한 코드
List<String> winnerList = new ArrayList<>(); StringJoiner joiner = new StringJoiner(", "); winnerList.forEach(joiner::add);
🔥 구분자를 사이사이에 붙여주어 코드가 훨씬 줄어들 수 있었다.
- StringBuilder를 사용했을 때 보다 더 깔끔하게 코드를 작성할 수 있게 되었다.
📍 StringJoiner는 Stream API를 이용해서 더 간결하게 작성할 수 있다.
// Collectors.joining() 사용 List<String> winnerList = new ArrayList<>(); String winnerResult = winnerList.stream().collect(Collectors.joining(", "));
- Collectors.joining()는 내부적으로 StringJoiner를 사용해 조인 작업을 수행한다.
📍 Collectors
// Collectors public final class Collectors { ... public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) { return joining(delimiter, "", ""); } ... public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) { return new CollectorImpl<>( () -> new StringJoiner(delimiter, prefix, suffix), StringJoiner::add, StringJoiner::merge, StringJoiner::toString, CH_NOID); } ... }
- joining 함수를 보면 StringJoiner를 사용하고 있다.
📌 String.join()
String.join() 은 StringJoiner와 비슷하게 문자열 사이에 구분자를 넣어준다.
StringJoiner와 다른점은 접두사와 접미사를 넣어주는 작업은 하지 않는다.
String.join()을 사용한 경우
String winnerResult = String.join(", ", winnerList);
이번 미션에서는 간단하게 콤마(,)를 사이에 넣어 문자열을 연결하는 간단한 요구사항이므로 String.join()을 사용했다.
📖 참고자료
Java 8 StringJoiner | Baeldung
Concatenating Strings in Java | Baeldung
☕ 자바 String / StringBuffer / StringBuilder 차이점 & 성능 비교
Performance Comparison Between Different Java String Concatenation Methods | Baeldung
'☕️ java' 카테고리의 다른 글
ArrayDeque (0) 2024.03.15 👻 Mockito (0) 2023.10.31 Multiple Assertions (1) 2023.10.29 ☕️ Java 17 (1) 2023.10.25 🆚 AssertJ과 JUnit 의 Assertions 비교 (1) 2023.10.25