2017-07-01 14 views
9

Mam następujący kod, który jest iterowany nad kombinacjami wzorów i "M".."MMMM" Java.Łączenie strumieni podłańcuchów

Moje pytanie brzmi, czy istnieje idiomatyczny (lub po prostu "bardziej idiomatyczny") sposób korzystania z strumieni Java w tym przypadku?

import java.time.LocalDateTime; 
import java.time.format.DateTimeFormatter; 
import java.util.stream.IntStream; 
import java.util.stream.Stream; 

public class DateTimeFormattingStackOverflow { 
    static LocalDateTime dateTime = LocalDateTime.now(); 

    static Stream<String> substrings(String str) { 
     return IntStream.range(1, str.length() + 1) 
       .mapToObj(i -> str.substring(0, i)); 
    } 

    static void printDateTime(String pattern) { 
     DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern); 
     System.out.println(pattern + ", " + dtf.format(dateTime)); 
    } 

    public static void main(String[] args) { 
     Stream<String> patterns = substrings("EEEE") 
       .flatMap(e -> substrings("MMMM").map(m -> e + " " + m)) 
       .map(em -> em + " d"); 

     patterns.forEach(DateTimeFormattingStackOverflow::printDateTime); 
    } 
} 

Wyjście

E M d, Sat 7 1 
E MM d, Sat 07 1 
E MMM d, Sat Jul 1 
E MMMM d, Sat July 1 
EE M d, Sat 7 1 
EE MM d, Sat 07 1 
EE MMM d, Sat Jul 1 
EE MMMM d, Sat July 1 
... 
+0

Czy potrzebujesz go ze strumieniami, czy może to być inny * idiomatyczny * sposób? –

+0

Byłem szczególnie zainteresowany próbą zrobienia tego w "sposób w strumieniach", ale masz rację, że wersje tego kodu z non-Stream mogą być jednakową/bardziej idiomatyczną Javą, i dziękuję ci za twoje sugestie. –

Odpowiedz

0

chciałbym zmienić sposób, w jaki można wygenerować kolejne rozruchy o charakterze: zamiast konstruowania najdłuższy łańcuch ręcznie, a następnie iteracji jego prefiksów, chciałbym skonstruować biegnie z Collection.nCopies coś takiego:

static Stream<String> repeatedRuns(String c, int start, int end) { 
    return IntStream 
     .rangeClosed(start, end) 
     .mapToObj(len -> 
      Collections.nCopies(len, c).stream().collect(Collectors.joining("")) 
     ); 
} 

można by potem zastąpić substrings("EEEE") z repeatedRuns("E", 1, 4).

Demo.

0

Nie wiem, czy to jest bardziej idiomatyczne, ale chciałbym wycisnąć, że do jednej metody (może to być tylko ja ...)

public static Stream<String> combinations(String first, String second, String d, LocalDateTime dateTime) { 

     Stream<String> s = IntStream.range(0, first.length()) 
       .mapToObj(i -> first.substring(0, i + 1)) 
       .flatMap(x -> IntStream.range(0, second.length()) 
         .mapToObj(i -> second.substring(0, i + 1)) 
         .map(m -> String.join(" ", x, m)) 
         .map(res -> res + " d") 
         .peek(System.out::print) 
         .map(DateTimeFormatter::ofPattern) 
         .map(dtf -> dtf.format(dateTime))); 
     return s; 
    } 

    combinations("EEEE", "MMMM", " d", LocalDateTime.now()).forEach(x -> System.out.println(", " + x)); 
+0

Nie uważam, że 'String.join (" ", x, m)' jest lepszy niż po prostu 'x +" "+ m', ale niezależnie od tego, jaki preferujesz styl, nie jest możliwe użycie drugiego z nich w następnym wierszu z '.map (res -> res +" d ")'. Zwłaszcza, że ​​można to zrobić w jednej operacji, 'String.join (" ", x, m," d ")' lub 'x +" "+ m +" d "' ... – Holger

+0

@ Och, masz rację ... dziękuję ty – Eugene

1

używasz IntStream do napęd ciąg znaków. To jeden ze sposobów, aby to zrobić, a tu są dwa inne sposoby:

static Stream<String> substrings(String str) { 
    return str.length() == 1 ? Stream.of(str) : 
      Stream.concat(Stream.of(str), substrings(str.substring(1))); 
} 

Stwarza to Stream rekurencyjnie, a drugi sposób będzie w następujący sposób:

static Stream<String> substrings2(String str) { 
    return Stream.iterate(str, s -> s.substring(1)).limit(str.length()); 
} 

Dotyczy daną funkcję do poprzedni wynik. Ponieważ tworzy nieskończony strumień, musisz użyć limit.

mam lekko zmodyfikowana metodę main, tak aby uniknąć jednego map operacji:

substrings("EEEE") 
    .flatMap(e -> substrings("MMMM").map(m -> e + " " + m + " d")) 
    .forEach(DateTimeFormattingStackOverflow::printDateTime); 

ja naprawdę nie wiem, czy powyższe sposoby są bardziej lub mniej idiomatyczne niż swój sposób, ale jeśli chodzi o mnie, najbardziej idiomatycznych sposób zrobić to zadanie jest z zagnieżdżonej pętli:

String e = "EEEE"; 
String m = "MMMM"; 

for (int i = 0; i < e.length(); i++) 
    for (int j = 0; j < m.length(); j++) 
     printDateTime(e.substring(i) + " " + m.substring(j) + " d"); 

I to może być tłumaczone na Java 8 następująco:

IntStream.range(0, e.length()).boxed() 
    .flatMap(i -> IntStream.range(0, m.length()) 
     .mapToObj(j -> e.substring(i) + " " + m.substring(j) + " d")) 
    .forEach(DateTimeFormattingStackOverflow::printDateTime); 
0

Nie strumieniowałbym w ogóle podłańcuchami.To wystarczy, aby przesyłać na coś reprezentujących szesnaście kombinacji i skonstruować ciąg formatu w jednej operacji:

Stream<String> patterns = IntStream.range(0, 16).map(i -> 15-i) 
    .mapToObj(i -> "EEEE".substring(i>>2)+" "+"MMMM".substring(i&3)+" d"); 

Fakt, że są to cztery razy cztery kombinacje sprawia kwalifikujące obliczeniowy dla taniego bitowej arytmetyki, ale w zasadzie każdy n x m połączenie jest możliwe strumieniowych od 0 do n x m i stosując i/n i i%n wyboru elementu (jak wykonywania operacji substring). Poprzedni krok .map(i -> 15-i) po prostu odwraca kolejność, aby dopasować go do oryginalnego kodu; możesz go pominąć, jeśli kolejność nie ma znaczenia.

Powiązane problemy