Item 47. 반환 타입으로는 stream보다는 collection이 낫다.

stream은 반복을 지원하지 않으므로, API를 stream만 반환하도록 짜놓으면 반환된 stream을 for-each로 반복하길 원하는 사용자는 사용이 힘들다

stream은 Iterable 인터페이스가 정의한 추상 method를 모두 포함할뿐 아니라, Iterable 인터페이스가 명시한 스펙대로 동작하지만 Iterable을 확장하고 있지는 않기 때문에 for-each문으로 반복이 불가능하다.

따라서 stream의 iterator method에 method 참조를 건네고, 이를 매개변수화된 Iterable로 적절히 형변환하면 동작은 한다.

1
2
3
for(ProcessHandle ph: (Iterable<ProcessHandler>) ProcessHandle.allProcesses()::iterator){
// process 처리
}

adapter method

하지만 이렇게 stream을 사용하기는 가독성이 떨어지므로, adapter method를 사용하는 방안이 있다.

1
2
3
4
// Stream<E> 를 Iterable<E>로 중개해주는 adapter 
public static <E> Iterable<E> iterableOf(Stream<E> stream){
return stream::iterator;
}

adapter method를 사용하면 어떤 stream도 for-each문으로 반복할 수 있다.

1
2
3
for(ProcessHandle p : iterableOf(ProcessHandle.allProcesses())){

}

반대의 상황으로서 API가 iterable만 반환하면 이를 stream 으로 사용하고자 하는 사용자는 사용이 힘들다. 따라서 adapter method를 제공하는 방안이 있다.

1
2
3
4
// Iterable<E>를 Stream<E>로 중개해주는 adapter 
public static <E> Stream<E> streamOf(Iterable<E> iterable){
return StreamSupport.stream(iterable.spliterator(),false);
}

객체 시퀀스 반환 타입에 대한 고려사항

객체 시퀀스를 반환할 일이 있는데 오직 stream pipeline에서만 쓰일 것을 안다면 stream을 그대로 반환하면 되고, 반복문에서만 쓰일 것을 안다면 iterable을 반환해주면 된다. 하지만 공개 API를 작성해야 할떄는 양쪽 모두 지원해주는 것이 좋다.

Collection 인터페이스는 iterable의 하위 타입이면서 stream method도 제공하니 반복문과 stream을 동시에 지원한다. 따라서 공개 API 반환 타입에는 collection을 포함한 하위 타입을 쓰는게 좋다.

하지만 단지 collection을 반환한다는 이유로 메모리에 올라가기 큰 sequence도 collection 타입으로 반환하면 안된다.
예를 들면 임의의 집합이 매개변수로 주어지면 멱집합(합 집합의 모든 부분집합을 원소로 하는 집합)을 반환하는 상황이라고 할때, 임의의 집합의 원소의 개수가 n개라고 하면 멱집합의 원소의 개수는 2^n개가 된다. 이떄는 아래와 같이 멱집합의 전용 collection 또는 stream과 iterable중 더 사용자가 사용하기 적합한 것을 반환하는 것을 권고하고 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 멱집합의 전용 collection :각 원소의 인덱스를 비트 벡터로 사용한다.
// 인덱스의 n 번쨰 비트 값은 멱집합의 해당원소가 원래 집합의 n번쨰 원소를 포함하는 지 여부를 알려준다.
public class PowerSet {
public static final <E> Collection<Set<E>> of(Set<E> s){
List<E> src = new ArrayList<>(s);
if(src.size() > 30){
throw new IllegalArgumentException("집합에 원소의 개수가 너무 많습니다.(최대 30개) : " +s);
}
return new AbstractList<Set<E>>(){
@Override public int size(){
// 멱집합의 크기는 2를 원래 집합의 원소 수만큼 거듭제곱한것과 동일하기 떄문임.
return 1 << src.size();
}

@Override public boolean contains(Object o){
return o instanceof Set && src.containsAll((Set) o);
}

@Override public Set<E> get(int idx){
Set<E> result = new HashSet();
for (int i= 0; idx !=0; i++ , idex >> =1){
if((idx & 1) == 1){
result.add(src.get(i));
}
}
return result;
}
}
}
}

Comments