Backend/Java

[Java] Collectors.toList() vs Stream.toList()

누구세연 2025. 7. 9. 21:39

Java에서 Stream API를 사용할 때 Collectors.toList()는 오랫동안 익숙하게 사용해온 방식이었습니다.
그런데 최근 IntelliJ에서 Stream.toList() 사용을 권장하는 안내 메시지를 보고 궁금증이 생겼습니다. 🤔

이번 글에서는 두 방식의 차이점언제 어떤 걸 선택해야 하는지 명확히 정리해보겠습니다.

 

 


 

Collectors.toList() → 변경 가능한 리스트 (mutable)

List<String> list = Stream.of("A", "B", "C").collect(Collectors.toList());
list.add("D"); // 가능! 리스트에 값 추가됨

Collectors.toList()는 내부적으로 new ArrayList<>()를 사용해 리스트를 생성합니다.

그래서 리스트에 값을 추가하거나 삭제할 수 있습니다.

하지만 이로 인해 실수로 외부에서 리스트를 수정해버리는 경우가 생길 수 있습니다.
(특히 불변(immutable) 데이터가 필요한 곳에서는 치명적인 버그가 될 수 있음!)

 

 

Stream.toList() → 변경 불가능한 리스트 (immutable)

List<String> list = Stream.of("A", "B", "C").toList();
list.add("D"); // 오류 발생! UnsupportedOperationException

Java 16부터 추가된 메서드로 이 방식은 리스트를 바꿀 수 없게 만듭니다.

반환되는 리스트는 불변 리스트 (UnmodifiableList)입니다.

즉, 한 번 만든 후에는 추가/삭제가 불가능한 불변리스트를 반환합니다.

 

 

핵심 차이: 가변 리스트 vs 불변 리스트

메서드 반환  타입 변경 가능 변경 가능 버전
Collectors.toList() ArrayList ✅ 가능 Java 8+
Collectors.toUnmodifiableList() UnmodifiableList ❌ 불가능 Java 10+
Stream.toList() UnmodifiableList ❌ 불가능 Java 16+
📝 Stream.toList()는 사실상 Collectors.toUnmodifiableList()를 짧게 쓴 문법이자 더 나은 대체제입니다.

 

 

SonarQube 경고 (Rule S6204)

Collectors.toList()를 불변 리스트가 필요한 곳에서 사용하면 SonarQube가 경고를 띄우기도 합니다.

 

❌ 잘못된 예시

List<String> emails = users.stream()
    .map(User::getEmail)
    .collect(Collectors.toList()); // Code Smell (S6204)

이 코드에서는 emails 리스트가 외부에서 바뀔 위험이 있지만, 누가 봐도 "읽기 전용"일 것처럼 보여 문제가 됩니다.

 

✅ 올바른 개선 방법 (Java 16 이상)

List<String> emails = users.stream()
    .map(User::getEmail)
    .toList(); // 불변 리스트 반환

Stream.toList()를 사용하면 의도가 명확하고, SonarQube도 만족합니다!

 

 

언제 어떤 걸 써야 할까?

상황 추천 방식
리스트를 외부에서 바꾸면 안 되는 경우 (기본값) Stream.toList()
Java 8~15 환경에서 불변 리스트가 필요할 때 Collectors.toUnmodifiableList()
일부러 변경 가능한 리스트가 필요할 때 Collectors.toList()

기본 원칙:
→ 변경이 필요하면 명시적으로 Collectors.toList()
→ 기본은 Stream.toList()로 시작하고, 정말 필요할 때만 mutable로

 

 

 

Stream API는 Java 개발자에게 너무 익숙한 기능이지만, Stream.toList()의 도입은 작지만 중요한 변화입니다.
의도가 더 분명해지고, 코드가 더 안전해지며, 더 적은 코드로 같은 결과를 낼 수 있습니다.

Java 16 이상을 쓰고 있다면 이제부터는 Collectors.toList() 대신 Stream.toList()를 기본값으로 써보세요. 👩🏻‍💻