VAVR (известная ранее, как Javaslang) — это некоммерческая функциональная библиотека для Java 8+. Она позволяет писать функциональный Scala-подобный код в Java и служит для уменьшения количества кода и повышения его качества. Сайт библиотеки.
Под катом — перевод статьи, систематизирующей информацию по API Коллекций Vavr.
Перевел @middle_java
Последнее изменение оригинальной статьи: 15 августа 2019 г.
Библиотека Vavr, известная ранее, как Javaslang, является функциональной библиотекой для Java. В этой статье мы исследуем ее мощный API коллекций.
Дополнительные сведения об этой библиотеке см. в этой статье.
Персистентная коллекция при изменении создает новую версию коллекции, не изменяя при этом текущую версию.
Поддержка нескольких версий одной коллекции может привести к неэффективному использованию ЦП и памяти. Однако, библиотека коллекций Vavr преодолевает это, расшаривая структуру данных между различными версиями коллекции.
Это принципиально отличается от
Попытка изменить такую коллекцию приводит к
Он предоставляет некоторые полезные методы по-умолчанию, такие как
Исследуем далее библиотеку коллекций.
Начнем с последовательностей.
Интерфейс
Персистентные
API
Можно также использовать статический метод
Рассмотрим некоторые примеры манипулирования списками.
Мы можем использовать метод
Также есть методы
Далее,
Наконец,
Кроме того, в API есть и другие полезные методы, например, даже
Очень интересно, что также есть
Хотите разделить список на категории? И для этого есть API:
Метод
Ключ
Как и ожидалось, при изменении
Мы также можем взаимодействовать с
Функция
В интерфейсе
Неизменяемая (immutable)
Это позволяет привести операции постановки в очередь и удаления из очереди к сложности O(1). Когда при удалении из очереди в переднем
Давайте создадим очередь:
Функция
Мы можем использовать
Снова видно, что исходная
Допустим, у нас есть
При печати результата
Вызов
Такое поведение может улучшить производительность и позволяет использовать
Давайте создадим
В отличие от
Поэтому, у него есть такие методы, как
Наконец, давайте быстро продемонстрируем
Мы также можем использовать
Можно создать экземпляр
Методы
Давайте поработаем с элементами с использованием индекса:
Чтобы создать
В этом разделе рассматриваются различные реализации
Существуют различные реализации
Давайте рассмотрим более подробно эти реализации одну за другой.
Разницу между двумя set можно получить с помощью метода
Мы также можем выполнять основные операции, такие как добавление и удаление элементов:
Реализация
Неизменяемый
По умолчанию элементы
Давайте создадим
Чтобы упорядочить элементы кастомным образом, передайте экземпляр
В коллекциях Vavr также присутствует неизменяемая реализация
Как и в других реализациях структуры данных
Он наследует методы для манипулирования из интерфейса
Рассмотрим создание экземпляра
Для выбора первых четырех элементов
Другие методы и операции, описанные выше, определенные в интерфейсе
Как правило, контракты map не допускают дублирование ключей, в то время как повторяющиеся значения, сопоставленные с различными ключами, могут быть.
Как и
Элементы map можно фильтровать по ключам с помощью метода
Также можно преобразовать элементы map с помощью метода
Неизменяемая
Продемонстрируем создание
По умолчанию, записи
Как и в случае
API коллекций Vavr полностью совместим с фреймворком коллекций Java. Посмотрим, как это делается на практике.
Каждая реализация коллекции в Vavr имеет статический фабричный метод
Чтобы преобразовать
Другой полезной функцией является
Интерфейс
Рассмотрим пару примеров:
Также мы можем использовать Java 8
Кроме того, библиотека предоставляет так называемые представления коллекций, которые работают лучше при преобразовании в коллекции Java. Методы преобразования, приведенные в предыдущем разделе, перебирают (итерируют) все элементы для создания коллекции Java.
Представления, с другой стороны, реализуют стандартные интерфейсы Java и делегируют вызовы методов базовой коллекции Vavr.
На момент написания этой статьи поддерживается только представление
Вызов методов для изменения на неизменяемом представлении приводит к исключению
Давайте рассмотрим пример:
Чтобы создать неизменяемое представление:
В этом уроке мы узнали о различных функциональных структурах данных, предоставляемых API Коллекций Vavr. Есть еще полезные и производительные методы API, которые можно найти в Java doc и руководстве пользователя коллекций Vavr.
Наконец, важно отметить, что библиотека также определяет
Полный исходный код для всех примеров в этой статье можно найти на Github.
Дополнительные материалы:
habr.com/ru/post/421839
www.baeldung.com/vavr
Перевел @middle_java
Под катом — перевод статьи, систематизирующей информацию по API Коллекций Vavr.
Перевел @middle_java
Последнее изменение оригинальной статьи: 15 августа 2019 г.
1. Обзор
Библиотека Vavr, известная ранее, как Javaslang, является функциональной библиотекой для Java. В этой статье мы исследуем ее мощный API коллекций.
Дополнительные сведения об этой библиотеке см. в этой статье.
2. Персистентные Коллекции
Персистентная коллекция при изменении создает новую версию коллекции, не изменяя при этом текущую версию.
Поддержка нескольких версий одной коллекции может привести к неэффективному использованию ЦП и памяти. Однако, библиотека коллекций Vavr преодолевает это, расшаривая структуру данных между различными версиями коллекции.
Это принципиально отличается от
unmodifiableCollection()
из утилитного Java класса Collections
, который просто предоставляет оболочку (wrapper) для базовой коллекции.Попытка изменить такую коллекцию приводит к
UnsupportedOperationException
вместо создания новой версии. Более того, базовая коллекция по-прежнему изменяема через прямую ссылку на нее.3. Traversable
Traversable
— это базовый тип всех коллекций Vavr. Этот интерфейс определяет методы, общие для всех структур данных.Он предоставляет некоторые полезные методы по-умолчанию, такие как
size()
, get()
, filter()
, isEmpty()
и другие, которые наследуются суб-интерфейсами.Исследуем далее библиотеку коллекций.
4. Seq
Начнем с последовательностей.
Интерфейс
Seq
представляет собой последовательные структуры данных. Это родительский интерфейс для List
, Stream
, Queue
, Array
, Vector
и CharSeq
. Все эти структуры данных имеют свои уникальные свойства, которые мы рассмотрим ниже.4.1. List
List
— это энергично вычисляемая (eagerly-evaluated, операция выполняется, как только становятся известны значения ее операндов) последовательность элементов, расширяющих интерфейс LinearSeq
.Персистентные
List
конструируются рекурсивно с использованием головы и хвоста:- Голова — первый элемент
- Хвост — список, содержащий остальные элементы (этот список также формируется из головы и хвоста)
API
List
содержит статические фабричные методы, которые можно использовать для создания List
. Можно использовать статический метод of()
для создания экземпляра List
из одного или нескольких объектов.Можно также использовать статический метод
empty()
для создания пустого List
и метод ofAll()
для создания List
из типа Iterable
:List < String > list = List.of(
"Java", "PHP", "Jquery", "JavaScript", "JShell", "JAVA");
Рассмотрим некоторые примеры манипулирования списками.
Мы можем использовать метод
drop()
и его варианты для удаления первых N
элементов:List list1 = list.drop(2);
assertFalse(list1.contains("Java") && list1.contains("PHP"));
List list2 = list.dropRight(2);
assertFalse(list2.contains("JAVA") && list2.contains("JShell"));
List list3 = list.dropUntil(s - > s.contains("Shell"));
assertEquals(list3.size(), 2);
List list4 = list.dropWhile(s - > s.length() > 0);
assertTrue(list4.isEmpty());
drop(int n)
удаляет n
элементов из списка, начиная с первого элемента, в то время как dropRight()
делает то же самое, начиная с последнего элемента в списке.dropUntil()
удаляет элементы из списка до тех пор, пока предикат не станет равен true
, в то время как dropWhile()
удаляет элементы, пока предикат равен true
.Также есть методы
dropRightWhile()
и dropRightUntil()
, которые удаляют элементы, начиная справа.Далее,
take(int n)
используется для извлечения элементов из списка. Он берет n
элементов из списка, а затем останавливается. Существует также takeRight(int n)
, который берет элементы, начиная с конца списка:List list5 = list.take(1);
assertEquals(list5.single(), "Java");
List list6 = list.takeRight(1);
assertEquals(list5.single(), "Java");
List list7 = list.takeUntil(s - > s.length() > 6);
assertEquals(list3.size(), 3);
Наконец,
takeUntil()
берет элементы из списка до тех пор, пока предикат не станет равен true
. Существует вариант takeWhile()
, который также принимает аргумент-предикат.Кроме того, в API есть и другие полезные методы, например, даже
distinct()
, который возвращает список элементов с удаленными дубликатами, а также distinctBy()
, который принимает Comparator
для определения равенства.Очень интересно, что также есть
intersperse()
, который вставляет элемент между каждым элементом списка. Это может быть очень удобно для операций со String
:List list8 = list
.distinctBy((s1, s2) - > s1.startsWith(s2.charAt(0) + "") ? 0 : 1);
assertEquals(list3.size(), 2);
String words = List.of("Boys", "Girls")
.intersperse("and")
.reduce((s1, s2) - > s1.concat(" " + s2))
.trim();
assertEquals(words, "Boys and Girls");
Хотите разделить список на категории? И для этого есть API:
Iterator < List < String >> iterator = list.grouped(2);
assertEquals(iterator.head().size(), 2);
Map < Boolean, List < String >> map = list.groupBy(e - > e.startsWith("J"));
assertEquals(map.size(), 2);
assertEquals(map.get(false).get().size(), 1);
assertEquals(map.get(true).get().size(), 5);
Метод
group(int n)
разделяет List
на группы из n
элементов каждая. Метод groupdBy()
принимает Function
, содержащую логику разделения списка и возвращает Map
с двумя элементами: true
и false
.Ключ
true
мапится на List
элементов, удовлетворяющих условию, указанному в Function
. Ключ false
мапится на List
элементов, не удовлетворяющих этому условию.Как и ожидалось, при изменении
List
, исходный List
на самом деле не изменяется. Вместо этого всегда возвращается новая версия List
.Мы также можем взаимодействовать с
List
, используя семантику стека — извлечение элементов по принципу «последним вошел, первым вышел» (LIFO). В этом смысле, для манипулирования стеком существуют такие методы API, как peek()
, pop()
и push()
:List < Integer > intList = List.empty();
List < Integer > intList1 = intList.pushAll(List.rangeClosed(5, 10));
assertEquals(intList1.peek(), Integer.valueOf(10));
List intList2 = intList1.pop();
assertEquals(intList2.size(), (intList1.size() - 1));
Функция
pushAll()
используется для вставки диапазона целых чисел в стек, а функция peek()
— для извлечения головного элемента стека. Существует также метод peekOption()
, который может обернуть (wrap) результат в объект Option
.В интерфейсе
List
есть и другие интересные и действительно полезные методы, которые тщательно задокументированы в Java docs.4.2. Queue
Неизменяемая (immutable)
Queue
хранит элементы, позволяя извлекать их по принципу FIFO (первым вошел, первым вышел).Queue
внутри состоит из двух связанных списков: переднего List
и заднего List
. Передний List
содержит элементы, удаляемые из очереди, а задний List
— элементы, поставленные в очередь.Это позволяет привести операции постановки в очередь и удаления из очереди к сложности O(1). Когда при удалении из очереди в переднем
List
заканчиваются элементы, задний List
реверсируется и становится новым передним List
.Давайте создадим очередь:
Queue < Integer > queue = Queue.of(1, 2);
Queue < Integer > secondQueue = queue.enqueueAll(List.of(4, 5));
assertEquals(3, queue.size());
assertEquals(5, secondQueue.size());
Tuple2 < Integer, Queue < Integer >> result = secondQueue.dequeue();
assertEquals(Integer.valueOf(1), result._1);
Queue < Integer > tailQueue = result._2;
assertFalse(tailQueue.contains(secondQueue.get(0)));
Функция
dequeue()
удаляет головной элемент из Queue
и возвращает Tuple2<T, Q>
. Первым элементом кортежа является головной элемент, удаленный из очереди, вторым элементом кортежа являются оставшиеся элементы Queue
.Мы можем использовать
combination(n)
, чтобы получить все возможные N
комбинаций элементов в Queue
:Queue < Queue < Integer >> queue1 = queue.combinations(2);
assertEquals(queue1.get(2).toCharSeq(), CharSeq.of("23"));
Снова видно, что исходная
Queue
не изменяется во время добавления/удаления элементов из очереди.4.3. Stream
Stream
— это реализация лениво-связанного списка, который значительно отличается от java.util.stream
. В отличие от java.util.stream
, Stream
Vavr хранит данные и лениво вычисляет последующие элементы.Допустим, у нас есть
Stream
целых чисел:Stream < Integer > s = Stream.of(2, 1, 3, 4);
При печати результата
s.toString()
в консоли будет отображаться только Stream(2, ?). Это означает, что был вычислен только головной элемент Stream
, в то время как хвостовые элементы нет.Вызов
s.get(3)
и последующее отображение результата s.tail()
возвращает Stream(1, 3, 4, ?). Напротив, если не вызвать первым s.get(3)
— что заставит Stream
вычислить последний элемент — результатом s.tail()
будет только Stream(1, ?). Это означает, что был вычислен только первый элемент хвоста.Такое поведение может улучшить производительность и позволяет использовать
Stream
для представления последовательностей, которые (теоретически) бесконечно длинны.Stream
в Vavr является неизменяемым и может быть Empty
или Cons
. Cons
состоит из головного элемента и лениво вычисляемого хвоста Stream
. В отличие от List
, Stream
хранит в памяти только головной элемент. Хвостовые элементы вычисляются по необходимости.Давайте создадим
Stream
из 10 положительных целых чисел и вычислим сумму четных чисел:Stream < Integer > intStream = Stream.iterate(0, i - > i + 1)
.take(10);
assertEquals(10, intStream.size());
long evenSum = intStream.filter(i - > i % 2 == 0)
.sum()
.longValue();
assertEquals(20, evenSum);
В отличие от
Stream
API из Java 8, Stream
в Vavr — это структура данных для хранения последовательности элементов.Поэтому, у него есть такие методы, как
get()
, append()
, insert()
и другие для манипулирования его элементами. Также доступны drop()
, distinct()
и некоторые другие методы, рассмотренные ранее.Наконец, давайте быстро продемонстрируем
tabulate()
в Stream
. Этот метод возвращает Stream
длиной n
, содержащий элементы, являющиеся результатом применения функции:Stream < Integer > s1 = Stream.tabulate(5, (i) - > i + 1);
assertEquals(s1.get(2).intValue(), 3);
Мы также можем использовать
zip()
для создания Stream
из Tuple2<Integer, Integer>
, который содержит элементы, образованные путем комбинирования двух Stream
:Stream < Integer > s = Stream.of(2, 1, 3, 4);
Stream < Tuple2 < Integer, Integer >> s2 = s.zip(List.of(7, 8, 9));
Tuple2 < Integer, Integer > t1 = s2.get(0);
assertEquals(t1._1().intValue(), 2);
assertEquals(t1._2().intValue(), 7);
4.4. Array
Array
— это неизменяемая индексированная последовательность, обеспечивающая эффективный произвольный доступ. Он основан на Java массиве объектов. По сути, это Traversable
оболочка для массива объектов типа T
.Можно создать экземпляр
Array
с помощью статического метода of()
. Кроме того, можно создать диапазон элементов с помощью статических методов range()
и rangeBy()
. Метод rangeBy()
имеет третий параметр, который позволяет определить шаг.Методы
range()
и rangeBy()
будут создавать элементы, начиная только с начального значения до конечного значения минус один. Если нам нужно включить конечное значение, можно использовать rangeClosed()
или rangeClosedBy()
:Array < Integer > rArray = Array.range(1, 5);
assertFalse(rArray.contains(5));
Array < Integer > rArray2 = Array.rangeClosed(1, 5);
assertTrue(rArray2.contains(5));
Array < Integer > rArray3 = Array.rangeClosedBy(1, 6, 2);
assertEquals(list3.size(), 3);
Давайте поработаем с элементами с использованием индекса:
Array < Integer > intArray = Array.of(1, 2, 3);
Array < Integer > newArray = intArray.removeAt(1);
assertEquals(3, intArray.size());
assertEquals(2, newArray.size());
assertEquals(3, newArray.get(1).intValue());
Array < Integer > array2 = intArray.replace(1, 5);
assertEquals(s1.get(0).intValue(), 5);
4.5. Vector
Vector
— это что-то среднее между Array
и List
, предоставляющее другую индексированную последовательность элементов, позволяющую осуществлять и произвольный доступ, и модификацию за константное время:Vector < Integer > intVector = Vector.range(1, 5);
Vector < Integer > newVector = intVector.replace(2, 6);
assertEquals(4, intVector.size());
assertEquals(4, newVector.size());
assertEquals(2, intVector.get(1).intValue());
assertEquals(6, newVector.get(1).intValue());
4.6. CharSeq
CharSeq
— это объект-коллекция для представления последовательности примитивных символов. По сути — это оболочка для String
с добавлением операций коллекции.Чтобы создать
CharSeq
необходимо выполнить следующее.CharSeq chars = CharSeq.of("vavr");
CharSeq newChars = chars.replace('v', 'V');
assertEquals(4, chars.size());
assertEquals(4, newChars.size());
assertEquals('v', chars.charAt(0));
assertEquals('V', newChars.charAt(0));
assertEquals("Vavr", newChars.mkString());
5. Set
В этом разделе рассматриваются различные реализации
Set
в библиотеке коллекций. Уникальная особенность структуры данных Set
заключается в том, что она не допускает повторяющихся значений.Существуют различные реализации
Set
. Основная из них — HashSet
. TreeSet
не допускает повторяющихся элементов и может быть отсортирован. LinkedHashSet
сохраняет порядок вставки элементов.Давайте рассмотрим более подробно эти реализации одну за другой.
5.1. HashSet
HashSet
имеет статические фабричные методы для создания новых экземпляров. Некоторые из которых мы изучили ранее в этой статье, например of()
, ofAll()
и вариации методов range()
.Разницу между двумя set можно получить с помощью метода
diff()
. Также, методы union()
и intersect()
возвращают объединение и пересечение двух set:HashSet < Integer > set0 = HashSet.rangeClosed(1, 5);
HashSet < Integer > set0 = HashSet.rangeClosed(1, 5);
assertEquals(set0.union(set1), HashSet.rangeClosed(1, 6));
assertEquals(set0.diff(set1), HashSet.rangeClosed(1, 2));
assertEquals(set0.intersect(set1), HashSet.rangeClosed(3, 5));
Мы также можем выполнять основные операции, такие как добавление и удаление элементов:
HashSet < String > set = HashSet.of("Red", "Green", "Blue");
HashSet < String > newSet = set.add("Yellow");
assertEquals(3, set.size());
assertEquals(4, newSet.size());
assertTrue(newSet.contains("Yellow"));
Реализация
HashSet
основана на Hash array mapped trie (HAMT), которая может похвастаться превосходной производительностью по сравнению с обычной HashTable
и ее структура делает ее подходящей для поддержки персистентных коллекций.5.2. TreeSet
Неизменяемый
TreeSet
является реализацией интерфейса SortedSet
. Он хранит набор отсортированных элементов и реализуется с применением бинарных деревьев поиска. Все его операции выполняются за время O(log n).По умолчанию элементы
TreeSet
сортируются в их натуральном порядке.Давайте создадим
SortedSet
используя натуральный порядок сортировки:SortedSet < String > set = TreeSet.of("Red", "Green", "Blue");
assertEquals("Blue", set.head());
SortedSet < Integer > intSet = TreeSet.of(1, 2, 3);
assertEquals(2, intSet.average().get().intValue());
Чтобы упорядочить элементы кастомным образом, передайте экземпляр
Comparator
при создании TreeSet
. Можно также создать строку из набора элементов:SortedSet < String > reversedSet = TreeSet.of(Comparator.reverseOrder(), "Green", "Red", "Blue");
assertEquals("Red", reversedSet.head());
String str = reversedSet.mkString(" and ");
assertEquals("Red and Green and Blue", str);
5.3. BitSet
В коллекциях Vavr также присутствует неизменяемая реализация
BitSet
. Интерфейс BitSet
расширяет интерфейс SortedSet
. BitSet
можно создать с помощью статических методов в BitSet.Builder
.Как и в других реализациях структуры данных
Set
, BitSet
не позволяет добавлять в набор повторяющиеся записи.Он наследует методы для манипулирования из интерфейса
Traversable
. Обратите внимание, что он отличается от java.util.BitSet
из стандартной библиотеки Java. Данные BitSet
не могут содержать значения String
.Рассмотрим создание экземпляра
BitSet
с использованием фабричного метода of()
:BitSet < Integer > bitSet = BitSet.of(1, 2, 3, 4, 5, 6, 7, 8);
BitSet < Integer > bitSet1 = bitSet.takeUntil(i - > i > 4);
assertEquals(list3.size(), 4);
Для выбора первых четырех элементов
BitSet
мы использовали команду takeUntil()
. Операция вернула новый экземпляр. Обратите внимание, что метод takeUntil()
определен в интерфейсе Traversable
, который является родительским интерфейсом для BitSet
.Другие методы и операции, описанные выше, определенные в интерфейсе
Traversable
, также применимы к BitSet
.6. Map
Map
— это структура данных «ключ-значение». Map
в Vavr является неизменяемой и имеет реализации для HashMap
, TreeMap
и LinkedHashMap
.Как правило, контракты map не допускают дублирование ключей, в то время как повторяющиеся значения, сопоставленные с различными ключами, могут быть.
6.1. HashMap
HashMap
— это реализация неизменяемого интерфейса Map
. Он хранит пары «ключ-значение», используя хэш-код ключей.Map
в Vavr использует Tuple2
для представления пар «ключ-значение» вместо традиционного типа Entry
:Map < Integer, List < Integer >> map = List.rangeClosed(0, 10)
.groupBy(i - > i % 2);
assertEquals(2, map.size());
assertEquals(6, map.get(0).get().size());
assertEquals(5, map.get(1).get().size());
Как и
HashSet
, реализация HashMap
основана на Hash array mapped trie (HAMT), что приводит к константному времени почти для всех операций.Элементы map можно фильтровать по ключам с помощью метода
filterKeys()
или по значениям с помощью метода filterValues()
. Оба метода принимают Predicate
в качестве аргумента:Map < String, String > map1 =
HashMap.of("key1", "val1", "key2", "val2", "key3", "val3");
Map < String, String > fMap =
map1.filterKeys(k - > k.contains("1") || k.contains("2"));
assertFalse(fMap.containsKey("key3"));
Map < String, String > map1 =
map1.filterValues(v - > v.contains("3"));
assertEquals(list3.size(), 1);
assertTrue(fMap2.containsValue("val3"));
Также можно преобразовать элементы map с помощью метода
map()
. Давайте, например, преобразуем map1 в Map<String, Integer>
:Map < String, Integer > map2 = map1.map(
(k, v) - > Tuple.of(k, Integer.valueOf(v.charAt(v.length() - 1) + "")));
assertEquals(map2.get("key1").get().intValue(), 1);
6.2. TreeMap
Неизменяемая
TreeMap
является реализацией интерфейса SortedMap
. Как и в случае TreeSet
, для кастомной сортировки элементов TreeMap
используется экземпляр Comparator
.Продемонстрируем создание
SortedMap
:SortedMap < Integer, String > map = TreeMap.of(3, "Three", 2, "Two", 4, "Four", 1, "One");
assertEquals(1, map.keySet().toJavaArray()[0]);
assertEquals("Four", map.get(4).get());
По умолчанию, записи
TreeMap
сортируются в натуральном порядке ключей. Однако, можно указать Comparator
, который будет использоваться для сортировки:TreeMap < Integer, String > treeMap2 =
TreeMap.of(Comparator.reverseOrder(), 3, "three", 6, "six", 1, "one");
assertEquals(treeMap2.keySet().mkString(), "631");
Как и в случае
TreeSet
, реализация TreeMap
также создана с применением дерева, следовательно, его операции имеют время O(log n). Метод map.get(key)
возвращает Option
, который содержит значение указанного ключа map.7. Совместимость с Java
API коллекций Vavr полностью совместим с фреймворком коллекций Java. Посмотрим, как это делается на практике.
7.1. Преобразование из Java в Vavr
Каждая реализация коллекции в Vavr имеет статический фабричный метод
ofAll()
, который принимает java.util.Iterable
. Это позволяет создать коллекцию Vavr из коллекции Java. Аналогично, другой фабричный метод ofAll()
принимает непосредственно Java Stream
.Чтобы преобразовать
List
Java в неизменяемый List
:java.util.List < Integer > javaList = java.util.Arrays.asList(1, 2, 3, 4);
List < Integer > vavrList = List.ofAll(javaList);
java.util.stream.Stream < Integer > javaStream = javaList.stream();
Set < Integer > vavrSet = HashSet.ofAll(javaStream);
Другой полезной функцией является
collector()
, который можно использовать совместно с Stream.collect()
для получения коллекции Vavr:List < Integer > vavrList = IntStream.range(1, 10)
.boxed()
.filter(i - > i % 2 == 0)
.collect(List.collector());
assertEquals(4, vavrList.size());
assertEquals(2, vavrList.head().intValue());
7.2. Преобразование из Vavr в Java
Интерфейс
Value
имеет множество методов для преобразования из типа Vavr в тип Java. Эти методы имеют формат toJavaXXX()
.Рассмотрим пару примеров:
Integer[] array = List.of(1, 2, 3)
.toJavaArray(Integer.class);
assertEquals(3, array.length);
java.util.Map < String, Integer > map = List.of("1", "2", "3")
.toJavaMap(i - > Tuple.of(i, Integer.valueOf(i)));
assertEquals(2, map.get("2").intValue());
Также мы можем использовать Java 8
Collectors
для сбора элементов из коллекций Vavr:java.util.Set < Integer > javaSet = List.of(1, 2, 3)
.collect(Collectors.toSet());
assertEquals(3, javaSet.size());
assertEquals(1, javaSet.toArray()[0]);
7.3. Представления Коллекций Java
Кроме того, библиотека предоставляет так называемые представления коллекций, которые работают лучше при преобразовании в коллекции Java. Методы преобразования, приведенные в предыдущем разделе, перебирают (итерируют) все элементы для создания коллекции Java.
Представления, с другой стороны, реализуют стандартные интерфейсы Java и делегируют вызовы методов базовой коллекции Vavr.
На момент написания этой статьи поддерживается только представление
List
. Каждая последовательная коллекция имеет два метода: один для создания неизменяемого представления, другой для изменяемого.Вызов методов для изменения на неизменяемом представлении приводит к исключению
UnsupportedOperationException
.Давайте рассмотрим пример:
@Test(expected = UnsupportedOperationException.class)
public void givenVavrList_whenViewConverted_thenException() {
java.util.List < Integer > javaList = List.of(1, 2, 3)
.asJava();
assertEquals(3, javaList.get(2).intValue());
javaList.add(4);
}
Чтобы создать неизменяемое представление:
java.util.List < Integer > javaList = List.of(1, 2, 3)
.asJavaMutable();
javaList.add(4);
assertEquals(4, javaList.get(3).intValue());
8. Выводы
В этом уроке мы узнали о различных функциональных структурах данных, предоставляемых API Коллекций Vavr. Есть еще полезные и производительные методы API, которые можно найти в Java doc и руководстве пользователя коллекций Vavr.
Наконец, важно отметить, что библиотека также определяет
Try
, Option
, Either
и Future
, которые расширяют интерфейс Value
и, как следствие, реализуют интерфейс Java Iterable
. Это означает, что в некоторых ситуациях они могут вести себя, как коллекции.Полный исходный код для всех примеров в этой статье можно найти на Github.
Дополнительные материалы:
habr.com/ru/post/421839
www.baeldung.com/vavr
Перевел @middle_java
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы используете Vavr в разработке?
16.13% Да10
58.06% Нет36
25.81% Что это?16
Проголосовали 62 пользователя. Воздержались 10 пользователей.