[Groovy] 程式設計教學:建立和使用串列 (List)

【分享本文】
Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    陣列 vs. 串列

    Groovy 的串列在 Java 中其實對應三種容器 (或資料結構):

    • 陣列 (array)
    • 動態陣列 (dynamic array),相當於 java.util.ArrayList
    • 連結串列 (linked list),相當於 java.util.LinkedList

    在 Groovy 中,使用這三種容器的語法大部分是相同的,所以要知道其內部實作的差異。

    連結串列內部以不在記憶體中不連續的節點來相連:

    連結串列

    由於節點間不連續,新增和移除節點相對容易,只要配置節點的記憶體後將該節點和相鄰節點連結即可。但要以索引 (index) 隨機存取節點的值就不方便,因為要逐一走訪這些節點。

    相對來說,陣列內部則是連續配置且緊密排列的節點:

    陣列

    由於節點間緊密排列,以索引隨機存取的效率相當好。但是,要新增或移除節點就很不方便,要先配置一塊夠大的陣列後,再將陣列元素逐一搬動。

    兩種容器各有優缺點,要視當下的情境去選擇。有關陣列和串列的討論和比較可看一些資料結構的資料。

    建立串列

    以下例子建立串列實字 (list literal),存取其大小和其中的元素:

    def list = [1, 2, 3]
    
    assert list.size() == 3
    assert list[0] == 1

    串列以數字為索引 (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 搭配迭代器:

    final list = ("a" .. "e").toList()
    
    for (e in list) {
        println e
    }
    
    list.each { println it }

    必要時,也可用傳統的 C 風格 for 迴圈來走訪:

    final list = ("a" .. "e").toList()
    
    for (def i = 0; i < list.size(); i++) {
        println list[i]
    }

    以下範例則是直接用串列的迭代器來走訪:

    [5, 6, 7, 8].each {
        println it
    }

    Groovy 的語法比較多元,選擇最符合當下語境的即可。

    利用高階函式操作串列

    以下的例子進行一系列串列的操作:

    def n = (1..10).toList()
        .findAll { it % 2 != 0 }
        .collect { it ** 2 }
        .inject(0) { a, b -> a + b }
    
    assert n == 1 ** 2 + 3 ** 2 + 5 ** 2 + 7 ** 2 + 9 ** 2

    在這個例子,短短數行程式就進行了數項串列的操作。一開始時,先將 1..10 的 range 轉串列,再用 findAll 留下符合條作的元素,再用 collect 將這些元素逐一轉換,最後用 inject 將這些元素以相加合併。這算是高階函式 (higher-order function) 的應用,一開始看不懂不用太勉強。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email
    【追蹤新文章】
    Facebook Twitter Plurk
    標籤: GROOVY, JAVA