Рубрики
Без рубрики

Введение в Protonpack

Узнайте о классах утилит Java Stream API, предоставляемых библиотекой Protonpack

Автор оригинала: baeldung.

1. Обзор

В этом учебнике мы посмотрим на основные особенности Протонпак которая является библиотекой, которая расширяет стандартный Поток API добавив некоторые бесплатные функции.

Обратитесь к это написать здесь чтобы открыть для себя основы Java Поток API.

2. Зависимость от Maven

Чтобы использовать библиотеку Protonpack, мы должны добавить зависимость в нашем пом.xml файл:


    com.codepoetics
    protonpack
    1.15

Проверьте последнюю версию на Мавен Центральный .

3. СтримУтилс

Это основной класс, который расширяет стандартные возможности Java Поток API.

Все методы, обсуждаемые здесь, промежуточные операции , что означает, что они изменяют Поток но не вызывает его обработки.

3.1. takeWhile () и takeUntil()

takeWhile () берет значения из исходных потоков до тех пор, пока они отвечают поставленным :

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

И наоборот, takeUntil () принимает значения до тех пор, пока значение не соответствует поставленным а затем останавливается:

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

В Java 9 далее, takeWhile () является частью стандартного Поток API .

3.2.zip()

zip () принимает два или три потока в качестве ввода и функции комбайна. Метод берет значение из одного и того же положения каждого потока и передает его комбайну .

Он делает это до тех пор, пока у одного из потоков не иссякнут значения:

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

Аналогичным образом, перегруженный zip () что занимает три источника потока:

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 () принимает значения и застегнул каждое значение своим индексом, чтобы создать поток индексных значений:

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. слияние ()

слияние () работает с несколькими потоками источников и комбайном. Это принимает значение одной и той же позиции индекса из каждого потока источника и передает его комбайну .

Метод работает, взяв 1 значение из одного и того же индекса из каждого потока подряд, начиная с семена ценность.

Затем значение передается комбайну, и полученное комбинированное значение подается обратно комбайну для создания следующего значения:

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. слияниеToList()

слияниеToList () принимает несколько потоков в качестве ввода. Это объединяет значение одного и того же индекса из каждого потока в Список :

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. переплетение ()

переплетение () создает альтернативные значения, взятые из нескольких потоков с помощью селектор .

Метод дает набор, содержащий одно значение от каждого потока к селектор , и селектор выберет одно значение.

Затем выбранное значение будет удалено из набора и заменено следующим значением, из которого возникло выбранное значение. Эта итерация продолжается до тех пор, пока у всех источников не выходят значения.

В следующем примере используется переплетение () создавать переменные значения с круговой стратегия:

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

Имейте в виду, что выше код для учебника цели, потому что круговой селектор предоставляется библиотекой в качестве Селекторы.круглыйРобин () .

3.7. пропуститьUntil () и пропуститьWhile ()

skipUntil () пропускает значения до тех пор, пока значение не соответствует :

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

В отличие от этого, skipWhile () пропускает значения, в то время как значения соответствуют :

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

Одна важная вещь в skipWhile () является то, что он будет продолжать потоковое после того, как он нашел первое значение, которое не соответствует условию:

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

В Java 9 далее, dropWhile () в стандартных Поток API обеспечивает ту же функциональность, что и skipWhile () .

3.8. разворачиваться ()

разворачиваться () генерирует потенциально бесконечный поток, применяя пользовательский генератор к семенной стоимости, а затем к каждому генерируемому значению – поток может быть прекращен путем 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. Оконные()

оконные () создает несколько подмножей потока источника в качестве потока Список . Метод принимает исходный поток, размер окна и пропустить значение в качестве параметра.

Список длина равна оконные размер, в то время как кип значение определяет, где начинается подмножество по сравнению с предыдущим подмножеством:

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

Кроме того, последнее окно гарантированно будет нужного размера, как мы можем видеть в следующем примере:

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. агрегат ()

Есть два агрегат () методы, которые работают совершенно по-разному.

Первая агрегат () группы вместе элементы равной ценности в соответствии с данной :

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

Предикат получает ценности смежным образом. Таким образом, выше даст другой результат, если номер не заказан.

С другой стороны, второй агрегат () просто используется для группы элементов из источника потока в группы желаемого размера :

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. агрегатОнлистУверение()

агрегатНаlistCondition () группы ценностей на основе предиката и текущего активного группового . Предикат дается в настоящее время активная группа в качестве Список и следующее значение. Затем он должен определить, должна ли группа продолжать работу или начать новую группу.

Следующий пример решает требование о совместной группе смежных значений в группе, где сумма значений в каждой группе не должна быть больше 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 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. КоллекционерУтилы

КоллекционерУтилс дополняет стандартную Коллекционеры добавив несколько полезных методов сбора.

5.1. maxBy () и minBy ()

maxBy () находит максимальное значение в потоке, используя предоставленную логику проекции :

Stream clubs = Stream.of("Juventus", "Barcelona", "PSG");
Optional longestName = clubs.collect(CollectorUtils.maxBy(String::length));
assertThat(longestName).contains("Barcelona");

В отличие от этого, minBy () находит минимальное значение, используя предоставленную логику проекции .

5.2. уникальный()

уникальный () коллекционер делает очень простую вещь: он возвращает единственное значение, если данный поток имеет ровно 1 элемент:

Stream singleElement = Stream.of(1);
Optional unique = singleElement.collect(CollectorUtils.unique());
assertThat(unique).contains(1);

В противном случае, уникальный () сделает исключение:

Stream multipleElement = Stream.of(1, 2, 3);
assertThatExceptionOfType(NonUniqueValueException.class).isThrownBy(() -> {
    multipleElement.collect(CollectorUtils.unique());
});

6. Заключение

В этой статье мы узнали, как библиотека Protonpack расширяет API Java Stream, чтобы упростить его использование. Он добавляет полезные методы, которые мы могли бы обычно использовать, но отсутствуют в стандартном API.

Начиная с Java 9, некоторые функциональные возможности, предоставляемые Protonpack, будут доступны в стандартном API Stream.

Как обычно, код можно найти более на Github .