2015-04-26 41 views
5

Có tương đương với getLineNumber() cho các luồng trong Java 8 không?Java 8 tương đương với getLineNumber() cho các luồng

Tôi muốn tìm kiếm một từ trong một tệp văn bản và trả về số dòng là Số nguyên. Đây là phương pháp tìm kiếm của tôi:

result = Files.lines(Paths.get(fileName)) 
      .filter(w -> w.contains(word)) 
      .collect(Collectors.<String> toList()); 
+0

kiểu trả về là một danh sách wundidajah

Trả lời

4

Tôi không nghĩ rằng có, bởi vì con suối không được thiết kế để cung cấp quyền truy cập vào các yếu tố của họ, không giống như các bộ sưu tập.

Một workaround sẽ được đọc các tập tin trong danh sách, sau đó sử dụng một IntStream để tạo ra các chỉ số tương ứng, từ đó thì bạn có thể áp dụng bộ lọc của bạn:

List<String> list = Files.readAllLines(Paths.get("file")); 

//readAllLines current implementation returns a RandomAccessList so 
//using get will not have a big performance impact. 
//The pipeline can be safely run in parallel 
List<Integer> lineNumbers = 
    IntStream.range(0, list.size()) 
       .filter(i -> list.get(i).contains(word)) 
       .mapToObj(i -> i + 1) 
       .collect(toList()); 

Đó là một chút quá mức cần thiết khi bạn lấy rủi ro để tải toàn bộ nội dung của tệp vào danh sách để có thể chỉ giữ lại một vài phần tử sau đó. Nếu nó không thỏa mãn bạn, bạn có thể viết tốt cho vòng lặp, nó không có nhiều mã.

Có thể bạn có thể quan tâm đến câu hỏi này Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip). Ví dụ, sử dụng thư viện proton-pack:

List<Long> lineNumbers = 
    StreamUtils.zipWithIndex(Files.lines(Paths.get("file"))) 
       .filter(in -> in.getValue().contains(word)) 
       .map(in -> in.getIndex() + 1) 
       .collect(toList()); 

Hoặc bạn có thể tạo một LineNumberReader từ một BufferedReader, sau đó gọi lines() và bản đồ mỗi dòng để số dòng của nó trong file. Lưu ý rằng cách tiếp cận này sẽ lỗi nếu đường ống chạy song song, vì vậy tôi không khuyên bạn nên sử dụng nó.

LineNumberReader numberRdr = new LineNumberReader(Files.newBufferedReader(Paths.get("file"))); 

List<Integer> linesNumbers = numberRdr.lines() 
             .filter(w -> w.contains(word)) 
             .map(w -> numberRdr.getLineNumber()) 
             .collect(toList()); 
0

Tôi nghĩ rằng trong trường hợp này đơn giản nhất bạn có thể làm là để có được một iterator từ con suối, và thực hiện tìm kiếm trường học cũ:

Iterator<String> iterator = Files.lines(Paths.get(fileName)).iterator(); 

    int lineNumber = 1; 
    while (iterator.hasNext()) { 
     if(iterator.next().contains(word)) { 
      break; 
     } 
     lineNumber++; 
    } 

Với giải pháp này bạn không đọc toàn bộ tệp vào bộ nhớ chỉ để có thể sử dụng hoạt động của luồng.

5

Nếu bạn muốn giữ bản chất lười biếng hiệu quả của Stream s (tức là không đọc toàn bộ tệp nếu bạn chỉ muốn tìm kết quả đầu tiên), bạn sẽ phải tự xây dựng luồng. Đây không phải là quá khó, trở ngại duy nhất là sự vắng mặt của một loại tuple để thực hiện cả hai, một số dòng và một dòng String. Bạn có thể, lạm dụng Map.Entry trường hoặc tạo một loại chuyên dụng:

static final class NumberedLine { 
    final int number; 
    final String line; 
    NumberedLine(int number, String line) { 
     this.number = number; 
     this.line = line; 
    } 
    public int getNumber() { 
     return number; 
    } 
    public String getLine() { 
     return line; 
    } 
    @Override 
    public String toString() { 
     return number+":\t"+line; 
    } 
} 

sau đó bạn có thể thực hiện một dòng thẳng về phía trước:

public static Stream<NumberedLine> lines(Path p) throws IOException { 
    BufferedReader b=Files.newBufferedReader(p); 
    Spliterator<NumberedLine> sp=new Spliterators.AbstractSpliterator<NumberedLine>(
     Long.MAX_VALUE, Spliterator.ORDERED|Spliterator.NONNULL) { 
      int line; 
      public boolean tryAdvance(Consumer<? super NumberedLine> action) { 
       String s; 
       try { s=b.readLine(); } 
       catch(IOException e){ throw new UncheckedIOException(e); } 
       if(s==null) return false; 
       action.accept(new NumberedLine(++line, s)); 
       return true; 
      } 
     }; 
    return StreamSupport.stream(sp, false).onClose(()->{ 
     try { b.close(); } catch(IOException e){ throw new UncheckedIOException(e); }}); 
} 

sử dụng phương pháp bạn có thể tìm kiếm sự xuất hiện đầu tiên

OptionalInt lNo=lines(path).filter(nl->nl.getLine().contains(word)) 
          .mapToInt(NumberedLine::getNumber) 
          .findFirst(); 

hoặc thu thập tất cả chúng

List<Integer> all=lines(path).filter(nl->nl.getLine().contains(word)) 
          .map(NumberedLine::getNumber) 
          .collect(Collectors.toList()); 

Hoặc, cũng trong mã sản xuất bạn muốn đảm bảo đóng cửa thích hợp các nguồn lực cơ bản:

OptionalInt lNo; 
try(Stream<NumberedLine> s=lines(path)) { 
    lNo=s.filter(nl->nl.getLine().contains(word)) 
     .mapToInt(NumberedLine::getNumber) 
     .findFirst(); 
} 

resp.

List<Integer> all; 
try(Stream<NumberedLine> s = lines(path)) { 
    all = s.filter(nl->nl.getLine().contains(word)) 
      .map(NumberedLine::getNumber) 
      .collect(Collectors.toList()); 
} 
+0

1 để thực hiện spliterator. Thật không may là phương thức zip đã bị loại bỏ (tôi đoán là do tính năng 'parallel()'). Một mặt tôi thích cách này để làm cho nó "dễ dàng" để song song nhiệm vụ của bạn miễn là bạn nhận thức được các tác dụng phụ hoặc thất bại tiềm năng, và mặt khác, API Stream có thể giàu hơn mà không có nó, nhưng tôi đoán có những điểm khác tôi vẫn chưa từng trải qua hoặc không biết điều đó đã đưa ra quyết định này .. –

Các vấn đề liên quan