2017-02-10 49 views
6

Tôi muốn thực hiện nhiều tác vụ trên một chuỗi. tôi cần phải nhận được một chuỗi và trích xuất khác nhau phụ chuỗi bằng cách sử dụng dấu phân cách ("/"), sau đó đảo ngược danh sách các tiểu chuỗi và cuối cùng tham gia cùng họ sử dụng một delimiter (".") sao cho /tmp/test/hello/world/ sẽ biến thành: world.hello.test.tmpJava 8 Luồng để thao tác chuỗi

sử dụng Java 7 mã được như sau:

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()); 

tôi đã tự hỏi làm thế nào tôi có thể làm điều tương tự sử dụng Java 8 suối và chức năng nó có cho Strings tham gia

Trả lời

7

cách đơn giản nhất là để thu thập các các thuật ngữ vào danh sách, đảo ngược danh sách và tham gia vào dấu phân cách mới:

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); 

Bạn có thể làm điều đó mà không cần phải thu thập vào danh sách trung gian nhưng sẽ ít đọc và không đáng lo ngại vì mục đích thực tế.

Một vấn đề khác cần xem xét là các chuỗi của bạn dường như là đường dẫn. Thường thì tốt hơn nên sử dụng lớp Path thay vì tách bằng "/" theo cách thủ công. Đây là cách bạn sẽ làm điều này (phương pháp này cũng cho thấy làm thế nào để sử dụng IntStream qua chỉ số dòng trên một danh sách ngược):

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(".")); 

Điều này sẽ có lợi thế là hệ điều hành độc lập.

+1

tự hỏi liệu bạn có giảm suy nghĩ hay không, khi bạn viết "ít có thể đọc được". 'reduce ((s1, s2) -> String.join (".", s2, s1))' với tôi có vẻ như có thể đọc được như 'collection', rồi' reverse' và sau đó 'join' :-) – Roland

+1

Sử dụng' Path' là hệ điều hành độc lập, nếu tác vụ thực sự là thay thế các ký tự phân tách của 'FileSystem' mặc định và không phải là' '/' 'cụ thể, như được hỏi trong câu hỏi. Nếu chúng ta đang nói, ví dụ về các phần URI hoặc các mục ZIP file, đây có thể không phải là giải pháp đúng (và thực sự * thêm * – Holger

+0

@ Holger, bạn nói đúng, đó là lý do tại sao tôi giả mạo và nói rằng "thường là" tốt hơn để sử dụng "Đường dẫn". Tôi không biết bối cảnh của câu hỏi, vì vậy tôi cảm thấy nó sẽ là tốt để – Misha

4

Nếu bạn không muốn một danh sách trung gian và chỉ muốn tham gia String reversely:

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

Hoặc chỉ cần sử dụng .reduce((s1, s2) -> s2 + '.' + s1); như nó có lẽ là có thể đọc được như String.join(".", s2, s1); (nhờ Holger cho đề nghị).

Từ đó trở đi bạn có thể làm một trong các cách sau:

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 

Một cách khác sử dụng StreamSupportSpliterator (lấy cảm hứng từ Mishas gợi ý để sử dụng một Path):

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

Tất nhiên bạn có thể đơn giản hóa nó bằng cách bỏ qua số trung gian Optional -object và chỉ cần gọi phương thức mong muốn của bạn ngay lập tức:

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

trong ví dụ cuối cùng bạn sẽ thêm nhập khẩu tĩnh sau:

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

Là '(s, s2) -> String.join (dấu phân cách, s2, s)' thực sự là một thắng lợi trên '(s1, s2) -> s2 + dấu phân tách + s1'? – Holger

+0

điểm tốt!' s2 + '.' + s1' chắc chắn sẽ làm điều đó ... không hỏi rằng việc sử dụng ;-) – Roland

2

mã Java 7 của bạn không phải là điều tôi muốn gọi một giải pháp thẳng về phía trước.
Đây là, làm thế nào tôi sẽ thực hiện nó trong 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); 

và suy nghĩ về nó một lần nữa, điều này cũng là cách tôi muốn thực hiện nó trong Java 8, như sử dụng các API Dòng không thực sự cải thiện hoạt động này.

+1

nó có thể là một vấn đề của hương vị ... Tôi nghĩ rằng giải pháp này đòi hỏi sự chú ý nhiều hơn từ người đọc ... Tôi cần phải đọc toàn bộ mã để hiểu điều gì đang diễn ra ... trong giải pháp luồng tôi có thể kiểm tra (từng bước) các tham số 'map',' reduce' và/hoặc 'collect'. – Roland

+1

@Roland: Cho đến nay, tôi không thấy một sự thuyết phục Giải pháp dựa trên luồng cho phép đọc dễ dàng như vậy.Tôi sẽ xem xét giải pháp của Misha bằng cách sử dụng 'Collections.reverse' có thể đọc được, nhưng đây không phải là một hoạt động Luồng. Hoặc tất nhiên, trong một ứng dụng thực sự, bạn sẽ đặt mã của tôi vào tên phương pháp và tạo ra một số trường hợp thử nghiệm đơn vị… – Holger

+0

có lẽ tôi đã trở thành _'Stream'-fanboy_ và đó là lý do tại sao tôi tìm thấy 'map (Path :: getFileName) .map (Đường dẫn :: toString) .reduce ((s1, s2) -> s2 + '.' + s1); 'có thể đọc được như đặt nó trong danh sách trung gian ... chuyển mã khó hiểu thành các phương thức có tên là pro bably luôn luôn là một ý tưởng tốt ;-) (bất kể giải pháp nào được áp dụng) ... nên vẫn ... đó là vấn đề về hương vị ... (và hiệu năng). :-) – Roland