Yineleyiciyi bir akışa nasıl dönüştürebilirim?


468

Ben Iteratorbir Streamveya daha spesifik olarak yineleyici bir akış olarak "görüntülemek" dönüştürmek için özlü bir yol arıyorum .

Performans nedeniyle, yineleyicinin yeni bir listede bir kopyasını önlemek istiyorum:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Collection<String> copyList = new ArrayList<String>();
sourceIterator.forEachRemaining(copyList::add);
Stream<String> targetStream = copyList.stream();

Yorumlarda bazı önerilere dayanarak, ben de kullanmaya çalıştım Stream.generate:

public static void main(String[] args) throws Exception {
    Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
    Stream<String> targetStream = Stream.generate(sourceIterator::next);
    targetStream.forEach(System.out::println);
}

Ancak, NoSuchElementException(çünkü herhangi bir çağrı olmadığı için hasNext)

Exception in thread "main" java.util.NoSuchElementException
    at java.util.AbstractList$Itr.next(AbstractList.java:364)
    at Main$$Lambda$1/1175962212.get(Unknown Source)
    at java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1351)
    at java.util.Spliterator.forEachRemaining(Spliterator.java:326)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at Main.main(Main.java:20)

Baktım StreamSupportve Collectionshiçbir şey bulamadım.



3
@DmitryGinzburg euh "Sonsuz" bir Akış oluşturmak istemiyorum.
gontard

1
@DmitryGinzburg Stream.generate(iterator::next)çalışıyor mu?
gontard

1
@DmitryGinzburg Bu, sınırlı bir yineleyici için işe yaramaz.
assylias

Yanıtlar:


543

Bunun bir yolu Yineleyiciden bir Ayırıcı oluşturmak ve bunu akışınız için temel olarak kullanmaktır:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
          Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
          false);

Belki daha okunabilir bir alternatif, bir Yinelenebilir kullanmaktır ve bir Yineleyiciden bir Yinelenebilir oluşturmak lambdalarla çok kolaydır, çünkü Yinelenebilir fonksiyonel bir arabirimdir:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();

Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);

26
Akış tembel: kod yalnızca Akışı Yineleyiciye bağlar, ancak gerçek yineleme bir terminal işlemine kadar gerçekleşmez. Bu arada yineleyici kullanırsanız, beklenen sonucu elde edemezsiniz. Örneğin sourceIterator.next(), akışı kullanmadan önce a'yı tanıtabilirsiniz ve efekti görürsünüz (ilk öğe Akış tarafından görülmez).
assylias

9
@assylias, evet gerçekten güzel! Belki gelecekteki okuyucular için bu oldukça sihirli çizgiyi açıklayabilirsiniz Iterable<String> iterable = () -> sourceIterator;. İtiraf etmeliyim ki anlamam biraz zaman aldı.
gontard

7
Ne bulduğumu söylemeliyim. Iterable<T>a, FunctionalInterfacesadece bir arka yöntem açıklanmaktadır iterator(). Yani () -> sourceIteratorbir başlatmasını bir lambda ifadesidir Iterableanonim uygulama olarak örneğini.
Jin Kwon

13
Yine, () -> sourceIterator;kısaltılmış bir şeklidirnew Iterable<>() { @Override public Iterator<String> iterator() { return sourceIterator; } }
Jin Kwon

7
@JinKwon Anonim bir sınıfın kısaltılmış bir biçimi değildir (kapsam ve nasıl derlendiği gibi birkaç ince fark vardır), ancak bu durumda da benzer şekilde davranır.
assylias

122

Sürüm 21'den beri, Guava kütüphanesi Streams.stream(iterator)

Bu ne yapacak @ assylias 'ın cevabı gösterir .



JDK yerel bir astarı destekleyene kadar bunu tutarlı bir şekilde kullanmak çok daha iyi. Gelecekte bunu bulmak (dolayısıyla yeniden düzenleyici), başka bir yerde gösterilen saf JDK çözümlerinden çok daha kolay olacaktır.
drekbour

Bu mükemmel ama… Java'nın yerel yineleyicileri ve akışları nasıl var… ama birinden diğerine gitmek için yerleşik, basit bir yol yok !? Bence oldukça bir ihmal.
Dan Lenski

92

Büyük öneri! İşte benim yeniden kullanılabilir benim:

public class StreamUtils {

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator) {
        return asStream(sourceIterator, false);
    }

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator, boolean parallel) {
        Iterable<T> iterable = () -> sourceIterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }
}

Ve kullanım (statik olarak asStream'i içe aktardığınızdan emin olun):

List<String> aPrefixedStrings = asStream(sourceIterator)
                .filter(t -> t.startsWith("A"))
                .collect(toList());

43

Bu, Java 9'da mümkündür.

Stream.generate(() -> null)
    .takeWhile(x -> iterator.hasNext())
    .map(n -> iterator.next())
    .forEach(System.out::println);

1
Basit, verimli ve alt sınıfa başvurmadan - bu kabul edilen cevap olmalıdır!
martyglaubitz

1
Ne yazık ki bunlar .parallel()akışlarla çalışmıyor gibi görünüyor . SpliteratorSıralı kullanım için bile, gitmekten biraz daha yavaş görünüyorlar .
Thomas Ahle

Ayrıca, yineleyici boşsa ilk yöntem atar. İkinci yöntem şimdilik işe yarıyor, ancak harita ve takeWhile işlevlerinin gerekliliğini ihlal ediyor, bu yüzden üretim kodunda bunu yapmaktan çekinmeyin.
Hans-Peter Störr

Gerçekten, bu kabul edilmiş bir cevap olmalı. parallelKorkak olsa da basitlik şaşırtıcı.
Sven

11

Sınıfı kullanarak oluştur Spliterator, ayırıcı oluşturmak için birden fazla işlev içeriyor, örneğin burada yineleyiciyi parametre olarak alıyorum, ardından kullanarak Akış oluşturIteratorSpliteratorsspliteratorUnknownSizeStreamSupport

Spliterator<Model> spliterator = Spliterators.spliteratorUnknownSize(
        iterator, Spliterator.NONNULL);
Stream<Model> stream = StreamSupport.stream(spliterator, false);

1
import com.google.common.collect.Streams;

ve kullanın Streams.stream(iterator):

Streams.stream(iterator)
       .map(v-> function(v))
       .collect(Collectors.toList());


-4

kullanım Collections.list(iterator).stream()...


6
Kısa olsa da, bu çok düşük performans.
Olivier Grégoire

2
Bu, tüm yineleyiciyi java nesnesine açar ve ardından akışa dönüştürür. Ben bunu tavsiye etmiyorum
iec2011007

3
Bu sadece numaralandırmalar için görünüyor, yineleyiciler için değil.
john16384

1
Genel olarak korkunç bir cevap değil, bir tutam yararlı, ancak soru performanstan bahsediyor ve cevap performans değil.
Kızak
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.