我们将继续Dart语法的第二篇集合,虽然集合在第一篇中已经介绍的差不多,但是在这篇文章中将会更加全面介绍有关Dart中的集合,因为之前只是介绍了
dart:core包
中的List、Set、Map,实际上在dart中还提供一个非常丰富的
dart:collection包
, 看过集合源码小伙伴都知道dart:core包中的集合实际上是委托到dart:collection包中实现的,所以下面我也会从源码的角度去把两者联系起来。当然这里也只会选择几个常用的集合作为介绍。
一、List
在dart中的List集合是具有长度的可索引对象集合,它没有委托dart:collection包中集合实现,完全由内部自己实现。
main
() {
List<String> colorList1 =
[
'red'
,
'yellow'
,
'blue'
,
'green'
]
;
var
colorList2 = <String>
[
'red'
,
'yellow'
,
'blue'
,
'green'
]
;
List<String> colorList3 =
List
(
4
);
colorList3
.add
('deepOrange');
print
(colorList3[
2
]);
List<String> colorList4 =
List
();
colorList4
[2]
= 'white';
main() {
List<String> colorList = ['red', 'yellow', 'blue', 'green'];
for(var i = 0; i < colorList.length; i++) {
print(colorList[i]);
colorList.forEach((color) => print(color));
for(var color in colorList) {
print(color);
while(colorList.iterator.moveNext()) {
print(colorList.iterator.current);
常用的函数
main() {
List<String> colorList = ['red', 'yellow', 'blue', 'green'];
colorList.add('white');
List<String> newColorList = ['white', 'black'];
colorList.addAll(newColorList);
print(colorList[2]);
print(colorList.length);
colorList.insert(1, 'black');
colorList.removeAt(2);
colorList.clear();
print(colorList.sublist(1,3));
print(colorList.getRange(1, 3));
print(colorList.join('<--->'));
print(colorList.isEmpty);
print(colorList.contains('green'));
构造函数源码分析
dart中的List有很多个构造器,一个主构造器和多个命名构造器。主构造器中有个length可选参数.
external factory List([int length]);
external factory List.filled(int length, E fill, {bool growable = false});
external factory List.from(Iterable elements, {bool growable = true});
factory List.of(Iterable<E> elements, {bool growable = true}) =>
List<E>.from(elements, growable: growable);
external factory List.unmodifiable(Iterable elements);
exteranl关键字(插播一条内容)
注意: 问题来了,可能大家看到List源码的时候一脸懵逼,构造函数没有具体的实现。不知道有没有注意 到exteranl 关键字。external修饰的函数具有一种实现函数声明和实现体分离的特性。这下应该就明白了,也就是对应实现在别的地方。实际上你可以在DartSDK中的源码找到,以List举例,对应的是 sdk/sdk_nnbd/lib/_internal/vm/lib/array_patch.dart, 此外对应的external函数实现会有一个 @patch注解 修饰.
@patch
class List<E> {
@patch
factory List([int length]) native "List_new";
@patch
factory List.filled(int length, E fill, {bool growable: false}) {
var result = growable ? new _GrowableList<E>(length) : new _List<E>(length);
if (fill != null) {
for (int i = 0; i < length; i++) {
result[i] = fill;
return result;
@patch
factory List.from(Iterable elements, {bool growable: true}) {
if (elements is EfficientLengthIterable<E>) {
int length = elements.length;
var list = growable ? new _GrowableList<E>(length) : new _List<E>(length);
if (length > 0) {
int i = 0;
for (var element in elements) {
list[i++] = element;
return list;
if (elements is Iterable<E>) {
List<E> list = new _GrowableList<E>(0);
for (E e in elements) {
list.add(e);
if (growable) return list;
return makeListFixedLength(list);
} else {
List<E> list = new _GrowableList<E>(0);
for (E e in elements) {
list.add(e);
if (growable) return list;
return makeListFixedLength(list);
@patch
factory List.unmodifiable(Iterable elements) {
final result = new List<E>.from(elements, growable: false);
return makeFixedListUnmodifiable(result);
复制代码
对应的List.from sdk的源码解析
@patch
List<T> makeListFixedLength<T>(List<T> growableList)
native "Internal_makeListFixedLength";
DEFINE_NATIVE_ENTRY(Internal_makeListFixedLength, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray, array,
arguments->NativeArgAt(0));
return Array::MakeFixedLength(array, true);
RawArray* Array::MakeFixedLength(const GrowableObjectArray& growable_array, bool unique) {
ASSERT(!growable_array.IsNull());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
intptr_t used_len = growable_array.Length();
const TypeArguments& type_arguments =
TypeArguments::Handle(growable_array.GetTypeArguments());
if (used_len == 0) {
if (type_arguments.IsNull() && !unique) {
return Object::empty_array().raw();
Heap::Space space = thread->IsMutatorThread() ? Heap::kNew : Heap::kOld;
Array& array = Array::Handle(zone, Array::New(0, space));
array.SetTypeArguments(type_arguments);
return array.raw();
const Array& array = Array::Handle(zone, growable_array.data());
ASSERT(array.IsArray());
array.SetTypeArguments(type_arguments);
growable_array.SetLength(0);
growable_array.SetData(Object::empty_array());
array.Truncate(used_len);
return array.raw();
复制代码
总结一下List.from的源码实现,首先传入elements的Iterate<E>, 如果elements不带泛型参数,也就是所谓的原生集合类型,并且是非unique,直接返回空数组; 如果带泛型参数空集合,那么会创建新的空集合并带上原来泛型参数返回;如果是带泛型参数非空集合,会取出其中data数组,来创建一个新的复制原来数据的集合并带上原来泛型参数返回,最后需要截断把数组截断成原始数组长度。
为什么需要exteranl function
关键就是在于它能实现声明和实现分离,这样就能复用同一套对外API的声明,然后对应多套多平台的实现,如果对源码感兴趣的小伙伴就会发现相同API声明在js中也有另一套实现,这样不管是dart for web 还是dart for vm对于上层开发而言都是一套API,对于上层开发者是透明的。
二、Set
dart:core包中的Set集合实际上是委托到dart:collection中的LinkedHashSet来实现的。集合Set和列表List的区别在于 集合中的元素是不能重复 的。所以添加重复的元素时会返回false,表示添加不成功.
Set初始化方式
main() {
Set<String> colorSet= {'red', 'yellow', 'blue', 'green'};
var colorList = <String> {'red', 'yellow', 'blue', 'green'};
集合中的交、并、补集,在Kotlin并没有直接给到计算集合交、并、补的API
main() {
var colorSet1 = {'red'
, 'yellow', 'blue', 'green'};
var colorSet2 = {'black', 'yellow', 'blue', 'green', 'white'};
print(colorSet1.intersection(colorSet2));//交集-->输出: {'yellow', 'blue', 'green'}
print(colorSet1.union(colorSet2));//并集--->输出: {'black', 'red', 'yellow', 'blue', 'green', 'white'}
print(colorSet1.difference(colorSet2));//补集--->输出: {'red'}
Set的遍历方式(和List一样)
main() {
Set<String> colorSet = {'red', 'yellow', 'blue', 'green'};
for (var i = 0; i < colorSet.length; i++) {
print(colorSet[i]);
colorSet.forEach((color) => print(color));
for (var color in colorSet) {
print(color);
while (colorSet.iterator.moveNext()) {
print(colorSet.iterator.current);
构造函数源码分析
factory Set() = LinkedHashSet<E>;
factory Set.identity() = LinkedHashSet<E>.identity;
factory Set.from(Iterable elements) = LinkedHashSet<E>.from;
factory Set.of(Iterable<E> elements) = LinkedHashSet<E>.of;
对应LinkedHashSet的源码分析,篇幅有限感兴趣可以去深入研究
abstract class LinkedHashSet<E> implements Set<E> {
external factory LinkedHashSet(
{bool equals(E e1, E e2),
int hashCode(E e),
bool isValidKey(potentialKey)});
factory LinkedHashSet.from(Iterable elements) {
LinkedHashSet<E> result = LinkedHashSet<E>();
for (final element in elements) {
result.add(element);
return result;
factory LinkedHashSet.of(Iterable<E> elements) =>
LinkedHashSet<E>()..addAll(elements);
void forEach(void action(E element));
Iterator<E> get iterator;
对应的 sdk/lib/_internal/vm/lib/collection_patch.dart 中的@Patch LinkedHashSet
@patch
class LinkedHashSet<E> {
@patch
factory LinkedHashSet(
{bool equals(E e1, E e2),
int hashCode(E e),
bool isValidKey(potentialKey)}) {
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _CompactLinkedHashSet<E>();
hashCode = _defaultHashCode;
} else {
if (identical(identityHashCode, hashCode) &&
identical(identical, equals)) {
return new _CompactLinkedIdentityHashSet<E>();
equals ??= _defaultEquals;
} else {
hashCode ??= _defaultHashCode;
equals ??= _defaultEquals;
return new _CompactLinkedCustomHashSet<E>(equals, hashCode, isValidKey);
@patch
factory LinkedHashSet.identity() => new _CompactLinkedIdentityHashSet<E>();
三、Map
dart:core 包中的 Map集合 实际上是 委托到dart:collection中的LinkedHashMap 来实现的。集合Map和Kotlin类似,key-value形式存储,并且 Map对象的中key是不能重复的
Map初始化方式
main() {
Map<String, int> colorMap = {'white': 0xffffffff, 'black':0xff000000};
var colorMap = <String, int>{'white': 0xffffffff, 'black':0xff000000};
var colorMap = Map<String, int>();
var colorMap = LinkedHashMap<String, int>();
Map中常用的函数
main() {
Map<String, int> colorMap = {'white': 0xffffffff, 'black':0xff000000};
print(colorMap.containsKey('green'));
print(colorMap.containsValue(0xff000000));
print(colorMap.keys.toList());
print(colorMap.values.toList());
colorMap['white'] = 0xfffff000;
colorMap.remove('black');
Map的遍历方式
main() {
Map<String, int> colorMap = {'white': 0xffffffff, 'black':0xff000000};
colorMap.forEach((key, value) => print('color is $key, color value is $value'));
Map.fromIterables将List集合转化成Map
main() {
List<String> colorKeys = ['white', 'black']
List<int> colorValues = [0xffffffff, 0xff000000]
Map<String, int> colorMap = Map.fromIterables(colorKeys, colorValues)
构造函数源码分析
external factory Map();
factory Map.from(Map other) = LinkedHashMap<K, V>.from;
factory Map.of(Map<K, V> other) = LinkedHashMap<K, V>.of;
external factory Map.unmodifiable(Map other);
factory Map.identity() = LinkedHashMap<K, V>.identity;
factory Map.fromIterable(Iterable iterable,
{K key(element), V value(element)}) = LinkedHashMap<K, V>.fromIterable;
factory Map.fromIterables(Iterable<K> keys, Iterable<V> values) =
LinkedHashMap<K, V>.fromIterables;
对应LinkedHashMap构造函数源码分析
abstract class LinkedHashMap<K, V> implements Map<K, V> {
external factory LinkedHashMap(
{bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey)});
external factory LinkedHashMap.identity();
factory LinkedHashMap.from(Map other) {
LinkedHashMap<K, V> result = LinkedHashMap<K, V>();
other.forEach((k, v) {
result[k] = v;
return result;
factory LinkedHashMap.of(Map<K, V> other) =>
LinkedHashMap<K, V>()..addAll(other);
factory LinkedHashMap.fromIterable(Iterable iterable,
{K key(element), V value(element)}) {
LinkedHashMap<K, V> map = LinkedHashMap<K, V>();
MapBase._fillMapWithMappedIterable(map, iterable, key, value);
return map;
factory LinkedHashMap.fromIterables(Iterable<K> keys, Iterable<V> values) {
LinkedHashMap<K, V> map = LinkedHashMap<K, V>();
MapBase._fillMapWithIterables(map, keys, values);
return map;
static void _fillMapWithMappedIterable(
Map map, Iterable iterable, key(element), value(element)) {
key ??= _id;
value ??= _id;
for (var element in iterable) {
map[key(element)] = value(element);
static void _fillMapWithIterables(Map map, Iterable keys, Iterable values) {
Iterator keyIterator = keys.iterator;
Iterator valueIterator = values.iterator;
bool hasNextKey = keyIterator.moveNext();
bool hasNextValue = valueIterator.moveNext();
while (hasNextKey && hasNextValue) {
map[keyIterator.current] = valueIterator.current;
hasNextKey = keyIterator.moveNext();
hasNextValue = valueIterator.moveNext();
if (hasNextKey || hasNextValue) {
throw ArgumentError("Iterables do not have same length.");
Map的@Patch对应实现,对应 sdk/lib/_internal/vm/lib/map_patch.dart 中
@patch
class Map<K, V> {
@patch
factory Map.unmodifiable(Map other) {
return new UnmodifiableMapView<K, V>(new Map<K, V>.from(other));
@patch
factory
Map() => new LinkedHashMap<K, V>();
四、Queue
Queue队列顾名思义先进先出的一种数据结构,在Dart对队列也做了一定的支持, 实际上Queue的实现是委托给ListQueue来实现。 Queue继承于EfficientLengthIterable<E>接口,然后EfficientLengthIterable<E>接口又继承了Iterable<E>.所以意味着Queue可以向List那样使用丰富的操作函数。并且由Queue派生出了 DoubleLinkedQueue和ListQueue
import 'dart:collection';
main() {
var queueColors = Queue();
queueColors.addFirst('red');
queueColors.addLast('yellow');
queueColors.add('blue');
var queueColors2 = Queue.from(['red', 'yellow', 'blue']);
var queueColors3 = Queue.of(['red', 'yellow', 'blue']);
常用的函数
import 'dart:collection';
main() {
var queueColors = Queue()
..addFirst('red')
..addLast('yellow')
..add('blue')
..addAll(['white','black'])
..remove('black')
..clear();
import 'dart:collection';
main() {
Queue<String> colorQueue = Queue.from(['red', 'yellow', 'blue', 'green']);
for (var i = 0; i < colorQueue.length; i++) {
print(colorQueue.elementAt(i));
colorQueue.forEach((color) => print(color));
for (var color in colorQueue) {
print(color);
构造函数源码分析
factory Queue() = ListQueue<E>;
factory Queue.from(Iterable elements) = ListQueue<E>.from;
factory Queue.of(Iterable<E> elements) = ListQueue<E>.of;
对应的ListQueue的源码分析
class ListQueue<E> extends ListIterable<E> implements Queue<E> {
static const int _INITIAL_CAPACITY = 8
List<E?> _table
int _head
int _tail
int _modificationCount = 0
ListQueue([int? initialCapacity])
: _head = 0,
_tail = 0,
_table = List<E?>(_calculateCapacity(initialCapacity))
//计算队列所需要容量大小
static int _calculateCapacity(int? initialCapacity) {
//如果initialCapacity为null或者指定的初始化容量小于默认的容量就是用默认的容量大小
if (initialCapacity == null || initialCapacity < _INITIAL_CAPACITY) {
return _INITIAL_CAPACITY
} else if (!_isPowerOf2(initialCapacity)) {//容量大小不是2次幂
return _nextPowerOf2(initialCapacity)
assert(_isPowerOf2(initialCapacity))
return initialCapacity
//判断容量大小是否是2次幂
static bool _isPowerOf2(int number) => (number & (number - 1)) == 0
//找到大小是接近number的二次幂的数
static int _nextPowerOf2(int number) {
assert(number > 0)
number = (number << 1) - 1
for (
int nextNumber = number & (number - 1)
if (nextNumber == 0) return number
number = nextNumber
//ListQueue的命名构造函数from
factory ListQueue.from(Iterable<dynamic> elements) {
//判断elements 是否是List<dynamic>类型
if (elements is List<dynamic>) {
int length = elements.length
ListQueue<E> queue = ListQueue<E>(length + 1)
assert(queue._table.length > length)
for (int i = 0
queue._table[i] = elements[i] as E
queue._tail = length
return queue
} else {
int capacity = _INITIAL_CAPACITY
if (elements is EfficientLengthIterable) {//如果是EfficientLengthIterable类型,就将elements长度作为初始容量不是就使用默认容量
capacity = elements.length
ListQueue<E> result = ListQueue<E>(capacity)
for (final element in elements) {
result.addLast(element as E)
return result
//ListQueue的命名构造函数of
factory ListQueue.of(Iterable<E> elements) =>
ListQueue<E>()..addAll(elements)
五、LinkedList
在dart中LinkedList比较特殊,它不是一个带泛型集合,因为它泛型类型上界是LinkedListEntry, 内部的数据结构实现是一个双链表,链表的结点是LinkedListEntry的子类,且内部维护了_next和_previous指针。此外它并没有实现List接口
import 'dart:collection';
main() {
var linkedList = LinkedList<LinkedListEntryImpl<int>>();
var prevLinkedEntry = LinkedListEntryImpl<int>(99);
var currentLinkedEntry = LinkedListEntryImpl<int>(100);
var nextLinkedEntry = LinkedListEntryImpl<int>(101);
linkedList.add(currentLinkedEntry);
currentLinkedEntry.insertBefore(prevLinkedEntry);
currentLinkedEntry.insertAfter(nextLinkedEntry);
linkedList.forEach((entry) => print('${entry.value}'));
class LinkedListEntryImpl<T> extends LinkedListEntry<LinkedListEntryImpl<T>> {
final T value;
LinkedListEntryImpl(this.value);
@override
String toString() {
return "value is $value";
常用的函数
currentLinkedEntry.insertBefore(prevLinkedEntry);
currentLinkedEntry.insertAfter(nextLinkedEntry);
currentLinkedEntry.previous;
currentLinkedEntry.next;
currentLinkedEntry.list;
currentLinkedEntry.unlink();
linkedList.forEach((entry) => print('${entry.value}'));
for (var i = 0; i < linkedList.length; i++) {
print('${linkedList.elementAt(i).value}');
for (var element in linkedList) {
print('${element.value}');
六、HashMap
import 'dart:collection'
main() {
var hashMap = HashMap()
hashMap['a'] = 1
hashMap['b'] = 2
hashMap['c'] = 3
var hashMap2 = HashMap.from(hashMap)
var hashMap3 = HashMap.of(hashMap)
var keys = ['a', 'b', 'c']
var values = [1, 2, 3]
var hashMap4 = HashMap.fromIterables(keys, values)
hashMap2.forEach((key, value) => print('key: $key value: $value'))
常用的函数
import 'dart:collection'; //注意: HashMap位于dart:collection包中需要导包
main() {
var hashMap = HashMap();//通过HashMap主构造器初始化
hashMap['a'] = 1;
hashMap['b'] = 2;
hashMap['c'] = 3;
print(hashMap.containsKey('a'));//false
print(hashMap.containsValue(2));//true
print(hashMap.keys.toList());//['a','b','c']
print(hashMap.values.toList());//[1, 2, 3]
hashMap['a'] = 55;//修改指定key的元素
hashMap.remove('b');//移除指定key的元素
import 'dart:collection';
main() {
var hashMap = HashMap();
hashMap['a'] = 1;
hashMap['b'] = 2;
hashMap['c'] = 3;
hashMap.forEach((key, value) => print('key is $key, value is $value'));
构造函数源码分析
external factory HashMap(
{bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey)});
external factory HashMap.identity();
factory HashMap.from(Map other) {
Map<K, V> result = HashMap<K, V>();
other.forEach((k, v) {
result[k] = v;
return result;
factory HashMap.of(Map<K, V> other) => HashMap<K, V>()..addAll(other);
factory HashMap.fromIterable(Iterable iterable,
{K key(element), V value(element)}) {
Map<K, V> map = HashMap<K, V>();
MapBase._fillMapWithMappedIterable(map, iterable, key, value);
return map;
factory HashMap.fromIterables(Iterable<K> keys, Iterable<V> values) {
Map<K, V> map = HashMap<K, V>();
MapBase._fillMapWithIterables(map, keys, values);
return map;
HashMap对应的@Patch源码实现,sdk/lib/_internal/vm/lib/collection_patch.dart
@patch
class HashMap<K, V> {
@patch
factory HashMap(
{bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey)}) {
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _HashMap<K, V>();
hashCode = _defaultHashCode;
} else {
if (identical(identityHashCode, hashCode) &&
identical(identical, equals)) {
return new _IdentityHashMap<K, V>();
equals ??= _defaultEquals;
} else {
hashCode ??= _defaultHashCode;
equals ??= _defaultEquals;
return new _CustomHashMap<K, V>(equals, hashCode, isValidKey);
@patch
factory HashMap.identity() => new _IdentityHashMap<K, V>();
Set<K> _newKeySet();
七、Map、HashMap、LinkedHashMap、SplayTreeMap区别
在Dart中还有一个SplayTreeMap,它的初始化、常用的函数和遍历方式和LinkedHashMap、HashMap使用类似。但是Map、HashMap、LinkedHashMap、SplayTreeMap有什么区别呢。
Map是key-value键值对集合。在Dart中的Map中的每个条目都可以迭代的。迭代顺序取决于HashMap,LinkedHashMap或SplayTreeMap的实现。如果您使用Map构造函数创建实例,则默认情况下会创建一个LinkedHashMap。
HashMap
HashMap不保证插入顺序。如果先插入key为A的元素,然后再插入具有key为B的另一个元素,则在遍历Map时,有可能先获得元素B。
LinkedHashMap
LinkedHashMap保证插入顺序。根据插入顺序对存储在LinkedHashMap中的数据进行排序。如果先插入key为A的元素,然后再插入具有key为B的另一个元素,则在遍历Map时,总是先取的key为A的元素,然后再取的key为B的元素。
SplayTreeMap
SplayTreeMap是一个自平衡二叉树,它允许更快地访问最近访问的元素。基本操作如插入,查找和删除可以在O(log(n))时间复杂度中完成。它通过使经常访问的元素靠近树的根来执行树的旋转。因此,如果需要更频繁地访问某些元素,则使用SplayTreeMap是一个不错的选择。但是,如果所有元素的数据访问频率几乎相同,则使用SplayTreeMap是没有用的。
八、命名构造函数from和of的区别以及使用建议
通过上述各个集合源码可以看到,基本上每个集合(List、Set、LinkedHashSet、LinkedHashMap、Map、HashMap等)中都有from和of命名构造函数。可能有的人有疑问了,它们有什么区别,各自的应用场景呢。其实答案从源码中就看出一点了。以List,Map中的from和of为例。
main() {
var map = {'a': 1, 'b': 2, 'c': 3}
var fromMap = Map.from(map)
var ofMap = Map.of(map)
var list = [1, 2, 3, 4]
var fromList = List.from(list)
var ofList = List.of(list)
复制代码
从上述例子可以看出List、Map中的from函数返回对应的集合泛型类型是 List<dynamic> 和 Map<dynamic, dynamic> 而of函数返回对应集合泛型类型实际类型是 List<int> 和 Map<String, int>。我们都知道dynamic是一种无法确定的类型,在编译期不检查类型,只在运行器检查类型,而具体类型是在编译期检查类型。而且从源码中可以看到 from函数往往会处理比较复杂逻辑比如需要重新遍历传入的集合然后把元素加入到新的集合中,而of函数只需要创建一个新的对象通过addAll函数批量添加传入的集合元素。
所以这里为了代码效率考虑给出建议是: 如果你传入的原有集合元素类型是确定的,请尽量使用of函数创建新的集合,否则就可以考虑使用from函数。
到这里我们dart语法系列第二篇就结束了,相信通过这篇文章大家对dart中的集合应该有了全面的了解,下面我们将继续研究dart和Flutter相关内容。
我的公众号
这里有最新的Dart、Flutter、Kotlin相关文章以及优秀国外文章翻译,欢迎关注~~~