<T> Akışı neden Tekrarlanabilir <T> uygulamıyor?


262

Java 8'de , merakla bir yöntemi olan Stream <T> sınıfına sahibiz

Iterator<T> iterator()

Dolayısıyla , tam olarak bu yöntemi gerektiren Yinelenebilir <T> arabirimini uygulamasını beklersiniz , ancak durum böyle değildir.

Bir foreach döngüsü kullanarak bir Stream üzerinde yineleme yapmak istediğimde,

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)) { ... }

Burada bir şey mi eksik?


7
diğer iki yinelenebilir yöntem (forEach ve spliterator) de Stream'de olduğundan
bahsetmiyorum

1
bu StreamIterable
beklenen

12
İyi bir IDE (örneğin IntelliJ) kodunuzu basitleştirmek isteyecektir getIterable()içinreturn s::iterator;
Zhongyu

24
Hiç bir yönteme ihtiyacınız yok. Bir Akışınız varsa ve Tekrarlanabilir bir yer istiyorsanız, stream :: iterator (veya isterseniz, () -> stream.iterator ()) iletin ve işiniz bitti.
Brian Goetz

7
Ne yazık ki yazamıyor for (T element : stream::iterator)Akış da uygulamak olsaydı ben hala tercih ediyorum bu yüzden, Iterableya bir yöntem toIterable().
Thorsten

Yanıtlar:


197

İnsanlar posta listesinden de aynısını sordular ☺. Ana nedeni, Yinelenebilir de tekrarlanabilir bir semantik var, ancak Stream değil.

Bence asıl neden Iterableyeniden kullanılabilirliği ima ederken Stream, sadece bir kez kullanılabilecek bir şey - daha çok bir Iterator.

Eğer Streamuzatılmışsa, Iterablemevcut kod ikinci kez Iterableattığında mevcut kodu şaşırtabilir .Exceptionfor (element : iterable)


22
İlginçtir ki, Java 7'de bu davranışla bazı yinelenebilirler vardı, örneğin DirectoryStream: DirectoryStream Yinelenebilir'i genişletirken, yalnızca tek bir Yineleyiciyi desteklediği için genel amaçlı bir Yinelenebilir değildir; ikinci veya sonraki bir yineleyiciyi elde etmek için yineleyici yönteminin çağrılması IllegalStateException özel durumunu atar. ( openjdk.java.net/projects/nio/javadoc/java/nio/file/… )
roim

32
Ne yazık ki , her zaman birden fazla çağrılabilir Iterableolup olmayacağına dair hiçbir belge yoktur iterator. Oraya koymaları gereken bir şey. Bu, resmi bir spesifikasyondan ziyade standart bir uygulama gibi görünmektedir.
Lii

7
Eğer mazeretler kullanacaklarsa, en azından bir asIterable () yöntemi ekleyebileceklerini ya da yalnızca Yinelenebilir alan tüm yöntemler için aşırı yüklenebileceklerini düşünürsünüz.
Trejkaz

26
Belki de en iyi çözüm Java'nın öngörünümünün Yinelenebilir <T> ve potansiyel olarak bir Akış <T> kabul etmesini sağlamaktır?
devoured elysium

3
@Lii Standart uygulamalar ilerledikçe, oldukça güçlü bir uygulama.
biziclop

161

Bir dönüştürmek için Streambir etmek Iterable, yapabileceğiniz

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

Beklenen bir Streamyönteme geçmek için Iterable,

void foo(Iterable<X> iterable)

basitçe

foo(stream::iterator) 

ancak muhtemelen komik görünüyor; biraz daha açık olmak daha iyi olabilir

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

67
for(X x : (Iterable<X>)stream::iterator)Çirkin görünse de bunu bir döngüde de kullanabilirsiniz . Gerçekten, tüm durum sadece saçma.
Aleksandr Dubinsky

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

11
Bu bağlamda çift kolon sözdizimini anlamıyorum. Arasındaki fark nedir stream::iteratorve stream.iterator()bir eski kabul edilebilir kılan, Iterablefakat ikincisi olmasın?
Daniel C. Sobral

20
Kendime cevap vermek: Iterableişlevsel bir arayüzdür, bu yüzden onu uygulayan bir işlevi geçmek yeterlidir.
Daniel C. Sobral

5
@AleksandrDubinsky ile anlaşmak zorundayım, Asıl sorun, (X x: (Yinelenebilir <X>) akışı :: yineleyici) için sadece büyük bir kod gürültüsünde de olsa aynı işleme izin veren bir symantic geçici çözüm olmasıdır. Ama sonra bu Java ve uzun bir süre List <String> list = new ArrayList (Arrays.asList (dizi)) tolere ettik :) Her ne kadar güçler bize sadece List <String> list = dizi sunabilir. toArrayList (). Ancak asıl saçmalık, herkesi Akışta Her Biçim için kullanmaya teşvik ederek , istisnaların mutlaka işaretlenmesi gerekir [rant sona ermiş].
Koordinatör

10

Bunu StreamExuygulamak Iterable(ve Stream) yanı sıra eksik diğer son derece harika işlevsellik bir dizi işaret etmek istiyorum Stream.


8

Bir akışı bir fordöngüde aşağıdaki gibi kullanabilirsiniz :

Stream<T> stream = ...;

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

( Bu snippet'i burada çalıştırın )

(Bu, Java 8 işlevsel bir arayüz kullanır.)

(Bu, yukarıdaki yorumların bazılarında ele alınmıştır (örneğin, Aleksandr Dubinsky ), ancak daha görünür hale getirmek için bir cevaba çekmek istedim.)


Neredeyse herkesin nasıl derlendiğini fark etmeden önce iki veya üç kez bakması gerekir. Bu çok saçma. (Bu, aynı yorum akışındaki yorumlara yanıt kapsamındadır, ancak daha görünür hale getirmek için yorumu buraya eklemek istedim.)
ShioT

7

kennytm, neden a Streamolarak davranmanın güvensiz olduğunu açıkladı Iterableve Zhong Yu , güvenli olmayan bir şekilde olsa da, Streamolduğu gibi kullanılmasına izin veren bir geçici çözüm sunduIterable . Yeniden kullanılabilir: Bu iki dünyanın en iyisi almak mümkündür Iterablebir gelen Streamtarafından yapılan tüm garantileri karşıladığını Iterableşartname.

Not: SomeTypeburada bir tür parametresi değildir - uygun bir türle (örn. String) Değiştirmeniz veya yansımaya başvurmanız gerekir

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

Büyük bir dezavantaj var:

Tembel yinelemenin faydaları kaybolacak. Geçerli iş parçacığındaki tüm değerleri hemen yinelemeyi planladıysanız, ek yük ihmal edilebilir. Ancak, yalnızca kısmen veya farklı bir iş parçacığında yinelemeyi planladıysanız, bu anında ve tam yinelemenin istenmeyen sonuçları olabilir.

En büyük avantajı, elbette, sadece tek bir kullanıma izin Iterableverirken , yeniden (Iterable<SomeType>) stream::iteratorkullanabilirsiniz. Alıcı kod, koleksiyon üzerinde birden çok kez yinelenecekse, bu sadece gerekli değil, aynı zamanda performans için de faydalıdır.


1
Yanıtlamadan önce kodunuzu derlemeye çalıştınız mı? Çalışmıyor.
Tagir Valeev

1
@TagirValeev Evet, yaptım. T'yi uygun tiple değiştirmeniz gerekir. Örneği çalışma kodundan kopyaladım.
Zenexer

2
@TagirValeev Tekrar IntelliJ'de test ettim. Görünüşe göre IntelliJ bazen bu sözdizimi ile karıştırılıyor; Gerçekten bir örüntü bulamadım. Ancak, kod iyi derlenir ve IntelliJ derledikten sonra hata bildirimini kaldırır. Sanırım bu sadece bir hata.
Zenexer

1
Stream.toArray()bir dizi değil, bir dizi döndürür Iterable, dolayısıyla bu kod hala derlenmez. ancak IntelliJ
derliyor

1
@Zenexer Tekrarlanabilir bir diziye nasıl bir dizi atamayı başardınız?
radiantRazor

3

Streamuygulanmaz Iterable. Genel anlayışı Iterable, sık sık tekrar tekrar yinelenebilecek her şeydir. Streamtekrar oynatılamayabilir.

Akışı temel alan bir yinelemenin de tekrar oynatılabilir olduğu yerlerde düşünebildiğim tek çözüm, akışı yeniden oluşturmaktır. SupplierYeni bir yineleyici oluşturulduğunda her seferinde yeni bir akış örneği oluşturmak için aşağıdakileri kullanıyorum .

    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

Üçüncü taraf kütüphanelerini kullanmanın sakıncası yoksa cyclops-tepki , hem Akışı hem de Yinelenebilir'i uygulayan ve aynı zamanda tekrar oynatılabilen ( açıklanan kennytm sorununu çözen ) bir Akım tanımlar .

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

veya: -

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

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

[Açıklama Ben cyclops-tepki reaksiyonu geliştiricisiyim]


0

Mükemmel değil, ama işe yarayacak:

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

Mükemmel değil çünkü akıştaki tüm öğeleri alır ve bunlara koyar List, ki bu tam olarak ne olduğu Iterableve Streamhakkında değildir. Tembel olmaları gerekiyordu .


-1

Aşağıdaki Stream<Path>gibi kullanarak bir klasördeki tüm dosyaları yineleyebilirsiniz :

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

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

    // ...
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.