这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
遍历数组
上面其实已经演示了用 map 和 for 遍历数组的例子了,两者皆可,不过 map 将收集返回结果放入 collector 集合并返回,通常来说,如果你不需要结果,还是应当用 for 循环:
let a = seq.array(int, 1, 2, 3.3, 4);
for x in a {
println(x);
}
集合 List, Map 和 Set
在 AviatorScript 中也可以创建 java 的各种常见集合类型,比如 java.util.List、java.util.Map 和 java.util.Set 等。我们将一一介绍。
创建 List
创建一个链表可以通过 seq.list 函数:
let list = seq.list(1, 2, 3);
上面将创建三个整数组成的 ArrayList 对象, seq.list 接受不定参数,如果不传入任何参数,创建的是一个空链表:
let empty_list = seq.list();
链表和数组类似,也可以通过 a[i] = x 的方式来赋值,前提是 i 落在长度内
## examples/list.av
let list = seq.list(1, 2, 3);
list[0] = 4;
list[1] = 5;
list[2] = 6;
println(list);
如果你尝试给长度范围之外的位置赋值,都将报错:
list[3] = 7;
报错:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
因此你无法通过位置赋值的方式为一个空链表添加元素,我们将在后面操作集合里介绍添加元素的方式。
repeat 和 repeatedly
repeat(n, x) 函数用来创建一个全部是 x 的 List,并且个数为 n:
## examples/repeat.av
let list = repeat(10, "a");
p("type of list: " + type(list));
p("count of list: " + count(list));
p("list[0]=" + list[0]);
p("list is: " + list);
上面的例子将创建 10 个字符串 a 组成的链表:
type of list: java.util.ArrayList
count of list: 10
list[0]=a
list is: [a, a, a, a, a, a, a, a, a, a]
如果你有一个函数,可以产生元素,你想重复调用 n 次来产生一个集合,可以用 repeatedly(n, fn) :
let c = 0 ;
let counter = lambda() ->
c = c + 1;
return c;
end;
let list = repeatedly(10, counter);
p("type of list: " + type(list));
p("count of list: " + count(list));
p("list[0]=" + list[0]);
p("list is: " + list);
我们创建了一个闭包函数 counter ,每次调用它会返回一个数字,并且数字从 1 开始递增,然后传入 repeatedly 调用 10 次,这就产生了一个 1~10 的数字集合:
type of list: java.util.ArrayList
count of list: 10
list[0]=1
list is: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
创建 Map
创建一个 HashMap 也很容易,使用 seq.map(k1, v1, k2, v2 ...) 的方式:
## examples/hash_map.av
let m = seq.map("a", 1, "b", 2, "c", 3, 4, 5);
println(m);
key 并不要求类型一致,比如这里 key=4 ,对应的值是 5:
{a=1, b=2, c=3, 4=5}
seq.map 接受偶数个参数或者 0 个参数,不传入任何参数就是一个空的 map,可以通过 seq.put 来增加元素。
同样,对于 map ,你可以用 m.{key} 的方式来访问:
println("m.a = " + m.a);
println("m.b = " + m.b);
println("m.c = " + m.c);
但是如果你的 key 不是合法的变量,就不能用这样的方式访问了,需要用到 seq.get 函数:
println("m.4 = " + seq.get(m, 4));
可以赋值:
m.a = 100;
println("m.a = " + m.a);
但是,如果 key 不是合法变量,就需要用到 seq.put 函数:
seq.put(m, 4, 99);
println("m.4 = " + seq.get(m, 4));
为什么不是 seq.set 呢? 因为 seq.set 是用于创建集合 set 的。
**
从 5.2 开始,可以使用类似数组的语法 map[key] 来获取和设置值:
m["a"] = 'aviator';
println("m['a'] = " + m['a']);
**
key 和 value 集合
如果要获取 key 的集合,可以用 seq.keys(m) 函数, value 集合是用 seq.vals 函数:
p("key set: " + seq.keys(m));
p("value set: " + seq.vals(m));
输出:
key set: [a, b, c, 4]
value set: [aviator, 2, 3, 99]
**
创建 Set
创建不重复的元素组成的集合 Set,可以用 seq.set :
## examples/hash_set.av
let s = seq.set(1, 2, 2, "hello", 3.3, "hello");
println(s);
println("type(s) is: " + type(s));
输出:
[1, 2, hello, 3.3]
type(s) is: java.util.HashSet
我们传入的参数有重复的 2 和字符串 hello,但是最终结果是一个去重的集合 java.util.HashSet 。
Set 最常见的操作是判断某个元素是否存在,可以用 include 函数:
println(include(s, 1));
println(include(s, "hello"));
println(include(s, 100));
输出:
true
true
false
操作集合
这里我们介绍操作这些集合类的通用操作,首先是添加元素。
添加元素 seq.add
往集合里添加元素可以用上面介绍过的 seq.add(coll, element) ,它支持 List/Set ,同时他有一个三参数版本 seq.add(coll, key, value) 可以用于添加键值对到 map:
## examples/collections.av
let list = seq.list();
let set = seq.set();
let map = seq.map();
## add elements
for i in range(0, 3) {
seq.add(list, i);
seq.add(set, i);
seq.add(map, i, i);
}
println("list: " + list);
println("set: " + set);
println("map: " + map);
输出:
list: [0, 1, 2]
set: [0, 1, 2]
map: {0=0, 1=1, 2=2}
如果你的元素类型是 Map.Entry ,也可以直接调用 seq.add(m, e) 来添加:
seq.add(map, seq.entry(i, i));
seq.entry(key, value) 用于创建一个 Map.Entry 对象。
访问元素 seq.get
访问集合中的元素可以用 seq.get(coll, key) 函数,它同时支持数组和所有集合类型:
- 对于数组和链表, key 就是
0~(len - 1)的索引位置整数,返回的是该位置的值,超过范围内的访问将抛出异常。 - 对于 map 来说,key 就是键值对的 key,返回的是对应的 value。
- 对于 set 来说,key 就是集合里的元素,如果存在,返回该 key 本身,不存在返回 nil。
## retrieve elements by seq.get
for i in range(0, 3) {
assert(i == seq.get(list, i));
assert(i == seq.get(set, i));
assert(i == seq.get(map, i));
}
println("seq.get(set, 3) is: " + seq.get(set, 3)); ## nil
这里我们用了 assert 函数,它接受一个布尔值,如果为 false 将抛出 AssertFailed 异常。
判断元素是否存在
对于数组、List 和 Set 来说,判断某个元素是否存在都应该用 include(coll, element) 函数,对于数组和 List 来说,这个函数的时间复杂度是 O(n),因为要遍历整个数组或链表;对于 Set 来说是 O(1) 时间复杂度,直接调用用了 Set#contains 方法。
for i in range(0, 3) {
assert(include(list, i));
assert(include(set, i));
}
assert(!include(list, 5));
assert(!include(set, 5));
对于 map 来说,如果是判断 key 是否存在,需要用 seq.contains_key(coll, key) :
for i in range(0, 3) {
assert(seq.contains_key(map, i));
}
assert(!seq.contains_key(map, 5));
如果是判断 Map.Entry 是否存在,仍然继续使用 include :
for i in range(0, 3) {
assert(include(map, seq.entry(i, i)));
}
遍历集合
遍历集合和数组的方式一样,同样通过 for..in 语句:
## Iterate the collection by for..in loop
println("list elements:");
for x in list {
println(x);
}
println("set elements:");
for x in set {
println(x);
}
println("map elements:");
for x in map {
println(x.key + "=" + x.value);
}
对于 map 来说迭代循环中的元素就是 Map.Entry 对象,可以通过 key 和 value 属性来访问键和值。
输出:
list elements:
0
1
2
set elements:
0
1
2
map elements:
0=0
1=1
2=2
删除元素 seq.remove
删除元素也是常见的需求,可以用 seq.remove(coll, element) ,对于 List/Set 和 Map 都是如此,如果是 Map,传入的应该是 key:
## remove elements
assert(list == seq.remove(list, 2));
assert(list == seq.remove(list, 4));
assert(set == seq.remove(set, 1));
assert(map == seq.remove(map, 0));
println("list: " + list);
println("set: " + set);
println("map: " + map);
打印:
list: [0, 1]
set: [0, 2]
map: {1=1, 2=2}
可见删除生效了,删除不存在的元素不产生影响。 seq.remove 返回的是删除后的集合对象。