2014-09-02 4 views
7

Gibt es eine gute Möglichkeit, Java-Streams zum Verschachteln von Elementen in einem Stream mit einem Trennzeichen des gleichen Typs zu verwenden?Elemente in einem Stream mit Trennzeichen verschachteln

// Expected result in is list: [1, 0, 2, 0, 3] 
List<Integer> is = Stream.of(1, 2, 3).intersperse(0).collect(toList()); 

Dies ist ähnlich der intersperse Funktion in Haskell und anderen funktionalen Sprachen.

Ich habe viele Beispiele gesehen, wie man Strings auf ähnliche Weise verknüpft, aber keine Lösungen für allgemeine Listen gefunden hat.

Antwort

8

Sie können es mit flatMap tun, aber Sie werden einen zusätzlichen Separator nach dem letzten Element erhalten:

List<Integer> is = IntStream.of(1, 2, 3) 
          .flatMap(i -> IntStream.of(i, 0)) 
          .collect(toList()); 

Hier ist eine andere Art und Weise, ohne den hinteren Trennzeichen:

List<Integer> is = IntStream.of(1, 2, 3) 
          .flatMap(i -> IntStream.of(0, i)) 
          .skip(1) 
          .collect(toList()); 

Dieses Mal haben wir Fügen Sie das Trennzeichen vor jedem ursprünglichen Element hinzu und entfernen Sie das führende Trennzeichen.

+0

+1 Wenn Sie wissen, die Größe 'n' von der ursprünglichen Liste könnten Sie '.limit (2 * n-1)' verwenden. –

+0

@tobias_k Das ist wahr – Eran

+0

Danke. Aber nicht ein schließendes Trennzeichen zu bekommen, ist eine Frage. Und die Verwendung von 'limit' ist hier etwas zu plump. Aber es ist auch gut, eine Lösung zu haben, wenn Sie ein schließendes Trennzeichen haben wollen! – Lii

6

Sie können eine ähnliche Sache tun, die Collectors.joining tut mit String s mit einem kleinen Hilfsmethode:

static <T> Collector<T,List<T>,List<T>> intersperse(T delim) { 
    return Collector.of(ArrayList::new, (l,e)-> { 
     if(!l.isEmpty()) l.add(delim); 
     l.add(e); 
    }, (l,l2)-> { 
     if(!l.isEmpty()) l.add(delim); 
     l.addAll(l2); 
     return l; 
    }); 

dann können Sie es ähnlich verwenden, um Collectors.joining(delimiter):

List<Integer> l=Stream.of(1, 2, 3).collect(intersperse(0)); 

produziert

[1, 0, 2, 0, 3]

Beachten Sie, dass dies threadsicher ist, da collect diese Vorgänge überwacht.


Wenn Sie nicht in eine Liste sammeln wollen, aber die Trennzeichen als Zwischen-Stream-Operation einfügen können Sie es auf diese Weise tun:

Integer delimiter=0; 
Stream.of(1, 2, 3)/** or any other stream source */ 
     .map(Stream::of) 
     .reduce((x,y)->Stream.concat(x, Stream.concat(Stream.of(delimiter), y))) 
     .orElse(Stream.empty()) 
     /* arbitrary stream operations may follow */ 
+0

Interessante Lösungen! – Lii

+0

Ich mag den '.map (Stream :: of)' Trick. So ähnlich wie 'unflatMap'. – Lii