陣列和串列的差異
Groovy 的串列在 Java 中其實對應兩種容器:
- 動態陣列 (dynamic array),相當於 java.util.ArrayList
- 連結串列 (linked list),相當於 java.util.LinkedList
預設情形下,Groovy 的串列是 ArrayList
,但可在宣告串列時指定型態。在 Groovy 中,使用這兩種容器的語法大部分是相同的,所以要知道其內部實作的差異。
連結串列內部以不在記憶體中不連續的節點來相連:
由於節點間不連續,新增和移除節點相對容易,只要配置節點的記憶體後將該節點和相鄰節點連結即可。但要以索引 (index) 隨機存取節點的值就不方便,因為要逐一走訪這些節點。
相對來說,陣列內部則是連續配置且緊密排列的節點:
由於節點間緊密排列,以索引隨機存取的效率相當好。但是,要新增或移除節點就很不方便,要先配置一塊夠大的陣列後,再將陣列元素逐一搬動。
兩種容器各有優缺點,要視當下的情境去選擇。有關陣列和串列的討論和比較可看一些資料結構的資料。
建立串列
以下例子建立串列實字 (list literal),存取其大小和其中的元素:
def list = [1, 2, 3]
assert list.size() == 3
assert list[0] == 1
asset list instanceof java.util.ArrayList
串列以數字為索引 (index),從零開始計算 (zero-based)。
在預設情形下,Groovy 的串列是 ArrayList
。但我們可以使用 as
指定串列的型態:
def list = [5, 6, 7, 8] as java.util.LinkedList
// Use list as before.
assert list.size() == 4
assert list[0] == 5
// Now list is a `java.util.LinkedList`
assert list instanceof java.util.LinkedList
這時候串列是 LinkedList
。
存取串列元素
除了基本的以索引存取元素外,Groovy 提供一些語法糖來存取串列元素,參考下例:
def list = ["a", "b", "c", "d", "e", "f"]
// Retrieve sublist
assert list[0..2] == ["a", "b", "c"]
assert list[0,2,4] == ["a", "c", "e"]
// Replace sublist with another sublist
list[0..2] = ["x", "y", "z"]
assert list == ["x", "y", "z", "d", "e", "f"]
// Remove sublist
list -= list[3..5]
assert list == ["x", "y", "z"]
// Insert sublist
list[1..1] = [0, 1, 2]
assert list == ["x", 0, 1, 2, "z"]
如果覺得這些語法糖過於花俏,不一定要使用,也可以用比較基本的方式來寫。
走訪串列
在 Groovy 中,可透過 for
迴圈或迭代器 (iterator) 走訪元素。下例使用 for
搭配迭代器:
/* Convert a range to a list. */
final list = ("a" .. "e").toList()
for (e in list) {
println e
}
必要時,也可用傳統的 C 風格 for
迴圈來走訪:
final list = ("a" .. "e").toList()
for (def i = 0; i < list.size(); ++i) {
println list[i]
}
以下範例則是直接用串列的迭代器來走訪:
[5, 6, 7, 8].each { e ->
println e
}
Groovy 的語法比較多元,選擇最符合當下語境的即可。
利用高階函式操作串列
以下的例子進行一系列串列的操作:
def n = (1..10).toList()
.findAll { it % 2 != 0 } /* Filter */
.collect { it ** 2 } /* Map */
.inject(0) { a, b -> a + b } /* Reduce */
assert n == 1 ** 2 + 3 ** 2 + 5 ** 2 + 7 ** 2 + 9 ** 2
在這個例子,短短數行程式就進行了數項串列的操作。一開始時,先將 1..10
的 range 轉串列,再用 findAll
留下符合條作的元素,再用 collect
將這些元素逐一轉換,最後用 inject
將這些元素以相加合併。這算是高階函式 (higher-order function) 的應用,一開始看不懂不用太勉強。