java stream에서 maptoobj 함수는 중간 연산이라 되어 있습니다. 길이 n짜리 배열을 obj object n개로 채우려고 합니다. obj 클래스에는 int 자료형만 하나 있고, 우리는 이 n개의 obj가 깊은 복사가 되어야 해요. 이걸 stream을 써서 할 때, Intstream의 range 메서드와, maptoObj를 사용하면 손쉽게 처리할 수 있습니다.

 


 mapToObj 메서드를 봅시다. Intstream 뿐만이 아니라, Longstream과 DoubleStream에도 있습니다. 설명을 보면, 스트림으로부터 해당 함수를 적용한 객체 값들의 stream을 반환한다고 되어 있어요. 즉 입력 스트림으로부터 무언가를 받아서, 새로운 결과 가지고 있는 스트림으로 변환합니다.

 

 n개의 크기를 가진 리스트에 깊은 복사한 Obj 오브젝트 n개를 채워야 한다고 해 볼게요. 그러면 단순하게 List를 새로 생성해서 Obj 객체를 추가해도 됩니다. 그런데, 조금 더 생각해 보면 아래와 같은 로직을 생각할 수 있습니다.

 

 1부터 5까지 있는 stream이 있어요. 이것을 아래와 같이 변환시키면 어떨까요?

 

 하나의 원소가 새로운 new Obj(3)으로 바뀌는 것입니다. mapToXXX류가 이러한 역할을 수행합니다. 이 경우 IntStream의 원소들을 다른 obj로 변환시키는 apply 작업을 수행했습니다.

 

 

 그림으로 그리면 이런 상황인 셈입니다. 즉, mapToXXX는 스트림의 요소들을 다른 요소로 바꾼 새로운 스트림을 돌려줍니다. 위 그림에서는 x를 new Obj(3)으로 바꾸었습니다. 따라서, 1 2 3 4 5로 이루어져 있던 것을 new Obj(3), ... , new Obj(3)으로 대체하게 됩니다.

 


 이제 문제 상황을 다시 봅시다. 저는 객체 Obj n개가 채워진 ArrayList를 원합니다. 객체 n개는 깊은 복사가 되어야 하고요. 객체 Obj는 int 필드 하나만 주어져 있습니다. n = 4라고 해 보겠습니다.

 

 당연하게도, setter가 있기 때문에, 불변 객체가 아닙니다. 어떻게 하면 될까요? 로직부터 설계해 봅시다. 일단, 이전에 배웠던 range는 1부터 n까지의 순서를 가지는 stream을 생성합니다. 고로, IntStream.range(1, 5)를 먼저 수행합니다.

 

 

 다음에, 해당 stream을 토대로 Obj로 이루어진 스트림을 새로 생성합니다. mapToObj로요.

 

 그러면, 1부터 5까지 순서로 이루어진 Stream이 new Obj(3)으로 이루어진 4개의 객체로 이루어 집니다. 참고로 각 원소별로 apply가 별개로 동작하므로 매번 새로운 객체가 생기게 됩니다.

 

 toList로 Stream을 List로 변환합니다.

 

 최종 코드는 20 ~ 21번째 줄에 나와 있습니다. 23번째 줄에 list의 0번째 원소의 x 값을 2로 바꾸어 봅시다. 만약에 저 객체들이 모두 얕은 복사가 되었다면, 모두 2가 출력되었을 겁니다.

 

 그런데 3이 출력되었습니다. 이유는 toList가 호출될 때 mapToObj에 있는 apply인 x -> new Obj(3)가 실행되기 때문입니다. 각각의 element에 대해 적용되기 때문에 별개의 새로운 오브젝트 Obj가 생성됩니다.