Pengantar jOOL

1. Ikhtisar

Pada artikel ini, kita akan melihat jOOLperpustakaan - produk lain dari jOOQ.

2. Ketergantungan Maven

Mari kita mulai dengan menambahkan dependensi Maven ke pom.xml Anda :

 org.jooq jool 0.9.12  

Anda dapat menemukan versi terbaru di sini.

3. Antarmuka Fungsional

Di Java 8, antarmuka fungsional sangat terbatas. Mereka menerima jumlah maksimum dua parameter dan tidak memiliki banyak fitur tambahan.

jOOL memperbaikinya dengan membuktikan satu set antarmuka fungsional baru yang dapat menerima bahkan 16 parameter (dari Function1 hingga Function16) dan diperkaya dengan metode praktis tambahan.

Misalnya, untuk membuat fungsi yang membutuhkan tiga argumen, kita dapat menggunakan Function3:

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

Di Java murni, Anda perlu menerapkannya sendiri. Selain itu, antarmuka fungsional dari jOOL memiliki metode applyP Partially () yang memungkinkan kita untuk melakukan aplikasi parsial dengan mudah:

Function2 addTwoNumbers = (v1, v2) -> v1 + v2; Function1 addToTwo = addTwoNumbers.applyPartially(2); Integer result = addToTwo.apply(5); assertEquals(result, (Integer) 7);

Ketika kita memiliki metode berjenis Function2 , kita dapat mengubahnya dengan mudah menjadi Java BiFunction standar dengan menggunakan metode toBiFunction () :

BiFunction biFunc = addTwoNumbers.toBiFunction();

Demikian pula, ada metode toFunction () dalam tipe Function1 .

4. Tupel

Tupel adalah konstruksi yang sangat penting dalam dunia pemrograman fungsional. Ini adalah wadah yang diketik untuk nilai di mana setiap nilai dapat memiliki tipe yang berbeda. Tupel sering digunakan sebagai argumen fungsi .

Mereka juga sangat berguna saat melakukan transformasi pada aliran peristiwa. Di jOOL, kami memiliki tupel yang dapat membungkus dari satu hingga enam belas nilai, disediakan oleh Tuple1 hingga tipe Tuple16 :

tuple(2, 2)

Dan untuk empat nilai:

tuple(1,2,3,4); 

Mari pertimbangkan contoh ketika kita memiliki urutan tupel yang membawa 3 nilai:

Seq
    
      personDetails = Seq.of( tuple("michael", "similar", 49), tuple("jodie", "variable", 43)); Tuple2 tuple = tuple("winter", "summer"); List
     
       result = personDetails .map(t -> t.limit2().concat(tuple)).toList(); assertEquals( result, Arrays.asList(tuple("michael", "similar", "winter", "summer"), tuple("jodie", "variable", "winter", "summer")) );
     
    

Kita dapat menggunakan berbagai jenis transformasi pada tupel. Pertama, kita memanggil metode limit2 () untuk mengambil hanya dua nilai dari Tuple3. Kemudian, kita memanggil metode concat () untuk menggabungkan dua tupel.

Hasilnya, kami mendapatkan nilai yang berjenis Tuple4 .

5. Urutan

The Seq membangun menambahkan metode-tingkat yang lebih tinggi pada Streaming sementara sering menggunakan metode yang di bawah.

5.1. Berisi Operasi

Kita dapat menemukan beberapa varian metode yang memeriksa keberadaan elemen dalam Seq. Beberapa metode tersebut menggunakan metode anyMatch () dari kelas Stream :

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. Bergabunglah dengan Operasi

Saat kita memiliki dua aliran dan ingin menggabungkannya (mirip dengan operasi gabungan SQL dari dua kumpulan data), menggunakan kelas Stream standar bukanlah cara yang sangat elegan untuk melakukan ini:

Stream left = Stream.of(1, 2, 4); Stream right = Stream.of(1, 2, 3); List rightCollected = right.collect(Collectors.toList()); List collect = left .filter(rightCollected::contains) .collect(Collectors.toList()); assertEquals(collect, Arrays.asList(1, 2));

Kita perlu mengumpulkan aliran kanan ke daftar, untuk mencegah java.lang.IllegalStateException: aliran telah dioperasikan atau ditutup. Selanjutnya, kita perlu membuat operasi efek samping dengan mengakses daftar rightCollected dari metode filter . Ini rawan kesalahan dan bukan cara yang elegan untuk menggabungkan dua kumpulan data.

Untungnya, Seq memiliki metode yang berguna untuk melakukan gabungan dalam, kiri dan kanan pada kumpulan data. Metode tersebut menyembunyikan implementasinya yang mengekspos API elegan.

Kita bisa melakukan inner join dengan menggunakan metode 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)) );

Kita dapat melakukan gabungan kanan dan kiri sesuai:

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

Bahkan ada metode crossJoin () yang memungkinkan untuk membuat gabungan kartesian dari dua kumpulan data:

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. Manipulating a Seq

Seq has many useful methods for manipulating sequences of elements. Let's look at some of them.

We can use a cycle() method to take repeatedly elements from a source sequence. It will create an infinite stream, so we need to be careful when collecting results to a list thus we need to use a limit() method to transform infinite sequence into finite one:

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

Let's say that we want to duplicate all elements from one sequence to the second sequence. The duplicate() method does exactly that:

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

Returning type of a duplicate() method is a tuple of two sequences.

Let's say that we have a sequence of integers and we want to split that sequence into two sequences using some predicate. We can use a partition() method:

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. Grouping Elements

Grouping elements by a key using the Stream API is cumbersome and non-intuitive – because we need to use collect() method with a Collectors.groupingBy collector.

Seq hides that code behind a groupBy() method that returns Map so there is no need to use a collect() method explicitly:

Map
    
      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. Skipping Elements

Let's say that we have a sequence of elements and we want to skip elements while a predicate is not matched. When a predicate is satisfied, elements should land in a resulting sequence.

We can use a skipWhile() method for that:

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

We can achieve the same result using a skipUntil() method:

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

5.6. Zipping Sequences

When we're processing sequences of elements, often there is a need to zip them into one sequence.

The zip() API that could be used to zip two sequences into one:

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

The resulting sequence contains tuples of two elements.

When we are zipping two sequences, but we want to zip them in a specific way we can pass a BiFunction to a zip() method that defines the way of zipping elements:

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

Sometimes, it is useful to zip sequence with an index of elements in this sequence, via the zipWithIndex() API:

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

6. Converting Checked Exceptions to Unchecked

Let's say that we have a method that takes a string and can throw a checked exception:

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

Then we want to map elements of a Stream applying that method to each element. There is no way to handle that exception higher so we need to handle that exception in a map() method:

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

There is not much we can do with that exception because of the design of functional interfaces in Java so in a catch clause, we are converting a checked exception into unchecked one.

Fortunately, in a jOOL there is an Unchecked class that has methods that can convert checked exceptions into unchecked exceptions:

List collect = Stream.of("a", "b", "c") .map(Unchecked.function(elem -> methodThatThrowsChecked(elem))) .collect(Collectors.toList()); assertEquals( collect, Arrays.asList(1, 1, 1) );

Kami menggabungkan panggilan ke methodThatThrowsChecked () ke dalam metode Unchecked.function () yang menangani konversi pengecualian di bawahnya.

7. Kesimpulan

Artikel ini menunjukkan cara menggunakan pustaka jOOL yang menambahkan metode tambahan berguna ke API Streaming standar Java .

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di proyek GitHub - ini adalah proyek Maven, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.