数组
Tuple
tuple
函数可以创建一个固定大小的数组,等价 java 的类型为 Object []
:
## examples/tuple.av
let t = tuple(1, 2, "hello", 3.14);
println("type of t: " + type(t));
for x in t {
println(x);
}
println("count of t: "+ count(t));
println("t[0] = " + t[0]);
t[0] = 100;
println("t[0] = " + t[0]);
这个例子演示了 tuple 的基本操作: for 用来遍历数组, count 可以获取数组长度, t[x]
可以访问索引位置 x 的元素,同样也可以赋值特定位置的元素:
type of t: Object[]
1
2
hello
3.14
count of t: 4
t[0] = 1
t[0] = 100
tuple 可以放入任意类型的元素,如果要创建特定类型的,就需要用到其他函数。
创建类型数组并填充
可以通过 seq.array(type, ..args)
可以创建 type
类型的数组:
## examples/array.av
let a = seq.array(int, 1, 2, 3, 4);
println("type(a) is : " + type(a));
println("count(a) is: " + count(a));
比如上面创建了一个 int[]
数组,他的长度是 4,元素分别为 1, 2, 3, 4
。
如果传入错误的类型,创建将报错:
let a = seq.array(int, 1, 2, "hello", 4);
报错 Unexpected param type, expected: int, given: java.lang.String
。
但是如果可以转化为目标类型,比如 double 可以转成 int ,那么还是可以创建的:
let a = seq.array(int, 1, 2, 3.3, 4);
map(a, println)
3.3
将被转为 3:
1
2
3
4
seq.array
的 type 可以是基本类型,如 short
、 byte
、 char
、 bool
、 float
、 double
、 int
、 long
以及 void
。
也可以是对象,但是需要给完整的类名,比如创建字符串数组:
let s = seq.array(java.lang.String, "hello", "world", "aviator");
println(string.join(s, ","));
string.join
函数将第一个参数的字符串集合用第二个参数的字符串起来,这里将输出 hello,world,aviator
。
创建空数组
如果要创建一个“空”数组,只确定类型和长度,可以用 seq.array_of(type, len)
,这样就创建了一个 type[len]
的数组,每个元素都将是该类型的默认值,比如整数就是 0,对象就是 null:
let a = seq.array_of(int, 3);
println("type(a) is : " + type(a));
println("count(a) is: " + count(a));
println("before assignment:");
for x in a {
println(x);
}
for i in range(0, 3) {
a[i] = i;
}
println("after assignment:");
for x in a {
println(x);
}
输出:
type(a) is : int[]
count(a) is: 3
before assignment:
0
0
0
after assignment:
0
1
2
创建多维数组
seq.array_of(Class, &dimensions)
也可以用于创建多维数组,举例来说:
## create multidimensional array
let a = seq.array_of(long, 3, 2);
assert(3 == count(a));
assert(2 == count(a[0]));
let x = 0;
for i in range(0, 3) {
for j in range(0, 2) {
a[i][j] = x;
x = x + 1;
}
}
for i in range(0, 3) {
for j in range(0, 2) {
p("a[#{i}][#{j}] = #{a[i][j]}");
}
}
我们创建了一个 2 x 3
的二维数组,并遍历初始化,最终打印数组:
a[0][0] = 0
a[0][1] = 1
a[1][0] = 2
a[1][1] = 3
a[2][0] = 4
a[2][1] = 5
遍历数组
上面其实已经演示了用 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']);
<br />
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
返回的是删除后的集合对象。