2016-08-30 2 views
2

Kann ich vorherige und/oder nächste Elemente analysiere mit Java8 irgendwie Ströme?Ist es möglich Elemente von Java8 Streams zu "fusionieren"?

Zum Beispiel kann ich zählen identisch benachbarte Zahlen?

public class Merge { 
    public static void main(String[] args) { 

     Stream<Integer> stream = Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1); 

     // How to get 3, 2, 2, 4 from above 

    } 
} 
+3

In Vanille Java, Single-Pass und ohne Zwischendatenstrukturen: nein . [StreamEx] (https://github.com/amaembo/streamex) Angebote [ 'StreamEx :: runLengths'] (http://amaembo.github.io/streamex/javadoc/one/util/streamex/StreamEx.html# runLengths--) was du suchst. – Flown

+0

Kann ich meine benutzerdefinierten Aggregationsobjekte in 'StreamEx' schreiben? – Dims

+1

Sie können StreamEx als Erweiterung der allgemeinen 'Stream'-API sehen. Es gibt also kein Limit und Sie können eigene Aggregationsobjekte schreiben. – Flown

Antwort

4

Wenn Sie es faul sein wollen, müssen Sie den Stream-API durch Stream.iterator() oder Stream.spliterator() entkommen.

Ansonsten ist der Weg, es zu tun ist, um den Terminalbetrieb Stream.collect(Collector) mit einem benutzerdefinierten Kollektor zu nennen, die den ganzen Strom verbrauchen.


@Test 
public void test() { 
    Stream<Integer> input = Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1); 

    UniqCountSpliterator uniqCountSpliterator = new UniqCountSpliterator(input.spliterator()); 

    long[] output = uniqCountSpliterator.stream() 
      .toArray(); 

    long[] expected = {3, 2, 2, 4}; 

    assertArrayEquals(expected, output); 
} 

import java.util.Spliterator; 
import java.util.function.LongConsumer; 
import java.util.stream.Stream; 
import java.util.stream.StreamSupport; 

public class UniqCountSpliterator implements Spliterator.OfLong { 
    private Spliterator wrapped; 
    private long count; 
    private Object previous; 
    private Object current; 

    public UniqCountSpliterator(Spliterator wrapped) { 
     this.wrapped = wrapped; 
    } 

    public LongStream stream() { 
     return StreamSupport.longStream(this, false); 
    } 

    @Override 
    public OfLong trySplit() { 
     return null; 
    } 

    @Override 
    public long estimateSize() { 
     return Long.MAX_VALUE; 
    } 

    @Override 
    public int characteristics() { 
     return NONNULL | IMMUTABLE; 
    } 

    @Override 
    public boolean tryAdvance(LongConsumer action) { 
     while (wrapped.tryAdvance(next -> current = next) && (null == previous || current.equals(previous))) { 
      count++; 
      previous = current; 
     } 
     if (previous == null) { 
      return false; 
     } 
     action.accept(count); 
     count = 1; 
     previous = null; 
     return true; 
    } 
} 
0

Sie können fast tun es mit flatMap. Es würde für unendliche Ströme funktionieren, mit endlichem Strom sehe ich keine Möglichkeit, das Ende des Stromes in ihm zu erkennen.

Stream<Integer> stream = Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1); 

    Stream<Integer> flatMap = stream.flatMap(new Function<Integer, Stream<Integer>>() { 
     Integer prev = null; 
     int count; 
     public java.util.stream.Stream<Integer> apply(Integer i) { 
      if (i.equals(prev)) { 
       count++; 
       return Stream.empty(); 
      } else { 
       int c = count; 
       count = 1; 
       prev = i; 
       if (c > 0) { 
        return Stream.of(c); 
       } else { 
        return Stream.empty(); 
       } 
      } 
     }; 
    }); 

    flatMap.forEach(i -> { 
     System.out.println(i); 
    }); 

sagte, dass Sie wahrscheinlich viel bessere Kilometerleistung aus rxjava für eine solche Art von Dingen bekommen könnte (wo man Subject nutzen könnten Werte emittieren, wie Sie wollen und in der Lage sein Ende Stream zu erkennen).

Natürlich, wenn Sie Stream-Grenzen entkommen wollen, gibt es viele Möglichkeiten, wie Christ Antwort angegeben.

+1

Fast keine Lösung und wie wäre es mit paralleler Berechnung? – Flown

+0

Es hängt davon ab, was OP _real_ Problem ist. Trick wollte ich hier zeigen, wie Stromelemente emittieren nur in einigen Fällen mit flatMap und leer/Einzelstrom, der nicht offensichtlich sein könnte (seine ursprüngliche Frage der Suche Elemente, auch wenn möglich, würde noch etwas Ähnliches erfordern Änderungen Anzahl der Elemente in Streams). Wie für Parallell ... viel Glück damit, vor allem für unendliche Ströme. –

+1

Ich stimme zu, aber 'flatMap' ist nicht das richtige Werkzeug für diesen Job. Ich muss zugeben, das ist ein netter Trick, aber man kann keine endlichen 'Streams' verarbeiten. Der einzige Weg, um diese Art von Aufgabe zu erledigen, ist der 'Spliterator', wie es Christoffer Hammarström vorgeschlagen hat. Dann können Sie die parallele Berechnung unterdrücken und das Ende des "Streams" behandeln. – Flown

0

Wenn Sie nicht zwei Aussagen dagegen haben, können Sie Setup eine Liste mit den Zählungen zu füllen, und dann verwenden reduce:

List<Integer> counts = new ArrayList<>(); 
Stream.of(0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1).reduce((i, j) -> { 
    if (counts.isEmpty()) { 
     counts.add(1); 
    } 

    if (j == i) { 
     int index = counts.size() - 1; 
     counts.set(index, counts.get(index) + 1); 
    } else { 
     counts.add(1); 
    } 
    return j; 
}); 
Verwandte Themen