2017-02-10 26 views
6

Chcę wykonywać wiele zadań na jednym ciągu. trzeba uzyskać ciąg i wyodrębnić różne sub-ciągi przy użyciu separatora ("/"), a następnie odwrócić listy sub-strings i wreszcie połączyć je za pomocą innego ogranicznika (".") taką, że /tmp/test/hello/world/ zamieni się: world.hello.test.tmpStrumienie Java 8 do manipulowania ciągami znaków

Korzystanie Java 7 kod jest w następujący sposób:

String str ="/tmp/test/"; 
List<String> elephantList = new ArrayList<String>(Arrays.asList(str.split("/"))); 

StringBuilder sb = new StringBuilder(); 
for (int i=elephantList.size()-1; i>-1; i--) { 
    String a = elephantList.get(i); 
    if (a.equals("")) 
    { 
     elephantList.remove(i); 
    } 
    else 
    { 
     sb.append(a); 
     sb.append('.'); 
    } 
} 
sb.setLength(sb.length() - 1); 
System.out.println("result" + elephantList + " " + sb.toString()); 

zastanawiałem się, jak mogę zrobić to samo przy użyciu języka Java 8 strumieni, a funkcja przyjmuje na smyczki dołączyć

Odpowiedz

7

Najprostszym sposobem jest zebranie warunki na liście, odwrotnie lista i dołącz na nowym separatora:

import static java.util.stream.Collectors.toCollection; 

List<String> terms = Pattern.compile("/") 
     .splitAsStream(str) 
     .filter(s -> !s.isEmpty()) 
     .collect(toCollection(ArrayList::new)); 

Collections.reverse(terms); 

String result = String.join(".", terms); 

Można to zrobić bez zbierania do listy pośredniego ale będzie mniej czytelny i nie warta dla celów praktycznych.

Inną kwestią do rozważenia jest to, że twoje ciągi wydają się być ścieżkami. Zazwyczaj lepiej jest używać klasy Path niż dzielić ręcznie "/". Oto w jaki sposób to zrobić (to podejście pokazuje również, jak korzystać IntStream nad indeksami strumień na listę do tyłu):

Path p = Paths.get(str); 

result = IntStream.rangeClosed(1, p.getNameCount()) 
     .map(i -> p.getNameCount() - i) // becomes a stream of count-1 to 0 
     .mapToObj(p::getName) 
     .map(Path::toString) 
     .collect(joining(".")); 

ten będzie miał tę zaletę, że OS-niezależny.

+1

zastanawiasz się, czy miałeś na myśli zmniejszenie, kiedy napisałeś "mniej czytelny". 'reduce ((s1, s2) -> String.join (".", s2, s1))" wydaje mi się tak samo czytelny jak 'collect', a następnie' reverse', a następnie 'join' :-) – Roland

+1

Używanie' Path' jest niezależne od systemu operacyjnego, jeśli zadaniem jest naprawdę zamiana znaków separatora domyślnego 'FileSystem', a nie' '/ '' konkretnie, o co pytano w pytaniu .Jeśli mówimy, np. o częściach URI lub zapisach w pliku ZIP, może to nie być prawidłowe rozwiązanie (a właściwie * dodanie * zależność od OS) – Holger

+0

@Holger, masz rację, dlatego zabezpieczyłem się i powiedziałem, że "zwykle" lepiej jest używać 'Path' .Nie znam kontekstu pytania, więc czułem, że byłoby dobrze pokaż oba podejścia: – Misha

4

Jeśli nie chcesz listę pośrednią i po prostu chcesz dołączyć do String odwrotnie:

String delimiter = "."; 
Optional<String> result = Pattern.compile("/") 
           .splitAsStream(str) 
           .filter(s -> ! s.isEmpty()) 
           .reduce((s, s2) -> String.join(delimiter, s2, s)); 

lub po prostu użyć .reduce((s1, s2) -> s2 + '.' + s1); jak to jest prawdopodobnie tak czytelne jak String.join(".", s2, s1); (dzięki Holger za sugestię).

Odtąd można wykonać jedną z następujących czynności:

result.ifPresent(System.out::println); // print the result 
String resultAsString = result.orElse(""); // get the value or default to empty string 
resultAsString = result.orElseThrow(() -> new RuntimeException("not a valid path?")); // get the value or throw an exception 

Innym sposobem korzystania StreamSupport i Spliterator (inspirowany Mishas sugestii użyć Path):

Optional<String> result = StreamSupport.stream(Paths.get(str).spliterator(), false) 
             .map(Path::getFileName) 
             .map(Path::toString) 
             .reduce((s, s2) -> s2 + '.' + s); 

Oczywiście można uprość go, pomijając produkt przejściowy i po prostu wywołaj pożądaną metodę natychmiast:

stream(get(str).spliterator(), false) 
    .map(Path::getFileName) 
    .map(Path::toString) 
    .reduce((s, s2) -> s2 + '.' + s) 
    .ifPresent(out::println); // orElse... orElseThrow 

w ostatnim przykładzie należy dodać następujące importu statyczne:

import static java.lang.System.out; 
import static java.nio.file.Paths.get; 
import static java.util.stream.StreamSupport.stream; 
+0

Czy '(s, s2) -> String.join (ogranicznik, s2, s)' naprawdę wygrywa z '(s1, s2) -> s2 + ogranicznik + s1'? – Holger

+0

dobry punkt!' s2 + "." + s1' na pewno też to zrobi ... nie kwestionowało tego użycia ;-) – Roland

2

Twój kod Java 7 nie jest to co ja nazywam prosta rozwiązanie.
To, w jaki sposób zaimplementować go w Java 7:

String str = "/tmp/test/"; 

StringBuilder sb = new StringBuilder(str.length()+1); 
for(int s=str.lastIndexOf('/'), e=str.length(); e>=0; e=s, s=str.lastIndexOf('/', e-1)) { 
    if(s+1<e) sb.append(str, s+1, e).append('.'); 
} 
if(sb.length()>0) sb.setLength(sb.length() - 1); 
System.out.println("result " + sb); 

i znowu o tym myśleć, to również jak bym wdrożyć go w Java 8, jak przy użyciu API Stream naprawdę nie poprawi ta operacja.

+1

to chyba kwestia gustu ... Myślę, że to rozwiązanie wymaga więcej uwagi od czytelnika ... muszę przeczytać cały kod aby zrozumieć, co się dzieje ... w rozwiązaniu strumienia mogę raczej sprawdzić (krok po kroku) parametry 'map',' reduce' i/lub 'collect'. – Roland

+1

@Roland: Do tej pory nie widziałem przekonującego Rozwiązanie oparte na strumieniu, które umożliwiłoby tak łatwe czytanie.Zdobyłbym rozwiązanie Mishy, ​​używając 'Collections.reverse' do odczytu, ale to też nie jest operacja Stream. Oczywiście w prawdziwej aplikacji umieściłbyś mój kod w nazwać metodę i utworzyć kilka przypadków testowania jednostkowego ... – Holger

+0

Może stałem się fanem_'Stream' i dlatego znajduję 'map (Path :: getFileName) .map (Path :: toString) .reduce ((s1, s2) -> s2 + '.' + s1); 'tak czytelny, jak umieszczenie go na pośredniej liście ... przejście trudnego do zrozumienia kodu na nazwane metody jest pro zawsze dobry pomysł ;-) (niezależnie od tego, które rozwiązanie jest stosowane) ... więc wciąż ... to kwestia gustu ... (i wydajności). :-) – Roland

Powiązane problemy