2015-04-26 17 views
5

Java 8'de Streams for getLineNumber() için bir eşdeğer var mı?Java 8 eşdeğeri getLineNumber() Akımlar için

Metin dosyasında bir sözcük aramak ve satır numarasını Tamsayı olarak döndürmek istiyorum. akışları değil koleksiyonları gibi, onların elemanları için bir erişim sağlayacak şekilde tasarlanmamıştır çünkü

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

döndürme türü spliterator uygulanması için bir Liste wundidajah

cevap

4

orada olduğunu sanmıyorum: Bu benim arama yöntemi olduğunu. Çektiğiniz olarak

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

Bu biraz overkill:

Bir geçici çözüm sonra filtreyi uygulayabilirsiniz hangi karşılık gelen endeks oluşturmak için bir IntStream kullanmak sonra, listedeki dosyayı okumak olurdu Tüm dosyanın içeriğini bir liste haline getirme riski vardır; Eğer sizi tatmin etmiyorsa, döngü için iyi yazabilirsin, çok fazla kod değil.

Bu soru ilginizi çekebilir Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip).

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

Yoksa o, bir BufferedReader bir LineNumberReader oluşturmak lines() arayıp dosyasında satır numarasına Her satırı bağlanabilirler: Örneğin, proton-pack kitaplığını kullanarak. Bu yaklaşımın, boru hattı paralel çalışıyorsa 'un başarısız olacağını unutmayın, bu yüzden tavsiye etmem.

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

ben yapabileceğiniz en basit akışından bir yineleyici almak için bu durumda düşünmek ve eski okul arama yapmak: Eğer okumaz bu çözüm sayesinde

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

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

Akarsu işlemlerini kullanabilmek için tüm dosya belleğe sadece.

5

Stream s etkin tembelliğini korumak istiyorsanız (yani sadece ilk eşleşmeyi bulmak istiyorsanız tüm dosyayı okumazsanız), akışı kendiniz oluşturmanız gerekir. Bu çok zor değil, tek engel, hem bir satır numarası hem de String hattı taşımak için bir tuple tipi olmamasıdır. Ya, Map.Entry örneklerini kötüye ya adanmış türü oluşturabilirsiniz: Eğer ilk geçtiği

arayabilir yöntemi kullanarak

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

:

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

o zaman düz ileri bir dere uygulayabilirsiniz

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

veya

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

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

resp: Eğer altta yatan kaynakların uygun kapanmasını sağlamak istiyoruz kuyu üretim kodunda

Veya.

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 olduğunu. Zip yönteminin kaldırılmasından ziyade (paralel() 'özelliği nedeniyle tahmin ediyorum). Bir yandan, potansiyel yan etkilerin veya başarısızlıkların farkında olduğunuz sürece görevinizi paralel hale getirmenin "kolay" olmasını sağladım ve diğer yandan Stream API'sı daha zengin olabilirdi, ama ben Bu kararı verdiğim için hala deneyimsiz olduğum veya habersiz olduğum başka noktalar var. –