Stream ラムダ式の実行中のカウンタ

Stream の forEach や、map 、filter 、様々なラムダ式実行の中で、カウンタが必要な時、
しかたなく、AtomicInteger や、グローバルスコープに置いたカウンタインクリメント参照を
よく書いてました。
また、ネット検索してよく紹介されてる例は、実行処理記述で関数型インターフェース定義を書き直す
方法で、以下のような書き方です。

Stream.iterate('A', e->(char)(e+1)).limit(8)
.forEach(new Consumer<Character>(){
   int c = 1;
   @Override
   public void accept(Character t){
      System.out.println(c + ":" + t);
      c++;
   }
});

これだと、t->{ } というラムダ式記述ではなくなって、
関数型インターフェース使用の旨味がなくなってしまいます。

それでは、、、
BiConsumer で書いて、Consumer を返す static メソッドを用意する方法

public static <T> Consumer<T> countCounsumer(BiConsumer<T, Long> biconsumer){
   AtomicLong c = new AtomicLong(1);
   return t->{
      biconsumer.accept(t, c.getAndIncrement());
   };
}

以下のように書くことができます。

Stream.iterate('A', e->(char)(e+1)).limit(8)
.forEach(countCounsumer((t, i)->{
   System.out.println(i + ":" + t);
}));

static メソッドを都度々、書くのをさけるなら、Consumer を 継承して、、

import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
 * CountCounsumer
 */
public interface CountCounsumer<T> extends Consumer<T>{
   public static <T> Consumer<T> of(BiConsumer<T, Long> biconsumer){
      AtomicLong c = new AtomicLong(1);
      return t->{
         biconsumer.accept(t, c.getAndIncrement());
      };
   }
}

この方が汎用性もあります。

Stream.iterate('A', e->(char)(e+1)).limit(8)
.forEach(CountCounsumer.of((t, i)->{
   System.out.println(i + ":" + t);
}));

Stream でカウンタが欲しいのは、forEach だけではないはずで、
例えば、filter ~ Predicate だって、欲しいこともあります。

上と同じように、、static メソッドなら、、

public static <T> Predicate<T> countPredicate(BiPredicate<T, Long> bipredicate){
   AtomicLong c = new AtomicLong(1);
   return t->{
      return bipredicate.test(t, c.getAndIncrement());
   };
}

例)

Stream.iterate('A', e->(char)(e+1)).limit(8)
.filter(countPredicate((e, i)->i % 2 == 1))
.forEach(CountCounsumer.of((t, i)->{
   System.out.println(i + ":" + t);
}));

Predicate を継承して、、

import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
/**
 * CountPredicate
 */
public interface CountPredicate<T> extends Predicate<T>{
   public static <T> Predicate<T> of(BiPredicate<T, Long> bipredicate){
      AtomicLong c = new AtomicLong(1);
      return t->{
         return bipredicate.test(t, c.getAndIncrement());
      };
   }
}

例)

Stream.iterate('A', e->(char)(e+1)).limit(8)
.filter(CountPredicate.of((e, i)->i % 2 == 1))
.forEach(CountCounsumer.of((t, i)->{
   System.out.println(i + ":" + t);
}));