Привет! Представляю вашему вниманию перевод статьи про возможности пакета FIC(Fast Immutable Collections, Быстрые неизменяемые коллекции). FIC - конкурент для таких пакетов как built_collection и kt_dart, но он намного быстрее и проще в использовании.
Оригинал: https://medium.com/flutter-community/announcing-fic-fast-immutable-collections-5eb091d1e31f
Фото от Ross Fondon на UnsplashМассивы, к которым вы, возможно, привыкли в других языках, в Dart называются списками (List), и они изменяемые по умолчанию - вы можете добавлять в них новые элементы, удалять и т.д.
var list = [1, 2];list.add(3); // list: [1, 2, 3].
Существует способ создать неизменяемый список с помощью
List.unmodifiable
, список, по прежнему, будет иметь
метод add
, который выдаст ошибку во время исполнения,
при попытке данный метод вызвать:
var list = List.unmodifiable([1, 2]);list.add(3); // Ошибка во время выполнения программы.
Однако можно делать изменения в копии такого списка:
var list = List.unmodifiable([1, 2]); // Этот список нельзя изменять.var newList = list.toList()..add(3);print(newList); // [1, 2, 3].
Есть способ лучше
Как вы поняли, обычный List
можно использовать как
неизменяемый, но при этом приходится писать шаблонный код, его
становится трудно читать. Существует другой, более хороший способ:
использование IList
вместо List. IList
неизменяемый - неизменяемый список, с простым API для
использования.
Чтобы создать IList
вы можете просто использовать
extension-метод lock
над обычным
List
:
IList<int> iList = [1, 2].lock;
Изменить содержимое IList
не получится. Метод
add
будет возвращать новый IList
с
добавленным элементом:
var myList = [1, 2].lock; // Его изменить не получится.var newList = myList.add(3));print(myList); // [1, 2].print(newList); // [1, 2, 3].
FIC
Пакет FIC (Fast Immutable Collections, Быстрые неизменяемые коллекции) предоставляет неизменяемые списки. FIC - конкурент для таких пакетов как built_collection и kt_dart, но он намного быстрее и проще в использовании.
По ссылке https://pub.dev/packages/fast_immutable_collections Вы можете посмотреть сравнения по скорости с альтернативными пакетами. Иногда FIC даже быстрее, чем родные коллекции в Dart:
Время, требуемое для добавления 1 000 элементов в список из 10 000 элементов. Чем меньше, тем лучшеИспользование
Неизменяемые коллекции имеют имена IList
,
ISet
и IMap
. Чтобы создать неизменяемую
коллекцию, Вы просто можете "заблокировать" изменяемую:
IList<int> iList = [1, 2].lock;ISet<int> iSet = {1, 2}.lock;IMap<String, int> iMap = {"a": 1, "b": 2}.lock;
Любой Iterable
также может быть заблокирован в
неизменяемый список(List) или набор(Set) с
помощью toIList()
и toISet()
соответственно, или используя конструкторы:
IList<int> iList = myIterable.toIList();ISet<int> iSet = myIterable.toISet();// С помощью конструкторов:IList<int> iList = IList(myIterable);ISet<int> iSet = ISet(myIterable);
Также можно "разблокировать" неизменяемые коллекции и получить изменяемые:
List<int> list = iList.unlock;Set<int> set = iSet.unlock;Map<String, int> map = iMap.unlock;// Работает и это:List<int> list = iList.toList();Set<int> set = iSet.toSet();// Работает и это:List<int> list = List.of(iList);Set<int> set = Set.of(iSet);
Но помните, что те методы, которые изменяют содержимое неизменяемых коллекций возвращают коллекции с новым содержанием. К примеру:
var iList1 = [1, 2].lock;var iList2 = iList1.add(3); // [1, 2, 3].var iList3 = iList2.remove(2); // [1, 3].print(iList1); // Все еще [1, 2].
Благодаря этому методы можно выстраивать в цепочку:
var iList = [1, 2, 3].lock.add(4).remove(2); // [1, 3, 4].
Равенство
По умолчанию, FIC равны, если их содержимое одинаковое и находится в одном порядке:
var iList1 = [1, 2].lock;var iList2 = [1, 2].lock;print(identical(iList1, iList2)); // false.print(iList1 == iList2); // true.
Это является полной противоположностью для обычных списков(List), которые сравниваются по идентичности:
var list1 = [1, 2];var list2 = [1, 2];var list3 = list1;// Обычные списки(List) сравниваются по идентичности:print(identical(list1, list2)); // false.print(identical(list1, list3)); // true.print(list1 == list2); // false.// В то время как ILists сравниваются по содержимому:print(list1.lock == list2.lock); // true.
Однако, IList
является настраиваемым. Вы можете
создать IList
, который будет сравниваться по
идентичности:
// Эти ILists сравниваются по содержимому:var iList1 = [1, 2].lock;var iList2 = [1, 2].lock;print(iList1 == iList2); // true.// Теперь эти же ILists сравниваются по идентичности:var iList3 = [1, 2].lock.withIdentityEquals;var iList4 = [1, 2].lock.withIdentityEquals;print(iList3 == iList4); // false.
Сортировка
Порядок объектов в ISet
и IMap
может
быть и тем, который Вы изначально ввели, а может быть и
отсортирован в зависимости от конфигурации:
var iSet = {2, 4, 1, 9, 3}.lock;print(iSet.join(", ")); // [2, 4, 1, 9, 3].var iSet = {2, 4, 1, 9, 3}.lock.withConfig(ConfigSet(sort: true));print(iSet.join(", ")); // [1, 2, 3, 4, 9].
IMapOfSets
При блокировке Map<K, V>
, Вы получаете
IMap<K, V>
.
Однако заблокированная Map<K, Set<V>>
становится IMapOfSets<K, V>
:
// Map в IMap.IMap<K, V> map = {'a': 1, 'b': 2}.lock;// Map в IMapOfSets.IMapOfSets<K, V> map = {'a': {1, 2}, 'b': {3, 4}}.lock;
"Map of sets" - это вид мультисловаря(multimap).
IMapOfSets
позволяет добавлять и удалять объекты, не
думая о наборах(sets), которые содержатся внутри.
К примеру:
IMapOfSets<K, V> map = {'a': {1, 2}, 'b': {3, 4}}.lock;print(map.add('a', 3)); // {'a': {1, 2, 3}, 'b': {3, 4}}.
По
этой ссылке можно найти пример класса, называющийся
StudentsPerCourse
, который распределяет студентов по
курсам. Каждый студент может быть записан в один или несколько
курсов. Такую модель можно получить с помощью словаря(map), в
котором ключами будут курсы, а значениями - наборы из
студентов.
ListSet
Несмотря на свое название, FIC предлагает и
изменяемые типы. К примеру, ListSet
сразу же:
-
Изменяемый, упорядоченный набор(Set) фиксированного размера.
-
Изменяемый список(List) фиксированного размера, в котором значения не повторяются:
ListSet<int> listSet = ListSet.of([1, 2, 3]);expect(listSet[2], 3);expect(listSet.contains(2), isTrue);List<int> list = listSet;Set<int> set = listSet;expect(list[2], 3);expect(set.contains(2), isTrue);
Если рассматривать ListSet
как Set
и
сравнивать с LinkedHashSet
, то ListSet
также является упорядоченным и имеет схожую производительность. Но
ListSet
занимает меньше места в памяти и может быть
отсортирован, так же как и List
. Также, у Вас есть
доступ к его элемента по индексу за константное время.
Минусом, конечно же, является, что ListSet
имеет
фиксированный размер, а LinkedHashSet
- нет.
ListSet
эффективен и как List
, и как
Set
. Поэтому, к примеру, у него есть эффективный sort
метод, в то время как LinkedHashSet
требует
конвертации в List
, сортировки, а затем обратного
преобразования в Set
.
Расширения, хелперы и компараторы
FIC также предоставляет:
Iterable хелперы и расширения:
-
combineIterables
- высокоуровневая функция, которая объединяет дваIterable
в один. -
Iterable.isNullOrEmpty
,Iterable.isNotNullOrEmpty
иIterable.isEmptyButNotNull
. -
Iterable.deepEquals
сравнивает два объекта по порядку с помощью оператора==
. -
Iterable.deepEqualsByIdentity
сравнивает все объекты по порядку с помощьюidentical
. -
Iterable.findDuplicates
ищет повторяющиеся значения и возвращаетSet
с дубликатами. -
Iterable.removeNulls
убирает все объекты, равныеnull
, изIterable
. -
Iterable.removeDuplicates
убирает все повторяющиеся объекты. Дополнительно, можно использоваться by функцию для сравнения объектов. Если добавить аргументremoveNulls
равноеtrue
, то уберутся и null-объекты. -
Iterable.sortedLike
возвращается список, отсортированный в соответствии сIterable
сортировки. Объекты, которых нет в сортировочномIterable
, встанут в конец.
Расширения над List
:
-
List.sortOrdered
похож на методsort
, но использует сортировку слиянием(merge sort). В противовесsort
,orderedSort
стабильный, что означает, что различные объекты, которые сравниваются как равные, остаются в начальном порядке. -
List.sortLike
сортирует список в соответствии с orderingIterable
. Объекты, которых нет в ordering, встанут в конец в определенном порядке. -
List.moveToTheFront
перемещает первое вхождение элемента в начало списка. -
List.moveToTheEnd
перемещает первое вхождение элемента в конец списка. -
List.whereMoveToTheFront
перемещает все элементы, которые удовлетворяют предоставленным требованиям, в начало списка. -
List.whereMoveToTheEnd
перемещает все элементы, которые удовлетворяют предоставленным требованиям, в конец списка. -
List.toggle
Если элемента нет в списке, добавляет его и возвращаетtrue
. Если уже есть, то удаляет первый необходимый и возвращаетfalse
. -
List.compareAsSets
возвращает true если в списке есть требуемые элементы (в любом порядке). Не берутся во внимание повторяющиеся элементы. -
List.mapIndexed
преобразует каждый элемент списка. Функция принимает изначальный элемент и его индекс. -
List.splitList
убирает элементы из списка, которые удовлетворяют условию. -
List.divideList
возвращает несколько списков, разделенных по выбранным элементам. -
List.addBetween
возвращает новый список, добавив разделение между изначальными элементами. -
List.concat
возвращает эффективное слияние до 5 списков. -
List.splitByLength
делит список на один или несколько списков с максимальным количеством элементовlength
. -
List.update
возвращает список, в котором новые элементы добавляются или обновляются в зависимости от порядкового номера. -
List.distinct
удаляет повторяющиеся объекты, оставляя только уникальные. -
List.reversedView
возвращает список объектов в обратном порядке.
Расширения над Set
:
-
Set.toggle
Если объекта нет вSet
, то он добавляется и возвращаетсяtrue
. Иначе, если объект уже есть вSet
, то он удаляется и возвращаетсяfalse
.
Расширения над Iterator
:
-
toIterable
,toList
,toSet
,toIList
, иtoISet
преобразуютIterator
вIterable
,List
,Set
,IList
, иISet
, соответственно.
Расширения над Boolean:
-
compareTo
делаетtrue > false
.
Компараторы
-
compareObject
-
compareObjectTo
-
sortBy
-
sortLike
-
if0
Пакет разработан Philippe Fanaro и Marcelo Glasberg.
Другие Flutter пакеты от автора:
-
async_redux (на Medium: Асинхронный Redux: Нешаблонная версия Flutter Redux https://medium.com/flutter-community/https-medium-com-marcglasberg-async-redux-33ac5e27d5f6)
-
i18n_extension (на Medium: Потрясающе легкие переводы и интернационализация для Flutter приложений https://medium.com/flutter-community/i18n-extension-flutter-b966f4c65df9)
https://github.com/marcglasberg
https://twitter.com/GlasbergMarcelo
Читайте сообщество Flutter в Twitter: https://www.twitter.com/FlutterComm