Stream <T>가 Iterable <T>를 구현하지 않는 이유는 무엇입니까?


261

Java 8에는 Stream <T> 클래스가 있는데 , 여기에는 흥미롭게 메소드가 있습니다

Iterator<T> iterator()

따라서 Iterable <T> 인터페이스를 구현할 것으로 예상 할 수 있는데,이 메소드는 정확히이 메소드를 필요로하지만 그렇지 않습니다.

foreach 루프를 사용하여 Stream을 반복하려면 다음과 같은 작업을 수행해야합니다

public static Iterable<T> getIterable(Stream<T> s) {
    return new Iterable<T> {
        @Override
        public Iterator<T> iterator() {
            return s.iterator();
        }
    };
}

for (T element : getIterable(s)) { ... }

여기에 뭔가 빠졌습니까?


7
말할 것도없이 다른 두 가지 반복 가능한 방법 (forEach 및 spliterator)도 스트림에 있습니다.
njzk2

1
이것은 Stream예상되는 레거시 API 에 전달 하는 데 필요합니다.Iterable
ZhongYu

11
좋은의 IDE (예 : 인 IntelliJ는) 귀하의 코드를 단순화하라는 메시지가 표시됩니다 getIterable()return s::iterator;
중위

23
방법이 전혀 필요하지 않습니다. Stream이 있고 Iterable을 원하면 stream :: iterator (또는 원하는 경우 ()-> stream.iterator ())를 전달하면 완료됩니다.
Brian Goetz

6
불행히도 나는 글을 쓸 수 없으므로 for (T element : stream::iterator)Stream이 구현 Iterable또는 메소드를 선호하는지 여전히 선호합니다 toIterable().
Thorsten

답변:


197

사람들은 이미 메일 링리스트 ☺ 에서 같은 것을 요청했습니다 . 주요 이유는 Iterable에도 반복 가능한 의미가 있지만 Stream은 그렇지 않습니다.

주된 이유는 Iterable재사용 성 을 의미하는 반면, Stream한 번만 사용할 수있는 것 Iterator입니다.

경우 Stream확장 Iterable후 기존의 코드가 놀랄 수 그것은받을 때 Iterable슬로우 Exception그들이 두 번째 시간 for (element : iterable).


22
흥미롭게도 Java 7에는이 동작에 대한 몇 가지 iterable이 이미있었습니다. DirectoryStream : DirectoryStream은 Iterable을 확장하지만 단일 Iterator 만 지원하므로 범용 Iterable은 아닙니다. 반복자 메소드를 호출하여 두 번째 또는 후속 반복자를 확보하면 IllegalStateException이 발생합니다. ( openjdk.java.net/projects/nio/javadoc/java/nio/file/... )
roim

31
불행히도 항상 여러 번 호출 할 수 Iterable있는지 여부 에 대한 문서는 iterator없습니다. 그것은 그들이 거기에 넣어야 할 것입니다. 이것은 공식 사양보다 표준 관행 인 것 같습니다.
Lii

7
그들이 변명을 사용한다면 적어도 asIterable () 메소드를 추가하거나 Iterable 만 취하는 모든 메소드에 과부하를 줄 수 있다고 생각할 것입니다.
Trejkaz

25
아마도 가장 좋은 해결책은 Java foreach가 잠재적으로 Stream <T>뿐만 아니라 Iterable <T>를 수락하도록 만드는 것일 것입니다.
삼켜 버린 엘리시움

3
@Lii 표준 관행이 진행됨에 따라 꽤 강력합니다.
biziclop

160

개종하기 Stream에를 Iterable, 당신은 할 수있다

Stream<X> stream = null;
Iterable<X> iterable = stream::iterator

를 전달하는 Stream방법에 기대하는 Iterable,

void foo(Iterable<X> iterable)

간단히

foo(stream::iterator) 

그러나 아마도 재미있을 것 같습니다. 좀 더 명확하게하는 것이 좋습니다.

foo( (Iterable<X>)stream::iterator );

66
for(X x : (Iterable<X>)stream::iterator)추악하게 보이지만 loop에서 이것을 사용할 수도 있습니다 . 실제로 전체 상황은 터무니없는 것입니다.
Aleksandr Dubinsky

24
@HRJIntStream.range(0,N).forEach(System.out::println)
MikeFHay

11
이 문맥에서 이중 콜론 구문을 이해하지 못합니다. 차이 무엇 stream::iteratorstream.iterator()의 이전 허용하게, Iterable하지만 후자되지는?
Daniel C. Sobral

20
스스로 대답 : Iterable기능 인터페이스이므로 구현하는 함수를 전달하면 충분합니다.
Daniel C. Sobral

5
수신 코드가 kennytm의 답변에 따라 Iterable (예 : 두 개의 개별 for-each 루프)을 재사용하려고 시도하면 이것이 중단된다는 점에 주목할 가치가 있습니다 . 이것은 기술적으로 사양을 위반합니다. 스트림은 한 번만 사용할 수 있습니다. BaseStream :: iterator를 두 번 호출 할 수 없습니다. 첫 번째 호출은 스트림을 종료합니다. JavaDoc에 따르면 : "이것은 터미널 작업입니다." 이 해결 방법이 편리 할 경우 결과 Iterable을 최종 제어 대상이 아닌 코드로 전달해서는 안됩니다.
Zenexer


8

for다음과 같이 루프 에서 스트림을 사용할 수 있습니다 .

Stream<T> stream = ...;

for (T x : (Iterable<T>) stream::iterator) {
    ...
}

( 이 코드를 여기에서 실행 하십시오 )

(이것은 Java 8 기능 인터페이스 캐스트를 사용합니다.)

(이것은 위의 주석 중 일부 (예 : Aleksandr Dubinsky )에서 다루지 만 더 잘 보이게하기 위해 답변으로 가져 가고 싶었습니다.)


거의 모든 사람들이 컴파일 방법을 깨닫기 전에 두 번이나 세 번 봐야합니다. 터무니없는입니다. (이 내용은 동일한 댓글 스트림의 댓글에 대한 답변에서 다루지 만 여기에 댓글을 추가하여 더 잘 보이게하고
싶습니다

7

kennytm 설명 그것은을 치료하는 데 안전하지 않은 이유 Streamint로서 Iterable, 그리고 종 유는 해결 방법이 제공 를 사용하여 허용 Stream같이 Iterable안전하지 않은 방식으로 불구을. 두 세계의 장점을 모두 얻을 수 있습니다. 사양 Iterable에서 제공하는 Stream모든 보증을 충족 하는 재사용이 가능 Iterable합니다.

참고 : SomeType여기서는 유형 매개 변수가 아닙니다. 적절한 유형 (예 :)으로 바꾸 String거나 반사에 의존해야합니다.

Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):

한 가지 큰 단점이 있습니다.

지연 반복의 이점은 사라집니다. 현재 스레드의 모든 값을 즉시 반복하려는 경우 오버 헤드는 무시할 수 있습니다. 그러나 부분적으로 또는 다른 스레드에서만 반복을 계획하는 경우이 즉각적이고 완전한 반복이 의도하지 않은 결과를 초래할 수 있습니다.

물론 가장 큰 장점은을 재사용 할 수 Iterable있지만 (Iterable<SomeType>) stream::iterator한 번만 사용할 수 있다는 것입니다. 수신 코드가 콜렉션에 대해 여러 번 반복되는 경우 이는 필수 일뿐만 아니라 성능에 유리할 수 있습니다.


1
응답하기 전에 코드를 컴파일하려고 했습니까? 작동하지 않습니다.
Tagir Valeev

1
@TagirValeev 그렇습니다. T를 적절한 유형으로 교체해야합니다. 작업 코드에서 예제를 복사했습니다.
Zenexer

2
@TagirValeev IntelliJ에서 다시 테스트했습니다. IntelliJ는 때때로이 구문에 혼동되는 것 같습니다. 나는 그것에 대한 패턴을 실제로 찾지 못했습니다. 그러나 코드는 제대로 컴파일되고 IntelliJ는 컴파일 후 오류 알림을 제거합니다. 나는 그것이 단지 버그라고 생각한다.
Zenexer

1
Stream.toArray()이 아닌 배열을 반환 Iterable하므로이 코드는 여전히 컴파일되지 않습니다. 그러나 IntelliJ가 그것을 컴파일하는 것처럼 보이기 때문에 이클립스의 버그 일 수 있습니다
benez

1
@Zenexer Iterable에 배열을 어떻게 할당 할 수 있었습니까?
radiantRazor

3

Stream구현하지 않습니다 Iterable. 일반적인 이해는 Iterable반복해서 반복 될 수있는 모든 것입니다. Stream재생할 수 없습니다.

내가 생각할 수있는 유일한 해결 방법은 스트림을 기반으로 반복 가능을 재생할 수있는 경우 스트림을 다시 만드는 것입니다. 내가 사용하고 Supplier스트림의 새로운 인스턴스를 만들려면 아래, 매번 새로운 반복자가 생성됩니다.

    Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
    Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
    for(int i : iterable) {
        System.out.println(i);
    }
    // Can iterate again
    for(int i : iterable) {
        System.out.println(i);
    }

2

타사 라이브러리를 사용하는 것이 마음에 들지 않으면 cyclops-react 는 Stream과 Iterable을 모두 구현하고 재생할 수있는 Stream을 정의합니다 ( kennytm 설명 문제 해결 ).

 Stream<String> stream = ReactiveSeq.of("hello","world")
                                    .map(s->"prefix-"+s);

또는 :-

 Iterable<String> stream = ReactiveSeq.of("hello","world")
                                      .map(s->"prefix-"+s);

 stream.forEach(System.out::println);
 stream.forEach(System.out::println);

[공개 나는 사이클롭스 반응의 주요 개발자입니다]


0

완벽하지는 않지만 작동합니다.

iterable = stream.collect(Collectors.toList());

이 스트림에서 모든 항목을 가져오고 그에 넣어 때문에 완벽하지 List정확히하지 않은, Iterable그리고 Stream에 대한 있습니다. 그들은 게으른 것으로 추정된다 .


-1

다음 Stream<Path>과 같이 사용하여 폴더의 모든 파일을 반복 할 수 있습니다 .

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

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