jOOLの紹介

1概要

この記事では、https://github.com/jOOQ/jOOL[jOOL]____ライブラリー - jOOQ からの別の製品を見ていきます。

2 Mavenの依存関係

まず、 pom.xml にMavenの依存関係を追加します。

<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>jool</artifactId>
    <version>0.9.12</version>
</dependency>

最新バージョンはhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.jooq%22%20AND%20a%3A%22jool%22[here]です。

3機能インターフェース

Java 8では、機能的インタフェースはかなり制限されています。それらは2つのパラメータの最大数を受け入れ、多くの追加機能を持っていません。

jOOLは、16個のパラメータでさえも受け入れることができる新しい機能的なインタフェースのセットを証明することによってそれを修正しています。/lambda/function/Function1.html Function16 )__そして追加の便利なメソッドで強化されています。

たとえば、3つの引数を取る関数を作成するために、__Function3を使用できます。

Function3<String, String, String, Integer> lengthSum
  = (v1, v2, v3) -> v1.length() + v2.length() + v3.length();

純粋なJavaでは、あなたはそれを自分で実装する必要があるでしょう。それに加えて、jOOLの機能的インターフェースは applyPartially() メソッドを持っています。

Function2<Integer, Integer, Integer> addTwoNumbers = (v1, v2) -> v1 + v2;
Function1<Integer, Integer> addToTwo = addTwoNumbers.applyPartially(2);

Integer result = addToTwo.apply(5);

assertEquals(result, (Integer) 7);

Function2 型のメソッドがある場合は、 toBiFunction() メソッドを使用して、標準のJava BiFunction に簡単に変換できます。

BiFunction biFunc = addTwoNumbers.toBiFunction();

同様に、 Function1 typeには toFunction() メソッドがあります。

4タプル

タプルは関数型プログラミングの世界では非常に重要な構成要素です。

各値が異なる型を持つことができる値の型付きコンテナです。タプルはしばしば関数の引数として使われます。

一連のイベントを変換するときにも非常に便利です。 jOOLでは、 Tuple1 から Tuple16 までの型で提供される1から16までの値をラップできるタプルがあります。

tuple(2, 2)

そして4つの値に対して:

tuple(1,2,3,4);

3つの値を持つ一連のタプルがある場合の例を考えてみましょう。

Seq<Tuple3<String, String, Integer>> personDetails = Seq.of(
  tuple("michael", "similar", 49),
  tuple("jodie", "variable", 43));
Tuple2<String, String> tuple = tuple("winter", "summer");

List<Tuple4<String, String, String, String>> result = personDetails
  .map(t -> t.limit2().concat(tuple)).toList();

assertEquals(
  result,
  Arrays.asList(tuple("michael", "similar", "winter", "summer"), tuple("jodie", "variable", "winter", "summer"))
);

タプルにはさまざまな種類の変換を使用できます。最初に、 limit2() メソッドを呼び出して Tuple3から2つの値のみを取得します。次に、 concat()__メソッドを呼び出して2つのタプルを連結します。

結果として、 Tuple4 型の値を取得します。

5 Seq

Seq コンストラクトは、 Stream に上位レベルのメソッドを追加しますが、その下ではしばしばそのメソッドを使用します。

5.1. 操作が含まれています

Seq. の中に要素が存在するかどうかをチェックするメソッドのバリエーションがいくつかあります。それらのメソッドの中には、 Stream クラスの anyMatch() メソッドを使用するものがあります。

assertTrue(Seq.of(1, 2, 3, 4).contains(2));

assertTrue(Seq.of(1, 2, 3, 4).containsAll(2, 3));

assertTrue(Seq.of(1, 2, 3, 4).containsAny(2, 5));

5.2. 参加操作

2つのストリームがあり、それらを結合したい場合(2つのデータセットのSQL結合操作に似ています)、標準の Stream クラスを使用することはこれを行うための非常に洗練された方法ではありません。

Stream<Integer> left = Stream.of(1, 2, 4);
Stream<Integer> right = Stream.of(1, 2, 3);

List<Integer> rightCollected = right.collect(Collectors.toList());
List<Integer> collect = left
  .filter(rightCollected::contains)
  .collect(Collectors.toList());

assertEquals(collect, Arrays.asList(1, 2));

java.lang.IllegalStateExceptionが発生しないように、リストに right__ストリームを収集する必要があります。ストリームは既に操作されているか、閉じられています。これはエラーが発生しやすく、2つのデータセットを結合するためのエレガントな方法ではありません。

幸いなことに、** Seq はデータセットの内側、左側、右側の結合を行うための便利なメソッドを持っています。

innerJoin() メソッドを使用して内部結合を実行できます。

assertEquals(
  Seq.of(1, 2, 4).innerJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(),
  Arrays.asList(tuple(1, 1), tuple(2, 2))
);

それに応じて左右の結合を行うことができます。

assertEquals(
  Seq.of(1, 2, 4).leftOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(),
  Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(4, null))
);

assertEquals(
  Seq.of(1, 2, 4).rightOuterJoin(Seq.of(1, 2, 3), (a, b) -> a == b).toList(),
  Arrays.asList(tuple(1, 1), tuple(2, 2), tuple(null, 3))
);

2つのデータセットをデカルト結合することを可能にする crossJoin() メソッドさえあります。

assertEquals(
  Seq.of(1, 2).crossJoin(Seq.of("A", "B")).toList(),
  Arrays.asList(tuple(1, "A"), tuple(1, "B"), tuple(2, "A"), tuple(2, "B"))
);

5.3. Seq を操作する

Seq には一連の要素を操作するための便利な方法がたくさんあります。

それらのいくつかを見てみましょう。

ソースサイクルから繰り返し要素を取り出すために cycle() メソッドを使うことができます。これは無限のストリームを作成するので、結果をリストに集めるときは注意が必要です。したがって、無限のシーケンスを有限のシーケンスに変換するために limit() メソッドを使用する必要があります。

assertEquals(
  Seq.of(1, 2, 3).cycle().limit(9).toList(),
  Arrays.asList(1, 2, 3, 1, 2, 3, 1, 2, 3)
);

あるシーケンスから2番目のシーケンスにすべての要素を複製したいとしましょう。 duplicate() メソッドは、まさにそれを行います。

assertEquals(
  Seq.of(1, 2, 3).duplicate().map((first, second) -> tuple(first.toList(), second.toList())),
  tuple(Arrays.asList(1, 2, 3), Arrays.asList(1, 2, 3))
);

duplicate() メソッドの戻り型は2つのシーケンスのタプルです。

整数のシーケンスがあり、述語を使用してそのシーケンスを2つのシーケンスに分割したいとしましょう。 partition() メソッドを使うことができます。

assertEquals(
  Seq.of(1, 2, 3, 4).partition(i -> i > 2)
    .map((first, second) -> tuple(first.toList(), second.toList())),
  tuple(Arrays.asList(3, 4), Arrays.asList(1, 2))
);

** 5.4. グループ化要素

Stream APIを使用してキーで要素をグループ化するのは面倒で直感的ではありません - collect() メソッドを Collectors.groupingBy コレクターと共に使用する必要があるためです。

Seq Map を返す groupBy() メソッドの背後にあるコードを隠しているので、 collect() メソッドを明示的に使用する必要はありません。

Map<Integer, List<Integer>> expectedAfterGroupBy = new HashMap<>();
expectedAfterGroupBy.put(1, Arrays.asList(1, 3));
expectedAfterGroupBy.put(0, Arrays.asList(2, 4));

assertEquals(
  Seq.of(1, 2, 3, 4).groupBy(i -> i % 2),
  expectedAfterGroupBy
);

5.5. スキップ要素

要素のシーケンスがあり、述語が一致しない間は要素をスキップしたいとしましょう。述語が満たされるとき、要素は結果として生じるシーケンスに着地するべきです。

そのために skipWhile() メソッドを使用できます。

assertEquals(
  Seq.of(1, 2, 3, 4, 5).skipWhile(i -> i < 3).toList(),
  Arrays.asList(3, 4, 5)
);

skipUntil() メソッドを使用しても同じ結果が得られます。

assertEquals(
  Seq.of(1, 2, 3, 4, 5).skipUntil(i -> i == 3).toList(),
  Arrays.asList(3, 4, 5)
);

5.6. ジッピングシーケンス

要素のシーケンスを処理するとき、それらを1つのシーケンスに圧縮する必要があることがよくあります。

2つのシーケンスを1つに圧縮するために使用できる zip() API

assertEquals(
  Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c")).toList(),
  Arrays.asList(tuple(1, "a"), tuple(2, "b"), tuple(3, "c"))
);

結果のシーケンスは2つの要素のタプルを含みます。

2つのシーケンスを圧縮しているが、特定の方法でそれらを圧縮したい場合は、 BiFunction を要素の圧縮方法を定義する zip() メソッドに渡すことができます。

assertEquals(
  Seq.of(1, 2, 3).zip(Seq.of("a", "b", "c"), (x, y) -> x + ":" + y).toList(),
  Arrays.asList("1:a", "2:b", "3:c")
);

場合によっては、 zipWithIndex() APIを使用して、シーケンス内の要素のインデックスでシーケンスを圧縮すると便利です。

assertEquals(
  Seq.of("a", "b", "c").zipWithIndex().toList(),
  Arrays.asList(tuple("a", 0L), tuple("b", 1L), tuple("c", 2L))
);

6. チェック済み例外を未チェック に変換する

文字列を受け取り、チェックされた例外をスローすることができるメソッドがあるとしましょう。

public Integer methodThatThrowsChecked(String arg) throws Exception {
    return arg.length();
}

それから、そのメソッドを各要素に適用する Stream の要素をマッピングします。その例外をより高く処理する方法はないので、 map() メソッドでその例外を処理する必要があります。

List<Integer> collect = Stream.of("a", "b", "c").map(elem -> {
    try {
        return methodThatThrowsChecked(elem);
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}).collect(Collectors.toList());

assertEquals(
    collect,
    Arrays.asList(1, 1, 1)
);

Javaの関数型インタフェースの設計のため、その例外でできることはそれほど多くありません。したがって、catch節では、チェック済み例外を未チェックの例外に変換しています。

  • 幸いなことに、jOOLには、チェック済みの例外を未チェックの例外に変換できるメソッドを持つ__未チェックのクラスがあります。

List<Integer> collect = Stream.of("a", "b", "c")
  .map(Unchecked.function(elem -> methodThatThrowsChecked(elem)))
  .collect(Collectors.toList());

assertEquals(
  collect,
  Arrays.asList(1, 1, 1)
);

methodThatThrowsChecked() の呼び出しを、その下の例外の変換を処理する Unchecked.function() メソッドにラップしています。

7. 結論

この記事では、Java標準の Stream APIに便利な追加メソッドを追加するjOOLライブラリの使い方を説明します。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/libraries[GitHubプロジェクト]にあります。そのまま実行します。