Dart - 内置类型(List、Set、Map、Runes)
这节继续学习Dart的内置类型,List、Set和Map。
list
数组 Array 是几乎所有编程语言中最常见的集合类型,在 Dart 中数组由 List 对象表示。
Dart 中 List 字面量看起来与 JavaScript 中数组字面量一样。下面是一个 Dart List 的示例:
var list = [1, 2, 3];
这里 Dart 推断出list
的类型为 List<int>
,如果往该数组中添加一个非 int
类型的对象则会报错。
如果想要创建一个编译时常量的 List,在 List 字面量前添加 const
关键字即可:
var constantList = const [1, 2, 3];
// 取消注释将导致出错 (Uncommenting this causes an error.)
// constantList[1] = 1;
Dart 在 2.3 引入了 扩展操作符(...)
和 null-aware 扩展操作符(...?)
,它们提供了一种将多个元素插入集合的简洁方法。
例如,你可以使用扩展操作符(...)
将一个 List 中的所有元素插入到另一个 List 中:
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
如果扩展操作符右边可能为 null ,你可以使用 null-aware 扩展操作符(...?)
来避免产生异常:
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
Dart 在 2.3 还同时引入了 Collection If
和 Collection For
,在构建集合时,可以使用条件判断(if)和循环(for)。
下面示例是使用 Collection If 来创建一个 List 的示例,它可能包含 3 个或 4 个元素:
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet'
];
下面示例是使用 Collection For 将列表中的元素修改后添加到另一个列表中的示例:
var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
Set
尽管 Set 类型(type) 一直都是 Dart 的一项核心功能,但是 Set 字面量(literals) 却是在 Dart2.2 中才加入的。
下面是使用 Set 字面量来创建一个 Set 集合的方法:
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
可以使用在 {} 前加上类型参数的方式创建一个空的 Set,或者将 {} 赋值给一个 Set 类型的变量:
var names = <String>{}; // 类型+{}的形式创建Set。
// 声明类型变量的形式创建 Set (This works, too).
// Set<String> names = {};
// var names = {};
// 这样的形式将创建一个 Map 而不是 Set (Creates a map, not a set.)
疑问:用{}声明返回的是Set 还是 map?
⚠️:" Map 字面量语法同 Set 字面量语法非常相似。因为是先有的 Map 字面量语法,所以 {} 默认是 Map 类型。如果忘记在 {} 上注释类型或赋值到一个未声明类型的变量上,那么 Dart 会创建一个类型为 Map<dynamic, dynamic> 的对象。"
向一个已存在的 Set 中添加项目可以使用 add() 方法或 addAll() 方法:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
使用 .length 可以获取 Set 中元素的数量:
var elements = <String>{};
elements.add('fluorine');
elements.addAll(halogens);
assert(elements.length == 5);
可以在 Set 字面量前添加 const 关键字创建一个 Set 编译时常量:
final constantSet = const {
'fluorine',
'chlorine',
'bromine',
'iodine',
'astatine',
};
// constantSet.add('helium');
// 取消注释将导致出错 (Uncommenting this causes an error).
从 Dart 2.3 开始,Set 可以像 List 一样支持使用扩展操作符(... 和 ...?)
以及 Collection If
和Collection For
操作。
Map
Dart 中 Map 通过 Map 字面量和 Map 类型来实现,下面是一对使用 Map 字面量创建 Map 的例子:
var gifts = {
// 键: 值
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
gifts的类型推断为Map<String, String>,而 nobleGases 的类型推断为 Map<int, String>。如果你向这两个 Map 对象中添加不正确的类型值,将导致运行时异常。
你也可以使用 Map 的构造器创建 Map:
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
var nobleGases = Map();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';
疑问:这里为什么使用 Map() 而不是使用 new Map() 构造 Map 对象?
⚠️:因为从Dart2 开始,构造对象的 new 关键字可以被省略掉。
从 Dart 2.3 Map 也可以像 List 一样支持使用扩展操作符(... 和 ...?)
以及 Collection If
和Collection For
操作。
Runes
Dart 使用 Runes 符文来表示 UTF-32 编码的字符串。
Unicode 编码为每一个字母、数字和符号都定义了一个唯一的数值。因为 Dart 中的字符串是一个 UTF-16 的字符序列,所以如果想要表示 32 位的 Unicode 数值则需要一种特殊的语法。
通常使用 \uXXXX 来表示 Unicode 字符,XXXX 是一个四位数的 16 进制数字。例如心形字符(♥)的 Unicode 为 \u2665。对于不是四位数的 16 进制数字,需要使用大括号将其括起来。例如大笑的 emoji 表情(😆)的 Unicode 为 \u{1f600}。
String 类中有一些属性可以用来提取字符串的 Rune 符文信息。codeUnitAt
和 codeUnit
属性返回 16 位代码单元。runes
属性可以获取字符串的 Runes 符文。
下面的例子说明了 Rune、16位代码单元、32位代码单元之间的关系:
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
Console:
👏
[55357, 56399]
[128079]
♥ 😅 😎 👻 🖖 👍
⚠️备忘:
在使用 List 操作 Rune 的时候需要小心,根据所操作的语种、字符集等不同可能会导致字符串出现问题,具体可参考 Stack Overflow 中的提问: 我如何在 Dart 中反转一个字符串?。