[java] Iterable <T>가 stream () 및 parallelStream () 메소드를 제공하지 않는 이유는 무엇입니까?

Iterable인터페이스가 왜 stream()and parallelStream()메소드를 제공하지 않는지 궁금 합니다. 다음 클래스를 고려하십시오.

public class Hand implements Iterable<Card> {
    private final List<Card> list = new ArrayList<>();
    private final int capacity;

    //...

    @Override
    public Iterator<Card> iterator() {
        return list.iterator();
    }
}

트레이딩 카드 게임을하는 동안 카드를 에 넣을 수 있으므로 핸드 를 구현 한 것입니다 .

본질적으로을 감싸고 List<Card>최대 용량을 보장하며 다른 유용한 기능을 제공합니다. 직접 구현하는 것이 좋습니다 List<Card>.

이제, 편의를 Iterable<Card>위해 루프 를 구현 하려는 경우 향상된 for-loop를 사용할 수 있도록 구현하는 것이 좋을 것이라고 생각 했습니다. (내 Hand수업도 get(int index)방법을 제공 하므로 Iterable<Card>내 의견으로는 정당합니다.)

Iterable인터페이스는 다음 (탈락의 javadoc)를 제공합니다 :

public interface Iterable<T> {
    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

이제 다음을 통해 스트림을 얻을 수 있습니다.

Stream<Hand> stream = StreamSupport.stream(hand.spliterator(), false);

그래서 실제 질문에 :

  • 이유는 무엇입니까 Iterable<T>구현하는 기본 방법을 제공하지 stream()그리고 parallelStream()난이 불가능하거나 원치 않는 만들 것 아무것도 볼?

내가 찾은 관련 질문은 다음과 같습니다. 왜 Stream <T>가 Iterable <T>을 구현하지 않습니까?
어느 정도 다른 방법으로 제안하는 것이 이상하게 충분합니다.



답변

이것은 생략이 아니었다. 2013 년 6 월 EG 목록에 대한 자세한 논의가있었습니다.

전문가 그룹에 대한 결정적인 논의는 이 스레드에서 시작 됩니다.

이해하기 쉬운 것처럼 보이는 것은 “명백한”(처음에는 전문가 그룹조차도) stream()보였지만 , 명백한 서명 때문에 매우 일반적인 Iterable사실이 Iterable문제가되었습니다.

Stream<T> stream()

항상 당신이 원하는 것이 아니 었습니다. 예를 들어 Iterable<Integer>스트림 메소드가 오히려를 반환하는 일부 항목은을 반환합니다 IntStream. 그러나이 stream()방법을 계층 구조에서 높이 올리면 불가능합니다. 대신 메소드 를 제공하여 Stream에서 쉽게 만들 수 있도록했습니다 . in 구현은 다음 과 같습니다.Iterablespliterator()stream()Collection

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

모든 클라이언트는 원하는 스트림을 얻을 수 있습니다 Iterable.

Stream s = StreamSupport.stream(iter.spliterator(), false);

결국 우리는에 추가 stream()하는 Iterable것이 실수 라고 결론을 내 렸습니다 .


답변

람다 메일 링리스트에서 조사를했는데 흥미로운 토론이 몇 개 있다고 생각합니다.

지금까지 만족스러운 설명을 찾지 못했습니다. 이 모든 것을 읽은 후에 나는 그것이 단지 생략이라고 결론지었습니다. 그러나 API 디자인 중에 수년에 걸쳐 여러 차례 논의되었다는 것을 알 수 있습니다.

Lambda Libs 사양 전문가

Lambda Libs Spec Experts 메일 링리스트 에서 이에 대한 토론을 찾았습니다 .

아래 의 Iterable / Iterator.stream () 샘 PULLARA는 말했다 :

제한 / 서브 스트림 기능 [1]이 어떻게 구현 될 수 있는지 알아보기 위해 Brian과 협력하고 있었으며 Iterator 로의 전환이 올바른 방법이라고 제안했습니다. 나는 그 해결책에 대해 생각했지만 반복자를 가져 와서 스트림으로 바꾸는 확실한 방법을 찾지 못했습니다. 그것은 거기에 있다는 것이 밝혀졌습니다. 먼저 반복자를 스플리터로 변환 한 다음 스플리터를 스트림으로 변환하면됩니다. 따라서 이것은 Iterable / Iterator 중 하나를 직접 또는 두 가지 모두에서 수행 해야하는지 여부를 다시 검토하게합니다.

내 제안은 적어도 Iterator에 두는 것이므로 두 세계 사이를 깨끗하게 이동할 수 있으며 수행하지 않아도 쉽게 발견 할 수 있습니다.

Streams.stream (Spliterators.spliteratorUnknownSize (iterator, Spliterator.ORDERED))

그리고 Brian Goetz는 다음과 같이 대답했습니다 .

Sam의 요점은 Iterator를 제공하는 라이브러리 클래스가 많지만 반드시 자신의 스플리터를 작성할 수는 없다는 것입니다. 따라서 스트림 호출 (spliteratorUnknownSize (iterator)) 만하면됩니다. Sam은 Iterator.stream ()을 정의하여 제안합니다.

stream () 및 spliterator () 메소드를 라이브러리 작성자 / 고급 사용자를 위해 유지하고 싶습니다.

그리고 나중에

“Spliterator를 작성하는 것이 Iterator를 작성하는 것보다 쉽다는 것을 감안할 때 Iterator 대신 Spliterator를 작성하는 것을 선호합니다 (Iterator is so 90s :)”

하지만 요점을 놓치고 있습니다. 이미 Iterator를 건네주는 클래스가 있습니다 . 그리고 그들 중 많은 사람들이 스플리터 준비가되어 있지 않습니다.

Lambda 메일 링리스트의 이전 토론

이것은 당신이 찾고있는 대답이 아닐 수도 있지만 Project Lambda 메일 링리스트에서 이것은 간단히 논의되었습니다. 아마도 이것은 주제에 대한 광범위한 토론을 촉진하는 데 도움이 될 것입니다.

Iterable의 Streams에서 Brian Goetz의 말에 따르면 :

물러서서 …

스트림을 만드는 방법에는 여러 가지가 있습니다. 요소를 설명하는 방법에 대한 정보가 많을수록 스트림 라이브러리가 더 많은 기능과 성능을 제공 할 수 있습니다. 대부분의 정보를 순서대로 정리하면 다음과 같습니다.

반복자

반복자 + 크기

스플리터

크기를 알고있는 스플리터

크기를 알고있는 모든 스플리터는 모든 하위 분할의 크기를 알고 있습니다.

(Q (요소 당 작업)이 사소한 경우에는 벙어리 반복자에서도 병렬 처리를 추출 할 수 있다는 사실에 놀랄 수도 있습니다.)

Iterable에 stream () 메서드가 있으면 크기 정보가없는 Spliterator로 Iterator를 래핑합니다. 그러나 Iterable 대부분 크기 정보를 가지고 있습니다. 이는 우리가 부족한 개울을 제공하고 있음을 의미합니다. 그다지 좋지 않습니다.

여기에 Stephen이 요약 한 API 연습의 한 가지 단점은 Collection 대신 Iterable을 허용한다는 것입니다. “작은 파이프”를 통해 사물을 강요하므로 유용 할 때 크기 정보를 버립니다. 당신이하려는 모든 것이 최선이라면, 괜찮지 만 더 많은 것을 원한다면 원하는 모든 정보를 보존 할 수 있다면 더 좋습니다.

Iterable이 제공하는 기본값은 실제로 엉뚱한 것입니다-대다수의 Iterables가 정보를 알고 있지만 크기를 버릴 것입니다.

모순?

비록 토론이 전문가 그룹이 초기에 반복자를 기반으로 한 스트림의 초기 설계에 대해 수행 한 변경 사항을 기반으로 한 것처럼 보입니다.

그럼에도 불구하고 Collection과 같은 인터페이스에서 스트림 메소드는 다음과 같이 정의됩니다.

default Stream<E> stream() {
   return StreamSupport.stream(spliterator(), false);
}

Iterable 인터페이스에서 사용되는 것과 정확히 동일한 코드 일 수 있습니다.

그래서 이것이 내가이 답변이 만족 스럽지는 않지만 여전히 토론에 흥미 롭다고 말한 이유입니다.

리팩토링의 증거

메일 링리스트의 분석을 계속하면 splitIterator 메소드가 원래 Collection 인터페이스에있는 것처럼 보이며 2013 년 어느 시점에서이 메소드를 Iterable로 옮겼습니다.

splitIterator를 Collection에서 Iterable로 가져옵니다 .

결론 / 이론?

그런 다음 Iterable에 메소드가 부족하면 생략 할 수 있습니다 .splitIterator를 Collection에서 Iterable로 옮길 때 스트림 메소드를 이동 해야하는 것처럼 보이기 때문입니다.

다른 이유가 있다면 분명하지 않습니다. 다른 사람이 다른 이론을 가지고 있습니까?


답변

사용할 수있는 크기를 알고 있다면 방법 java.util.Collection을 제공합니다 stream().

public class Hand extends AbstractCollection<Card> {
   private final List<Card> list = new ArrayList<>();
   private final int capacity;

   //...

   @Override
   public Iterator<Card> iterator() {
       return list.iterator();
   }

   @Override
   public int size() {
      return list.size();
   }
}

그리고:

new Hand().stream().map(...)

나는 같은 문제에 직면했고 단순히 메소드 를 추가하여 구현이 구현으로 Iterable쉽게 확장 될 수 있다는 것에 놀랐다 (행운 적으로 나는 컬렉션의 크기를 가졌다 🙂AbstractCollectionsize()

또한 재정의를 고려해야합니다 Spliterator<E> spliterator().


답변