문제설명
문자들이 담겨있는 배열 arr가 주어집니다. arr의 원소들을 순서대로 이어 붙인 문자열을 return하는 solution함수를 작성해 주세요.
제한사항
- 1 <= arr의 길이 <= 200
- arr의 원소는 전부 알파벳 소문자로 이루어진 길이가 1인 문자열입니다.
입/출력 예
arr | result |
["a","b","c"] | "abc" |
[제출 답안]
class Solution {
public String solution(String[] arr) {
String answer = "";
StringBuffer sb = new StringBuffer();
for(String literal : arr) {
sb.append(literal)
}
}
}
import java.util.Arrays;
class Solution {
public String solution(String[] arr) {
String answer = "";
StringBuffer sb = new StringBuffer();
Arrays.stream(arr).forEach(literal -> sb.append(literal));
answer = sb.toString();
return answer;
}
}
[풀이과정]
처음 향상된 for문을 사용하여 StringBuffer에 순차적으로 append하였다.
제출하기 직전 배열을 Stream으로 변환한뒤 forEach(람다)를 사용하여 StringBuffer에 순차적으로 append해줬다.
[리팩토링]
메소드 참조 방식으로도 리팩토링이 가능하다.
Arrays.stream(arr).forEach(sb::append);
람다 표현식은 메서드 참조보다 조금 더 오버헤드가 발생할 수 있다.
람다 표현식은 내부적으로 익명클래스로 변환되어 실행되기 때문이다.
자바 버전이 올라갈수록 이런 오버헤드의 최소화를 위해 최적화가 되고있다.
[다른사람 풀이]
1) String.join(CharSequence delimiter, CharSequence... elements)
class Solution {
public String solution(String[] arr) {
return String.join("", arr);
}
}
문제를 풀다 보면 join이 굉장히 효율적일때가 있으면서도 간과할때가 있는거 같다.
join에 대해서 다시 한번 개념을 정리해 보자면 첫번째 요소인 delimiter의 복사본과 elements의 요소들을 복사하여 합쳐진 문자를 반환한다.
쉽게 이해해보자면 첫번째 요소는 String변수를 초기화 한다고 이해하면 좋겠다.
또한 String에 대한 join연산은 불변성을 지키기 위해 새로운 문자열을 생성할 때 마다 기존 문자열을 변경하는 것이 아니고 새로운 문자열을 생성하므로 불변성을 해치지 않는다!
2) Stream.of(T... values)
import java.util.Stream;
class Solution {
public String solution(String[] arr) {
return Stream.of(arr).forEach(literal -> sb.append(literal));
//혹은 forEach(sb::apend)
}
}
내가 작성한 Arrays.stream(arr)과 비슷하다.
둘다 배열을 Stream으로 변환한다.
Arrays.stream()은 주로 primitive 기본타입의 배열을 스트림으로 변환할 때 사용하며, Stream.of()의 경우 Object 객체배열이나 임의의 객체를 스트림으로 변환할 때 사용한다.
두 방식의 리소스 측면에서는 큰 차이가 없으며 코드 가독성, 사용 목적에 따라 선택해 사용한다.
기본 데이터 타입은 Arrays.stream(), 객체배열,객체는 Stream.of를 사용하는것이 일반적인 관례라고 한다.
3) Arrays.stream(arr) || Stream.of(arr) - .Collect(Collectors.joining())
joining(CharSequence delimiter)
return Stream.of(arr).Collect(Collectors.joining());
- collect()
- Stream의 요소를 수집하는 역할을 한다.
- Collector를 인자로 받아 스트림의 요소를 해당 Collector로 수집한다.
- Collectors
- 다양한 컬렉션을 생성, 그룹화, 분할 등의 컬렉션 관련 작업을 수행하는 정적 팩토리 메소드를 제공하는 유틸리티 클래스이다.
- Collectors.joining()
- 문자열 요소를 연결하는 컬렉터를 생성한다.
- 내부적으로 StringBuffer를 사용하여 스트림의 문자열 요소를 이어 붙인다.
- 오버로드 되어 있어 구분자, 접두사, 접미사를 지정할 수 있다.
joining()을 들여다보면 아래와 같이 구현되어 있다.
public static Collector<CharSequence, ?, String> joining() {
return new CollectorImpl<CharSequence, StringBuilder, String>(
StringBuilder::new, StringBuilder::append,
(r1, r2) -> { r1.append(r2); return r1; },
StringBuilder::toString, CH_NOID);
}
4) .collect(공급자, 누산기, 결합자) / StringBuilder.append()
Arrays.stream(strings)
.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append);
- R collect(Supplier supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)
Stream의 collect 메소드는 위와 같이 정의되어 있다.
- Supplier supplier : Stream의 요소들을 이용하여 생성할 누적 연산결과 객체
- BiConsumer<R, ? super T> accumulator : 누적 연산결과를 도출할 함수 정의
- BiConsumer<R, R> combiner : 각 스레드에서 생성된 누적연산 결과 객체 R을 결합하는 역할. 병렬 처리시에만 사용.
5) Arrays.stream || Stream.of - reduce(초기값, 누산기) / String.concat()
람다식
return Arrays.stream(arr).reduce("", (a,b)-> a+b)
메소드참조(메소드레퍼런스)
return Arrays.stream(arr).reduce("", String::concat)
두가지 방식이 있다. 우선 reduce함수는 스트림 원소들을 하나씩 소모해 가며, 누적 계산을 수행하고 결과값을 리턴하는 메소드이다.
첫번째 인자로는 초기값을 지정해주고 두번째 인자로 누적 계산을 수행할 누산기를 추가한다.
이렇게 되면 스트림을통해 수집한 모든 원소들을 순차적으로 누산기를 통해 누적으로 연산한다.
단점으로는 String의 불변성을 해칠수 있다.
(누적 연산이기 때문에 값이 변경될 때 마다 새로운 String 객체를 생성하여 저장한다)
'코딩테스트 - 프로그래머스 > Lv. 0' 카테고리의 다른 글
[14] JAVA 더 크게 합치기 (0) | 2023.11.15 |
---|---|
[13] JAVA 문자열 곱하기 (0) | 2023.11.15 |
[11] JAVA 문자열 섞기 (0) | 2023.11.14 |
[10] JAVA 문자열 겹쳐쓰기 (0) | 2023.11.14 |
[9] JAVA 홀짝 구분하기 (0) | 2023.11.14 |