リストMap の リストに要素を追加する時、、

Map<String, List<String>>  map;

結構、頻繁にこういう Key と、List のマップを用意して、Key の存在を確認して
List を新しく生成して、put したり、マップから取得して add して
再びマップに put するのを記述するのは、いつもなんとかならないかと思ってました。

そこで、以下の List に add して List を返す UnaryOperator を用意します。

public static <T> UnaryOperator<List<T>> listAddReturn(T t){
   return l->{
      l.add(t);
      return l;
   };
}
Map<String, List<String>> map = new HashMap<>();

map.put("a", Optional.ofNullable(map.get("a"))
   .map(list->listAddReturn("A").apply(list))
   .orElse(listAddReturn("A").apply(new ArrayList<String>()))
);
map.put("a", Optional.ofNullable(map.get("a"))
      .map(list->listAddReturn("B").apply(list))
      .orElse(listAddReturn("B").apply(new ArrayList<String>()))
);
System.out.println(map);

結果

{a=[A, B]}

カウンタ付きの Consumer

Stream ラムダ式の実行中のカウンタ - Oboe吹きプログラマの黙示録 の続き、、、
Throwable を捕捉できる Consumer 以下を継承するようにして、
yipuran-core/ThrowableConsumer.java at master · yipuran/yipuran-core · GitHub
3要素の Consumer 、トリプル Consumer を以下のように用意すれば、
カウンタ付きの Consumer もまとめることができる。

triple Consumer

import java.io.Serializable;
import java.util.Objects;
/**
 * triple Consumer
 */
@FunctionalInterface
public interface TriConsumer<T, U, V> extends Serializable{
   void accept(T t, U u, V v);

   default TriConsumer<T, U, V> andThen(TriConsumer<? super T, ? super U, ? super V> after){
      Objects.requireNonNull(after);
      return (t, u, v)->{
         accept(t, u, v);
         after.accept(t, u, v);
      };
   }
}

カウンタ付きの Consumer

import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.yipuran.function.ThrowableConsumer;
/**
 * カウンタ付き Consumer.
 */
public interface CountConsumer<T> extends ThrowableConsumer<T>{

   default CountConsumer<T> andThen(CountConsumer<? super T> after){
      Objects.requireNonNull(after);
      return t->{
         accept(t);
         after.accept(t);
      };
   }

   public static <T> Consumer<T> of(BiConsumer<T, Long> biconsumer){
      AtomicLong c = new AtomicLong(1);
      return t->{
         try{
            biconsumer.accept(t, c.getAndIncrement());
         }catch(Exception ex){
            throw new RuntimeException(ex);
         }
      };
   }

   public static <T> Consumer<T> of(BiConsumer<T, Long> biconsumer, TriConsumer<T, Long, Throwable> onCatch){
      AtomicLong c = new AtomicLong(1);
      return t->{
         try{
            biconsumer.accept(t, c.getAndIncrement());
         }catch(Throwable ex){
            onCatch.accept(t, c.get(), ex);
         }
      };
   }
}

使用方法、、、

CountConsumer.of((t, c)->{
   // t : 要素
   // c : long カウンタ
   // TODO

});
CountConsumer.of((t, c)->{
   // t : 要素
   // c : long カウンタ
   // TODO

}, (t, c, x)->{
   // t : 要素
   // c : long カウンタ
   // x : 例外 Throwable
   // TODO

});

BiConsumer, BiPredicate, BiFunction 、、、各々カウンタ付きを
そのうち、、yipuran-core に入れようかと思う。。

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

List<Integer> → int[]

Object型からプリミティブ型でラッパーが必要になる宿命から、、
Integerリストから、int[] が欲しい時の為に、、、

public static int[] toIntArrays(Collection<Integer> values){
   int[] iary = (int[])Array.newInstance(int.class, values.size());
   int n = 0;
   for(Iterator<Integer> it=values.iterator(); it.hasNext();n++)
      iary[n] = it.next();
   return iary;
}

このように単純なメソッドを用意しておくのも良いかも。
悪ノリで、この int n = 0; と for 文のような書き方でなくて、
Stream collect 集計処理の Accumrator を実装記述で書いて、、、

public static int[] toIntArrays(Collection<Integer> values){
   return values.stream().collect(()->(int[])Array.newInstance(int.class, values.size())
   , new BiConsumer<int[], Integer>(){
      private int i = 0;
      @Override
      public void accept(int[] r, Integer v){
         r[i++] = v;
      }
   }, (r, t)->{});
}

AtomicInteger を書かないのも面白いかも。

配列のリストをソートする

先日の投稿、入れ子リスト、List<List<T>> をソートする - Oboe吹きプログラマの黙示録 と、
yipuran-core/NestedListComparator.java at master · yipuran/yipuran-core · GitHub
に続いて配列のリストをソートする場合もついでなので書いてみる。。

前回同様、配列要素の要素が持つ属性値によるソートである。

こちらも、
yipuran-core/Fieldgetter.java at master · yipuran/yipuran-core · GitHub
を使うのですが、以下のとおり。

import java.util.Comparator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.IntStream;
import org.yipuran.util.Fieldgetter;
/**
 * ArrayListComparator
 */
public class ArrayListComparator{

   private int size;

   private ArrayListComparator(int size) {
      this.size = size;
   }

   public static ArrayListComparator of(int size) {
      return new ArrayListComparator(size);
   }

   @SuppressWarnings({ "rawtypes", "unchecked" })
   public <T> Comparator<T[]> build(String name){
      AtomicReference<Comparator<T[]>> cp
      = new AtomicReference<>(Comparator.comparing(t->(Comparable)Fieldgetter.of(e->name).apply(t[0])));
      IntStream.range(1, size).boxed().forEach(i->{
         cp.set(cp.get().thenComparing( Comparator.comparing(t->(Comparable) Fieldgetter.of(e->name).apply(t[i]))));
      });
      return cp.get();
   }

   @SuppressWarnings({ "rawtypes", "unchecked" })
   public <T> Comparator<T[]> build(String...names){
      if (names.length==1) return build(names[0]);
      Comparator<T[]> c = IntStream.range(0, names.length).boxed()
      .<Comparator<T[]>>map(i->Comparator.comparing(t->(Comparable)Fieldgetter.of(e->names[i]).apply(t[0])))
      .collect(()->Comparator.comparing(t->(Comparable)Fieldgetter.of(e->names[0]).apply(t[0]))
      ,(t, r)->r.thenComparing(t),(t, r)->{});
      AtomicReference<Comparator<T[]>> cp = new AtomicReference<>(c);

      IntStream.range(1, size).boxed().forEach(i->{
         IntStream.range(0, names.length).boxed().forEach(ni->{
            cp.set(cp.get().thenComparing( Comparator.comparing(t->(Comparable) Fieldgetter.of(e->names[ni]).apply(t[i]))));
         });
      });
      return cp.get();
   }

   @SuppressWarnings({ "rawtypes", "unchecked" })
   public <T> Comparator<T[]> build(String name, boolean reverse){
      AtomicReference<Comparator<T[]>> cp
      = new AtomicReference<>(Comparator.comparing(t->(Comparable)Fieldgetter.of(e->name).apply(t[0])));
      IntStream.range(1, size).boxed().forEach(i->{
         cp.set(cp.get().thenComparing( Comparator.comparing(t->(Comparable) Fieldgetter.of(e->name).apply(t[i]))));
      });
      if (reverse) cp.set(cp.get().reversed());
      return cp.get();
   }

   @SuppressWarnings({ "rawtypes", "unchecked" })
   public <T> Comparator<T[]> build(boolean reverse, String...names){
      if (names.length==1) return build(names[0]);
      Comparator<T[]> c = IntStream.range(0, names.length).boxed()
      .<Comparator<T[]>>map(i->Comparator.comparing(t->(Comparable)Fieldgetter.of(e->names[i]).apply(t[0])))
      .collect(()->Comparator.comparing(t->(Comparable)Fieldgetter.of(e->names[0]).apply(t[0]))
      ,(t, r)->r.thenComparing(t),(t, r)->{});
      AtomicReference<Comparator<T[]>> cp = new AtomicReference<>(c);

      IntStream.range(1, size).boxed().forEach(i->{
         IntStream.range(0, names.length).boxed().forEach(ni->{
            cp.set(cp.get().thenComparing( Comparator.comparing(t->(Comparable) Fieldgetter.of(e->names[ni]).apply(t[i]))));
         });
      });
      if (reverse) cp.set(cp.get().reversed());
      return cp.get();
   }
}


使用例、、

List<Item[]>  list = new ArrayList<>();

// list セット済

list.stream()
.sorted(ArrayListComparator.of(3).build("price"))
.forEach(a->{
   System.out.println(Arrays.toString(a));
});

Repetition Permutation の生成数

要素の繰り返し有りの順列 Permutation が生成するリストは、Java では、長さ8以上ではメモリ不足になってしまう。
生成する順列を溜めなくても、7以上はかなり遅くなる。

以下のとおり長さに対して結果の数はとても大きくなる。

data length List
A,B,C 3 27
A,B,C,D 4 256
A,B,C,D,E 5 3,126
A,B,C,D,E,F 6 46,656
A,B,C,D,E,F,G 7 823,543
A,B,C,D,E,F,G,H 8 16,777,216
A,B,C,D,E,F,G,H,I 9 387,420,489
A,B,C,D,E,F,G,H,I,J 10 10,000,000,000

そこで考えたのが、Predicate で絞り込みまたは1つでも欲しい条件の結果を取得したら
早々と順列を見つけるループから抜けることである。
RepeatablePermutation は、この為のメソッドを用意した。
yipuran-core/RepeatablePermutation.java at master · yipuran/yipuran-core · GitHub

入れ子リスト、List<List<T>> をソートする

入れ子リストをソートしなければいけない要件は少ないとは思います。

任意クラスが、Comparable を実装しているなら簡単ですがそうでなく、中の属性値でソートしなければならない時は
面倒です。

例)Item は,属性 Integer price を Not NULL で持っており、この同じ数の List 同志をソートします。

public Comparator<List<Item>> build(String name){
   AtomicReference<Comparator<List<Item>>> cp = new AtomicReference<>(Comparator.comparing(u->u.get(0).getPrice()));
   IntStream.range(1, size).boxed().forEach(i->{
       cp.set(cp.get().thenComparing(Comparator.comparing(u->u.get(i).getPrice())));
   });
   return cp.get();
}

こんなComparatorを生成しなければなりません。
なんとか汎用的にならないかと考えたのが、、
以下 Fieldgetter を使って、、
yipuran-core/Fieldgetter.java at master · yipuran/yipuran-core · GitHub

@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> Comparator<List<T>> build(String name){
   AtomicReference<Comparator<List<T>>> cp = new AtomicReference<>(Comparator.comparing(u->(Comparable)Fieldgetter.of(t->name).apply(u.get(0))));
   IntStream.range(1, size).boxed().forEach(i->{
      cp.set(cp.get().thenComparing( Comparator.comparing(t->(Comparable) Fieldgetter.of(e->name).apply(t.get(i)))));
   });
   return cp.get();
}

と、属性名を指定する形で、 IntStream.range(1, size). の size は、比較する List の List のサイズです。
そこで、まとめたのが、、
yipuran-core/NestedListComparator.java at master · yipuran/yipuran-core · GitHub
ですが、
リスト同志をソートすることなんて、やはり良い設計には思えない。。。