평소 스트림을 리스트로 변환할 때 단순히 Stream.toList()를 활용해왔다.
하지만 어제 프로그래머스에서 코테 문제를 풀다가 Stream.toList()를 지원하지 않고 Collectors.toList()만 지원되는 것을 보고, 이 둘의 차이점을 조금 더 명확히 알 필요가 생겨 정리하게 되었습니다.
(우선 결론부터 말하자면.. stream의 toList는 자바 16이상 버전부터 지원하기에, 프로그래머스는 해당 버전이 아니어서 지원을 안한걸로)
우선 Collectors.toList()의 자바 공식 문서 설명을 보면 다음과 같습니다.
Returns a Collector that accumulates the input elements into a new List. There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned;
여기서 보면, mutability 즉 변경 가능성에 대해 보장하지 않는다 한다.
코드를 보며 확인해보자.
@DisplayName("Collectors.toList() 수정 테스트")
@Test
void collectorsToListTest(){
//Given
List<String> list = Stream.of("aaa", "bbb")
.collect(Collectors.toList());
//When
list.add("ccc");
//Then
assertThat(list).hasSize(3);
}
@DisplayName("Stream.toList() 수정 테스트")
@Test
void streamToListTest(){
//Given
List<String> list = Stream.of("aaa", "bbb").toList();
//When //Then
assertThatThrownBy(() -> list.add("ccc"))
.isInstanceOf(UnsupportedOperationException.class);
}
둘의 가장 큰 차이는, 반환하는 타입이다.
Collectors.toList()는 ArrayList를 반환한다.
그렇기에 수정을 해도 문제가 없는 것이다.
반면에 Stream.toList()는 Collectors.UnmodifiableList를 반환한다.
그렇기에 해당 메서드를 사용하면 수정하거나 추가하는 등의 작업을 할 수 없는 것이다.
우리가 코딩 테스트 문제들을 풀다보면, 보통 리스트를 반환받고 해당 리스트에 요소를 추가하는 경우가 생긴다.
List<Integer> keySet = indexMap.keySet().stream()
.sorted()
.collect(Collectors.toList());
keySet.add(0);
그런데 이걸 Collectors.toList()가 아니라, Stream.toList() 사용했다면, UnmodifiableList를 반환했기 때문에,
예외가 발생할 것이다.
static class UnmodifiableList<E> extends UnmodifiableCollection<E> implements List<E> {
...
public E get(int index) {return list.get(index);}
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public int indexOf(Object o) {return list.indexOf(o);}
public int lastIndexOf(Object o) {return list.lastIndexOf(o);}
public boolean addAll(int index, Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException();
}
@Override
public void sort(Comparator<? super E> c) {
throw new UnsupportedOperationException();
}
...
}
set, add, remove 등의 수정 작업에는 다 UnsupportedOperationException을 던지는 걸 확인할 수 있다.
그렇다면 드는 생각은, 그러면 Collectors.toUnmodifiableList()를 사용하면 Stream.toList()와 동일한 기능을 할 것인가? 라는 의문이다.
하지만 약간 다르다.
collect(Collectors.toUnmodifiableList())는 요소에 null을 허용하지 않는다.
하지만 Stream.toList()는 null 요소를 허용한다.
우선 저의 개인적인 생각은.. 수정하지 않고 반환할 리스트라면 Stream.toList()를 사용할 것 같지만..
코테처럼 해당 리스트에 조작을 가하는 작업이 빈번하게 일어나는 경우라면 Collectors.toList를 적극 활용할 것 같습니다.
'Java' 카테고리의 다른 글
Try with resources (0) | 2023.10.16 |
---|---|
SOLID(의존성을 잘 관리해보자!!) (0) | 2023.09.25 |
If문 제거하기 (리펙토링) (0) | 2023.09.21 |
Stream (0) | 2023.09.03 |