2017-07-21 72 views
1

什麼是遍歷Java 8 Stream的最佳方式,以便我可以爲每個元素(forEach)執行一個函數,而每10個元素執行一個函數。使用foreach的每個元素顯示在下面。我可以使用什麼函數攔截eveyr第n個元素並執行第二個函數調用?Java 8 Stream運行1函數每個索引而另一個每n次

示例代碼如下: -

Stream<String> strings = Files.lines(path); //some stream 

stream.forEach(s -> System::println)// every element but how can i perform 

回答

1

由於流不支持chunk/split功能,但我已經回答其他問題,所以我在這裏貼上。我希望它可以幫助你,例如:

Stream<String> strings = Files.lines(path); //some stream 

//     v--- skip the last chunk if the chunk size < 10? 
split(strings, 10 , true).forEach((List<String> chunk)->{ 
//   each chunk size is 10 ---^ 

    //      v--- Every element using foreach displayed 
    chunk.forEach(System.out::println); 

    //     v--- another for every 10 element. 
    System.out.println(chunk); 

}); 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Spliterator; 
import java.util.Spliterators; 
import java.util.function.Consumer; 
import java.util.stream.Stream;  
import static java.util.stream.StreamSupport.stream; 


<T> Stream<List<T>> split(Stream<T> source, 
          int limit, boolean skipRemainingElements) { 

    //variables just for printing purpose 
    Spliterator<T> it = source.spliterator(); 
    long size = it.estimateSize(); 
    int c = it.characteristics();// characteristics 

    return stream(new AbstractSpliterator<List<T>>(size, c) { 
     private int thresholds = skipRemainingElements ? limit : 1; 

     @Override 
     @SuppressWarnings("StatementWithEmptyBody") 
     public boolean tryAdvance(Consumer<? super List<T>> action) { 
      List<T> each = new ArrayList<>(limit); 

      while (each.size() < limit && it.tryAdvance(each::add)) ; 

      if (each.size() < thresholds) return false; 

      action.accept(each); 
      return true; 
     } 

    }, false).onClose(source::close); 
} 
5

由於Guava version 22,您可以使用Streams.forEachPair來完成你想要的東西:

Stream<String> strings = Stream.of("a", "b", "c", "d", "e", "f"); 

Stream<Long> indexes = Stream.iterate(0L, i -> i + 1L); 

Streams.forEachPair(strings, indexes, (s, i) -> { 
    if (i % 3 == 0) { 
     System.out.println("Every 3rd element: " + s); 
    } else { 
     System.out.println(s); 
    } 
}); 

這就形成了連續的無限流Long元素,從0L開始,並在內部將此流與strings流拉到一起。 Streams.forEachPair接受一個BiConsumer,它接收每對元素,每個元素來自一個流,並根據索引是否爲第三元素來執行操作。

您可以抽象這更具有以下輔助方法:

forEachNthOrElse(
    strings, 
    3, 
    s -> System.out.println("Every 3rd element: " + s), 
    System.out::println); 

注:

static <T> void forEachNthOrElse(
    Stream<T> stream, 
    int n, 
    Consumer<T> onNthElement, 
    Consumer<T> onAnyOther) { 

    Streams.forEachPair(
     stream, 
     Stream.iterate(0L, i -> i + 1), 
     (t, i) -> { 
      Consumer<T> action = i % n == 0 ? onNthElement : onAnyOther; 
      action.accept(t); 
     }); 
} 

而且,對於同樣的例子,你可以按如下方式使用它我不知道如果您需要始終執行System.out::println操作,或者僅在元素不是第n個元素時才執行。我實施了BiConsumer以執行一個動作或另一個。如果不是這種情況(即,如果你想爲第n個元素執行一個動作並始終執行另一個動作,即使是第n個元素),你應該相應地更改代碼。

+1

ñ新方法,起來! –

1

你也試着自己寫一個有狀態的方法,無需使用任何第三方庫的,它可能是這個樣子:

public <T> void foreachAndEveryNthDoSpecial(Stream<T> stream, Consumer<T> action, int n, Consumer<T> specialAction){ 
    AtomicInteger count = new AtomicInteger(0); 
    stream.forEach(t -> { 
     if(count.incrementAndGet() % n == 0){ 
      specialAction.accept(t); 
     } 
     action.accept(t); 
    }); 
} 

然後可以這樣調用:

foreachAndEveryNthDoSpecial(myStream, t -> yourAction, 10, t -> specialAction); 
2

使用Vavr(Javaslang)也可以是通過使用所提供的方法zipWithIndex相當容易:

stream 
    .zipWithIndex() 
    .forEach(t2 -> { 
     // something that runs for each element 
     if (t2._2 % 10 == 0) { 
      // something that runs every 10 elements 
     } 
    });