1. 소개

우리는 종종 Java Stream을 컬렉션으로 변환하고자 합니다. 이로 인해 일반적으로 변경 가능한 컬렉션이 생성되지만 사용자 지정할 수 있습니다.

이 짧은 사용방법(예제)에서는 먼저 일반 Java를 사용한 다음 Guava 라이브러리를 사용하여 Java 스트림을 불변 컬렉션으로 수집하는 방법을 자세히 살펴보겠습니다 .

2. 표준 자바 사용

2.1. Java의 toUnmodifiableList 사용

Java 10부터  Java의 수집기 클래스에서 toUnmodifiableList 메서드를 사용할 수 있습니다 .

List<String> givenList = Arrays.asList("a", "b", "c");
List<String> result = givenList.stream()
  .collect(toUnmodifiableList());

이 메서드를 사용하여 Java의 ImmutableCollections 에서 null 값을 지원하지 않는 List 구현을 얻습니다 .

class java.util.ImmutableCollections$ListN

2.2. Java의 collectAndThen 사용

 Java의 Collectors 클래스 의 collectingAndThen 메소드 Collector  및 finisher Function 을 허용합니다 . 피니셔는 Collector 에서 반환된 결과에 적용됩니다 .

List<String> givenList = Arrays.asList("a", "b", "c");
List<String> result = givenList.stream()
  .collect(collectingAndThen(toList(), ImmutableList::copyOf));

System.out.println(result.getClass());

이 접근 방식에서는 toCollection Collector를 직접 사용할 수 없기 때문에 요소를 임시 List으로 수집해야 합니다. 그런 다음 불변 List을 구성합니다.

2.3. Stream.toList() 메서드 사용

Java 16에는 toList() 라는 Stream API 의 새로운 메서드가 도입되었습니다 . 이 편리한 메서드는 스트림 요소를 포함하는 수정 불가능한 List을 반환합니다 .

@Test
public void whenUsingStreamToList_thenReturnImmutableList() {
    List<String> immutableList = Stream.of("a", "b", "c", "d").toList();
	
    Assertions.assertThrows(UnsupportedOperationException.class, () -> {
        immutableList.add("e");
    });
}

단위 테스트에서 볼 수 있듯이 Stream.toList()는 변경할 수 없는 List을 반환합니다 . 따라서 List에 새 요소를 추가하려고 하면 UnsupportedOperationException 이 발생합니다 .

새로운 Stream.toList() 메서드는 수정할 수 없는 List을 반환하므로 기존 Collectors.toList() 메서드와 약간 다릅니다.

3. 커스텀 컬렉터 만들기

커스텀 Collector 를 구현 하는 옵션도 있습니다 .

3.1. 기본 불변 수집기

이를 달성하기 위해 정적 Collector.of  메소드를 사용할 수 있습니다.

public static <T> Collector<T, List<T>, List<T>> toImmutableList() {
    return Collector.of(ArrayList::new, List::add,
      (left, right) -> {
        left.addAll(right);
        return left;
      }, Collections::unmodifiableList);
}

우리는 이 함수를 내장 Collector 처럼 사용할 수 있습니다 :

List<String> givenList = Arrays.asList("a", "b", "c", "d");
List<String> result = givenList.stream()
  .collect(MyImmutableListCollector.toImmutableList());

마지막으로 출력 유형을 확인하겠습니다.

class java.util.Collections$UnmodifiableRandomAccessList

3.2. MyImmutableListCollector 제네릭 만들기

구현에는 한 가지 제한이 있습니다. 항상 ArrayList 가 지원하는 변경 불가능한 인스턴스를 반환합니다 . 그러나 약간의 개선을 통해 이 수집기가 사용자 지정 유형을 반환하도록 할 수 있습니다.

public static <T, A extends List<T>> Collector<T, A, List<T>> toImmutableList(
  Supplier<A> supplier) {
 
    return Collector.of(
      supplier,
      List::add, (left, right) -> {
        left.addAll(right);
        return left;
      }, Collections::unmodifiableList);
}

이제 메서드 구현에서 Provider를 결정하는 대신 사용자에게 Provider를 요청합니다.

List<String> givenList = Arrays.asList("a", "b", "c", "d");
List<String> result = givenList.stream()
  .collect(MyImmutableListCollector.toImmutableList(LinkedList::new));

또한 ArrayList 대신 LinkedList를 사용하고 있습니다 .

class java.util.Collections$UnmodifiableList

이번에는 UnmodifiableRandomAccessList 대신 UnmodifiableList를 얻었습니다 .

4. Guava의 수집기 사용

이 섹션에서는 Google Guava 라이브러리를 사용하여 몇 가지 예제를 구동합니다.

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.1-jre</version>
</dependency>

Guava 21부터 모든 불변 클래스에는 Java 의 표준 Collector 만큼 사용하기 쉬운 Collector 가 함께 제공됩니다 .

List<Integer> list = IntStream.range(0, 9)
  .boxed()
  .collect(ImmutableList.toImmutableList());

결과 인스턴스는 RegularImmutableList 입니다 .

class com.google.common.collect.RegularImmutableList

5. 결론

이 짧은 기사에서 우리는 Stream을 불변 Collection 으로 수집하는 다양한 방법을 살펴보았습니다 .

항상 그렇듯이 이 기사의 전체 소스 코드는 GitHub에 있습니다. 섹션 3-4 , 섹션 2.2섹션 2.3 에 대한 예제로 Java 버전별로 구분됩니다 .

res – REST with Spring (eBook) (everywhere)