티스토리 뷰

반응형

자바7까지는 일련의 원소를 반환하는 메서드의 반환 타입으로 컬렉션 인터페이스나 Iterable, 배열을 써왔다.

그런데, 자바8의 스트림 도입 이후 이 선택이 복잡해졌다.

 

Iterable vs Stream

  • 스트림은 반복을 지원하지 않는다. (반환된 스트림을 for-each 반복 X)
  • 사실 Stream 인터페이스는 Iterable 인터페이스가 정ㅇ의한 추상 메서드를 전부 포함하고, Iterable 인터페이스가 정의한 방식대로 동작 → 그런데도 for-each로 반복 못하는 이유? Stream이 Iterable을 확장(extends)하지 않아서.

 

 

스트림을 반복하기 위한 우회

Stream의 iterator 메서드에 메서드 참조를 건네면 스트림을 반복할 수 있을 것 같다.

그러나 다음 코드는 컴파일 오류가 난다.

// 컴파일 오류 : 타입 추론 한계
for(ProcessHandle ph : ProcessHandle.allProcesses()::iterator) {
}

 

그렇다면 Iterable로 명시적 형변환을 해준다면?

// 너무 난잡하고 직관성 떨어짐 
for(ProcessHandle ph : (Iterable<ProcessHandler>)
                                    ProcessHandle.allProcesses()::iterator) {
}

 

이를 보완하기 위한 어뎁터 메서드 제공

이 경우에는 타입 추론이 잘 되어 명시적 형변환이 필요 없다.

//Stream<E> 를 Iterable<E>로 중계해주는 어뎁터
public static <E> Iterable<E> iterableOf(Stream<E> stream) {
    return stream::iterator;
}

//for-each문으로 스트림 반복 가능 
for(ProcessHandle ph : iterableOf(ProcessHandle.allProcesses())) {
}

API가 스트림만 반환하도록 짯을 경우에 위의 코드처럼 중계해주는 어뎁터를 직접 만들 수 있다.

반대 상황도 마찬가지다. API가 Iterable만 반환한다면?

//Iterable<E> 를 Stream<E>로 중계해주는 어뎁터 
public static <E> Stream<E> streamOf(Iterable<E> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false);
}

 

 

Iterator와 Stream 중 무엇을 반환할까?

공개 API를 작성할 때는 사용자 대부분이 한 방식만 사용할거라는 근거가 없다면 두 방식 모두 반환하도록 작성하자.

  • Collection 인터페이스는 Iterable의 하위타입이고 stream 메서드도 제공한다. → 그래서 원소 시퀀스를 반환하는 공개 API의 반환 타입에는 Collection이나 그 하위 타입을 쓰는게 최선 ex) Arrays 역시 Arrays.asList와 Stream.of 메서드 지원
  • 반환하는 시퀀스 크기가 메모리에 올려도 안전할만큼 작으면 표준 컬렉션 구현체를 반환하는 게 최선 → 이러한 이유로 덩치 큰 시퀀스를 메모리에 올리면 안됨. 이 경우에 전용 컬렉션을 구현하는 방안 검토
    • AbstractCollection 을 활용하여 컬렉션 구현체를 작성할때는 contains와 size도 구현해줘야함

 

 

결론

원소 시퀀스를 반환하는 메서드를 작성할 때는 Iterable(반복)과 Stream 두 방식을 모두 작성하자.

하지만 가능하면 Iterable의 하위타입이면서 stream 을 지원하는 Collection의 하위타입을 반환하도록 하자.

반환할 컬렉션의 원소 개수가 적다면 ArrayList와 같은 표준 컬렉션 구현체를 반환하라.

아니면 시퀀스 크기가 크다면 전용 컬렉션을 구현하는 것을 고민하라.

반응형
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday