1. Gambaran keseluruhan
Dalam tutorial ini, kita akan melihat ciri utama Protonpack yang merupakan perpustakaan yang memperluas API Stream standard dengan menambahkan beberapa fungsi percuma.
Rujuk penulisan ini di sini untuk mengetahui asas-asas Java Stream API.
2. Ketergantungan Maven
Untuk menggunakan perpustakaan Protonpack, kita perlu menambahkan kebergantungan dalam fail pom.xml kami :
com.codepoetics protonpack 1.15
Periksa versi terbaru di Maven Central.
3. Aliran Aliran
Ini adalah kelas utama yang memperluas API Streaming standard Java .
Semua kaedah yang dibincangkan di sini adalah operasi perantaraan, yang bermaksud mereka mengubah Aliran tetapi tidak mencetuskan pemprosesannya.
3.1. takeWhile () dan takeUntil ()
takeWhile () mengambil nilai dari aliran sumber selagi memenuhi syarat yang disediakan :
Stream streamOfInt = Stream .iterate(1, i -> i + 1); List result = StreamUtils .takeWhile(streamOfInt, i -> i < 5) .collect(Collectors.toList()); assertThat(result).contains(1, 2, 3, 4);
Sebaliknya, takeUntil () mengambil nilai sehingga nilai memenuhi keadaan yang dibekalkan dan kemudian berhenti:
Stream streamOfInt = Stream .iterate(1, i -> i + 1); List result = StreamUtils .takeUntil(streamOfInt, i -> i >= 5) .collect(Collectors.toList()); assertThat(result).containsExactly(1, 2, 3, 4);
Di Java 9 dan seterusnya, takeWhile () adalah sebahagian daripada API Aliran standard .
3.2. zip ()
zip () mengambil dua atau tiga aliran sebagai input dan fungsi penggabung. Kaedah mengambil nilai dari kedudukan yang sama setiap aliran dan menyebarkannya ke penggabung .
Ia berlaku sehingga salah satu aliran kehabisan nilai:
String[] clubs = {"Juventus", "Barcelona", "Liverpool", "PSG"}; String[] players = {"Ronaldo", "Messi", "Salah"}; Set zippedFrom2Sources = StreamUtils .zip(stream(clubs), stream(players), (club, player) -> club + " " + player) .collect(Collectors.toSet()); assertThat(zippedFrom2Sources) .contains("Juventus Ronaldo", "Barcelona Messi", "Liverpool Salah");
Begitu juga, zip berlebihan () yang mengambil aliran tiga sumber:
String[] leagues = { "Serie A", "La Liga", "Premier League" }; Set zippedFrom3Sources = StreamUtils .zip(stream(clubs), stream(players), stream(leagues), (club, player, league) -> club + " " + player + " " + league) .collect(Collectors.toSet()); assertThat(zippedFrom3Sources).contains( "Juventus Ronaldo Serie A", "Barcelona Messi La Liga", "Liverpool Salah Premier League");
3.3. zipWithIndex ()
zipWithIndex () mengambil nilai dan zip setiap nilai dengan indeksnya untuk membuat aliran nilai yang diindeks:
Stream streamOfClubs = Stream .of("Juventus", "Barcelona", "Liverpool"); Set
zipsWithIndex = StreamUtils .zipWithIndex(streamOfClubs) .collect(Collectors.toSet()); assertThat(zipsWithIndex) .contains(Indexed.index(0, "Juventus"), Indexed.index(1, "Barcelona"), Indexed.index(2, "Liverpool"));
3.4. menggabungkan ()
penggabungan () berfungsi dengan pelbagai aliran sumber dan penggabung. Ia mengambil nilai kedudukan indeks yang sama dari setiap aliran sumber dan menyebarkannya ke penggabung .
Kaedah ini berfungsi dengan mengambil 1 nilai dari indeks yang sama dari setiap aliran berturut-turut, bermula dari nilai benih .
Kemudian nilai diteruskan ke penggabung, dan nilai gabungan yang dihasilkan diserahkan kembali ke penggabung untuk membuat nilai seterusnya:
Stream streamOfClubs = Stream .of("Juventus", "Barcelona", "Liverpool", "PSG"); Stream streamOfPlayers = Stream .of("Ronaldo", "Messi", "Salah"); Stream streamOfLeagues = Stream .of("Serie A", "La Liga", "Premier League"); Set merged = StreamUtils.merge( () -> "", (valOne, valTwo) -> valOne + " " + valTwo, streamOfClubs, streamOfPlayers, streamOfLeagues) .collect(Collectors.toSet()); assertThat(merged) .contains("Juventus Ronaldo Serie A", "Barcelona Messi La Liga", "Liverpool Salah Premier League", "PSG");
3.5. mergeToList ()
mergeToList () mengambil banyak aliran sebagai input. Ia menggabungkan nilai indeks yang sama dari setiap aliran ke dalam Senarai :
Stream streamOfClubs = Stream .of("Juventus", "Barcelona", "PSG"); Stream streamOfPlayers = Stream .of("Ronaldo", "Messi"); Stream
mergedStreamOfList = StreamUtils .mergeToList(streamOfClubs, streamOfPlayers); List
mergedListOfList = mergedStreamOfList .collect(Collectors.toList()); assertThat(mergedListOfList.get(0)) .containsExactly("Juventus", "Ronaldo"); assertThat(mergedListOfList.get(1)) .containsExactly("Barcelona", "Messi"); assertThat(mergedListOfList.get(2)) .containsExactly("PSG");
3.6. interleave ()
interleave () mencipta nilai ganti yang diambil dari pelbagai aliran menggunakan pemilih .
Kaedah memberikan satu set yang berisi satu nilai dari setiap aliran ke pemilih , dan pemilih akan memilih satu nilai.
Kemudian nilai yang dipilih akan dikeluarkan dari set dan diganti dengan nilai seterusnya dari mana nilai yang dipilih berasal. Pengulangan ini berterusan sehingga semua sumber kehabisan nilai.
Contoh seterusnya menggunakan interleave () untuk membuat nilai bergantian dengan strategi round-robin :
Stream streamOfClubs = Stream .of("Juventus", "Barcelona", "Liverpool"); Stream streamOfPlayers = Stream .of("Ronaldo", "Messi"); Stream streamOfLeagues = Stream .of("Serie A", "La Liga"); List interleavedList = StreamUtils .interleave(Selectors.roundRobin(), streamOfClubs, streamOfPlayers, streamOfLeagues) .collect(Collectors.toList()); assertThat(interleavedList) .hasSize(7) .containsExactly("Juventus", "Ronaldo", "Serie A", "Barcelona", "Messi", "La Liga", "Liverpool");
Ketahuilah bahawa kod di atas adalah untuk tujuan tutorial kerana pemilih round-robin disediakan oleh perpustakaan sebagai Selectors.roundRobin () .
3.7. skipUntil () dan skipWhile ()
skipUntil () melangkau nilai sehingga nilai memenuhi syarat :
Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; List skippedUntilGreaterThan5 = StreamUtils .skipUntil(stream(numbers), i -> i > 5) .collect(Collectors.toList()); assertThat(skippedUntilGreaterThan5).containsExactly(6, 7, 8, 9, 10);
Sebaliknya, skipWhile () melangkau nilai sementara nilainya memenuhi syarat :
Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; List skippedWhileLessThanEquals5 = StreamUtils .skipWhile(stream(numbers), i -> i <= 5 || ) .collect(Collectors.toList()); assertThat(skippedWhileLessThanEquals5).containsExactly(6, 7, 8, 9, 10);
One important thing about skipWhile() is that it will continue streaming after it found the first value that does not meet the condition:
List skippedWhileGreaterThan5 = StreamUtils .skipWhile(stream(numbers), i -> i > 5) .collect(Collectors.toList()); assertThat(skippedWhileGreaterThan5).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
In Java 9 onward, dropWhile() in standard Stream API provides the same functionality as skipWhile().
3.8. unfold()
unfold() generates a potentially infinite stream by applying a custom generator to a seed value and then to each generated value – the stream can be terminated by returning Optional.empty():
Stream unfolded = StreamUtils .unfold(2, i -> (i < 100) ? Optional.of(i * i) : Optional.empty()); assertThat(unfolded.collect(Collectors.toList())) .containsExactly(2, 4, 16, 256);
3.9. windowed()
windowed()creates multiple subsets of source stream as a stream of List. The method takes a source stream, window size and skip value as the parameter.
The List length equals windowsize, while skip value determines where the subset begin relative to the previous subset:
Integer[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8 }; List windowedWithSkip1 = StreamUtils .windowed(stream(numbers), 3, 1) .collect(Collectors.toList()); assertThat(windowedWithSkip1) .containsExactly(asList(1, 2, 3), asList(2, 3, 4), asList(3, 4, 5), asList(4, 5, 6), asList(5, 6, 7));
In addition, the last window is guaranteed to be of the desired size, as we can see in the following example:
List windowedWithSkip2 = StreamUtils.windowed(stream(numbers), 3, 2).collect(Collectors.toList()); assertThat(windowedWithSkip2).containsExactly(asList(1, 2, 3), asList(3, 4, 5), asList(5, 6, 7));
3.10. aggregate()
There are two aggregate() methods that work quite differently.
The first aggregate() groups together elements of equal value according to a given predicate:
Integer[] numbers = { 1, 2, 2, 3, 4, 4, 4, 5 }; List aggregated = StreamUtils .aggregate(Arrays.stream(numbers), (int1, int2) -> int1.compareTo(int2) == 0) .collect(Collectors.toList()); assertThat(aggregated).containsExactly(asList(1), asList(2, 2), asList(3), asList(4, 4, 4), asList(5));
The predicate receives the values in a contiguous manner. Therefore, the above will give a different result if the number is not ordered.
On the other hand, the second aggregate() is simply used to group together elements from the source stream into groups of the desired size:
List aggregatedFixSize = StreamUtils .aggregate(stream(numbers), 5) .collect(Collectors.toList()); assertThat(aggregatedFixSize).containsExactly(asList(1, 2, 2, 3, 4), asList(4, 4, 5));
3.11. aggregateOnListCondition()
aggregateOnListCondition() groups values based on predicate and current active group. The predicate is given the currently active group as a List and the next value. It then must determine if the group should continue or start a new group.
The following example solves a requirement to group contiguous integer values together in a group, where the sum of values in each group must not be greater than 5:
Integer[] numbers = { 1, 1, 2, 3, 4, 4, 5 }; Stream
aggregated = StreamUtils .aggregateOnListCondition(stream(numbers), (currentList, nextInt) -> currentList.stream().mapToInt(Integer::intValue).sum() + nextInt <= 5); assertThat(aggregated) .containsExactly(asList(1, 1, 2), asList(3), asList(4), asList(4), asList(5));
4. Streamable
An instance of Stream isn't reusable. For this reason, Streamable provides reusable streams by wrapping and exposing the same methods as the Stream:
Streamable s = Streamable.of("a", "b", "c", "d"); List collected1 = s.collect(Collectors.toList()); List collected2 = s.collect(Collectors.toList()); assertThat(collected1).hasSize(4); assertThat(collected2).hasSize(4);
5. CollectorUtils
CollectorUtils complements the standard Collectors by adding several useful collector methods.
5.1. maxBy() and minBy()
maxBy()finds the maximum value in a stream using supplied projection logic:
Stream clubs = Stream.of("Juventus", "Barcelona", "PSG"); Optional longestName = clubs.collect(CollectorUtils.maxBy(String::length)); assertThat(longestName).contains("Barcelona");
In contrast, minBy()finds the minimum value using the supplied projection logic.
5.2. unique()
Yang unik () lain melakukan perkara yang sangat mudah: ia mengembalikan satu-satunya nilai jika aliran tertentu telah betul-betul 1 elemen:
Stream singleElement = Stream.of(1); Optional unique = singleElement.collect(CollectorUtils.unique()); assertThat(unique).contains(1);
Jika tidak, unik () akan memberikan pengecualian:
Stream multipleElement = Stream.of(1, 2, 3); assertThatExceptionOfType(NonUniqueValueException.class).isThrownBy(() -> { multipleElement.collect(CollectorUtils.unique()); });
6. Kesimpulannya
Dalam artikel ini, kami belajar bagaimana perpustakaan Protonpack memperluas Java Stream API agar lebih mudah digunakan. Ini menambah kaedah berguna yang mungkin biasa kita gunakan tetapi hilang dari API standard.
Bermula dengan Java 9, beberapa fungsi yang disediakan oleh Protonpack akan tersedia dalam API Stream standard.
Seperti biasa, kodnya boleh didapati di Github.