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


241

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>을 구현하지 않습니까?
어느 정도 다른 방법으로 제안하는 것이 이상하게 충분합니다.


1
나는 이것이 Lambda Mailing List에 대한 좋은 질문이라고 생각한다 .
Edwin Dalorzo

스트림을 반복하는 것이 왜 이상합니까? 다른 방법 break;으로 반복 할 수 있습니까? (좋아요, Stream.findFirst()해결책 일 수도 있지만 모든 요구를 충족하지 못할 수도 있습니다 ...)
glglgl

실용적인 해결 방법은 Java 8 JDK사용하여 Iterable을 스트림으로 변환을 참조하십시오 .
Vadzim

답변:


298

이것은 생략이 아니었다. 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것이 실수 라고 결론을 내 렸습니다 .


8
우선, 질문에 답변 해 주셔서 감사합니다. 왜 Iterable<Integer>(당신이 말하고 있다고 생각합니까?)가을 반환하고 싶지만 여전히 궁금 합니다 IntStream. iterable이 오히려 오히려 PrimitiveIterator.OfInt아닐까요? 아니면 다른 사용 사례를 의미합니까?
skiwi

139
위의 논리가 Iterable에 적용될 것으로 예상되는 것이 이상합니다 (누군가 IntStream을 반환하기를 원할 수 있기 때문에 stream ()을 가질 수 없음). 동일한 양의 생각이 Collection에 동일한 방법을 추가하지는 않았습니다 ( 내 Collection <Integer>의 stream ()이 IntStream도 반환하도록 할 수 있습니다.) 둘 다 존재했거나 둘 다 존재하지 않든, 사람들은 아마도 자신의 삶에 착수했을 것입니다. 다른, 그것은 눈부신 생략이된다 ...
Trejkaz

6
Brian McCutchon : 더 이해가됩니다. 사람들이 말다툼에 질려서 안전하게 연주하기로 결정한 것 같습니다.
Jonathan Locke 2016 년

44
이것이 의미가 있지만 대안적인 static이없는 이유가 있는데 Stream.of(Iterable), API 문서를 읽음으로써 적어도 메소드를 합리적으로 발견 할 수있게하는 이유가 있습니까? 심지어 보고 에서 StreamSupport"대부분 도서관 작가는"있다 "낮은 수준의 작업을"제공하는 등 문서에 설명되어있는.
Jules

9
나는 Jules에 전적으로 동의합니다. StreamSupport.stream (iter.spliterator (), false) 대신 정적 메소드 Stream.of (Iteratable iter) 또는 Stream.of (Iterator iter)를 추가해야합니다.
user_3380739

23

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

지금까지 만족스러운 설명을 찾지 못했습니다. 이 모든 것을 읽은 후에 나는 그것이 단지 생략이라고 결론지었습니다. 그러나 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로 옮길 때 스트림 메소드를 이동 해야하는 것처럼 보이기 때문입니다.

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


답장을 보내 주셔서 감사하지만 그 이유에 동의하지 않습니다. 당신이 무시하는 순간 spliterator()의를 Iterable, 모든 문제가 해결되고, 당신은 하찮게 구현할 수 stream()parallelStream()..
skiwi

@ skiwi 그래서 이것이 아마도 대답이 아니라고 말한 이유입니다. 전문가 그룹이 왜 결정을 내 렸는지 알기 어렵 기 때문에 토론에 추가하려고합니다. 우리가 할 수있는 일은 메일 링리스트에서 법의학을 시도하고 어떤 이유가 있는지 알아내는 것입니다.
Edwin Dalorzo

1
@skiwi 나는 다른 메일 링리스트를 검토하고 토론에 대한 더 많은 증거와 진단을 이론화하는 데 도움이되는 아이디어를 발견했습니다.
Edwin Dalorzo

노력해 주셔서 감사합니다. 메일 링리스트를 효율적으로 분리하는 방법을 배워야합니다. 그들이 인용 부호가있는 일반 텍스트 전자 메일을 읽는 것이 정확히 효율적이지 않기 때문에 포럼이나 다른 것과 같은 현대적인 방식으로 시각화 될 수 있다면 도움이 될 것입니다.
skiwi

6

사용할 수있는 크기를 알고 있다면 방법 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().

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.