HarmonyOS方舟開發框架容器類API的介紹與使用

newknight發表於2022-03-07

作者:liuxin,華為高階工程師

 

容器類,顧名思義就是儲存的類,用於儲存各種資料型別的元素,並具備一系列處理資料元素的方法。在方舟開發框架中,容器類採用了類似靜態的語言來實現,並通過NAPI框架對外提供。通過對儲存位置以及屬性的限制,讓每種型別的資料都能在完成自身功能的基礎上剪除冗餘分支,保證了資料的高效訪問,提升了應用的效能。

 

本期,我們將為大家介紹方舟開發框架中容器類的各種型別以及相關API的使用。 

 

一、容器類API介紹


在方舟開發框架中,提供了線性和非線性兩類容器類,共14種,每種容器都有自身的特性及使用場景。下面,我們將為大家一一道來。 

 

(一)線性容器類


線性容器類底層主要通過陣列實現,包括ArrayList、Vector、List、LinkedList、Deque、Queue、Stack七種。線性容器類API,充分考慮了資料訪問的速度,實現了執行時(Runtime)通過一條指令就可以完成增刪改查等操作。 

 

1. ArrayList

 

ArrayList即動態陣列,可用來構造全域性的陣列物件。ArrayList依據泛型定義,要求儲存位置是一片連續的記憶體空間,初始容量大小為10,並支援動態擴容,每次擴容大小為原始容量的1.5倍。ArrayList進行增、刪、改、查操作的相關API如下: 

 

2. Vector

 

Vector 是指連續儲存結構,可用來構造全域性的陣列物件。Vector依據泛型定義,要求儲存位置是一片連續的記憶體空間,初始容量大小為10,並支援動態擴容,每次擴容大小為原始容量的2倍。 

 

由於Vector擴容速度高於ArrayList,所以適用於資料新增比較頻繁的場景。Vector在支援操作符訪問的基礎上,還增加了get/set介面,提供更為完善的校驗及容錯機制,滿足使用者不同場景下的需求。Vector進行增、刪、改、查操作的相關API如下: 

 

3. List

 

List可用來構造一個單向連結串列物件,即只能通過頭結點開始訪問到尾節點。List依據泛型定義,在記憶體中的儲存位置可以是不連續的。 

 

可以通過get/set等介面對儲存的元素進行修改,List進行增、刪、改、查操作的相關API如下: 

 

4. LinkedList

 

LinkedList可用來構造一個雙向連結串列物件,可以在某一節點向前或者向後遍歷List。LinkedList依據泛型定義,在記憶體中的儲存位置可以是不連續的。

 

可以通過get/set等介面對儲存的元素進行修改,LinkedList進行增、刪、改、查操作的相關API如下: 

 

5. Queue

 

Queue可用來構造佇列物件,儲存元素遵循先進先出的規則。Queue依據泛型定義,要求儲存位置是一片連續的記憶體空間,初始容量大小為8,並支援動態擴容,每次擴容大小為原始容量的2倍。Queue底層採用迴圈佇列實現,入隊及出隊操作效率都比較高。Queue進行增、刪、改、查操作的相關API如下: 

 

6. Deque

 

Deque可用來構造雙端佇列物件,儲存元素遵循先進先出的規則,雙端佇列可以分別從對頭或者隊尾進行訪問。Deque依據泛型定義,要求儲存位置是一片連續的記憶體空間,其初始容量大小為8,並支援動態擴容,每次擴容大小為原始容量的2倍。Deque底層採用迴圈佇列實現,入隊及出隊操作效率都比較高。Deque進行增、刪、改、查操作的相關API如下:

 

7. Stack

 

Stack可用來構造棧物件,儲存元素遵循後進先出的規則。Stack依據泛型定義,要求儲存位置是一片連續的記憶體空間,初始容量大小為8,並支援動態擴容,每次擴容大小為原始容量的1.5倍。Stack底層基於陣列實現,入棧出棧均從陣列的一端操作,Stack進行增、刪、改、查操作的相關API如下: 

 

(二)非線性容器類


非線性容器類底層通過hash或者紅黑樹實現,包括HashMap、HashSet、TreeMap、TreeSet、LightWeightMap、LightWeightSet、PlainArray七種。非線性容器類中的key及value的型別均滿足ECMA標準。

 

1. HashMap

 

HashMap可用來儲存具有關聯關係的key-value鍵值對集合,儲存元素中key是唯一的,每個key會對應一個value值。HashMap依據泛型定義,集合中通過key的hash值確定其儲存位置,從而快速找到鍵值對。HashMap的初始容量大小為16,並支援動態擴容,每次擴容大小為原始容量的2倍。HashMap底層基於HashTable實現,衝突策略採用鏈地址法。HashMap進行增、刪、改、查操作的相關API如下: 

 

2. HashSet

 

HashSet可用來儲存一系列值的集合,儲存元素中value是唯一的。依據泛型定義。集合中通過value的hash值確定其儲存位置,從而快速找到該值。HashSet初始容量大小為16,支援動態擴容,每次擴容大小為原始容量的2倍。value的型別滿足ECMA標準中要求的型別。HashSet底層基於HashTable實現,衝突策略採用鏈地址法。HashSet進行增、刪、改、查操作的相關API如下: 

 

3. TreeMap

 

TreeMap可用來儲存具有關聯關係的key-value鍵值對集合,儲存元素中key是唯一的,每個key會對應一個value值。TreeMap依據泛型定義,集合中的key值是有序的,TreeMap的底層是一棵二叉樹,可以通過樹的二叉查詢快速的找到鍵值對。key的型別滿足ECMA標準中要求的型別。TreeMap中的鍵值是有序儲存的。TreeMap底層基於紅黑樹實現,可以進行快速的插入和刪除。TreeMap進行增、刪、改、查操作的相關API如下: 

 

4. TreeSet

 

TreeSet可用來儲存一系列值的集合,儲存元素中value是唯一的。TreeSet依據泛型定義,集合中的value值是有序的,TreeSet的底層是一棵二叉樹,可以通過樹的二叉查詢快速的找到該value值,value的型別滿足ECMA標準中要求的型別。TreeSet中的值是有序儲存的。TreeSet底層基於紅黑樹實現,可以進行快速的插入和刪除。TreeSet進行增、刪、改、查操作的相關API如下: 

 

5. LightWeightMap

 

LigthWeightMap可用來儲存具有關聯關係的key-value鍵值對集合,儲存元素中key是唯一的,每個key會對應一個value值。LigthWeightMap依據泛型定義,採用更加輕量級的結構,集合中的key值的查詢依賴於hash值以及二分查詢演算法,通過一個陣列儲存hash值,然後對映到其他陣列中的key值以及value值,key的型別滿足ECMA標準中要求的型別。 

 

初始預設容量大小為8,每次擴容大小為原始容量的2倍。LigthWeightMap底層標識唯一key通過hash實現,其衝突策略為線性探測法,查詢策略基於二分查詢法。LigthWeightMap進行增、刪、改、查操作的相關API如下:

 

6. LightWeightSet

 

LigthWeightSet可用來儲存一系列值的集合,儲存元素中value是唯一的。LigthWeightSet依據泛型定義,採用更加輕量級的結構,初始預設容量大小為8,每次擴容大小為原始容量的2倍。集合中的value值的查詢依賴於hash以及二分查詢演算法,通過一個陣列儲存hash值,然後對映到其他陣列中的value值,value的型別滿足ECMA標準中要求的型別。 

 

LigthWeightSet底層標識唯一value基於hash實現,其衝突策略為線性探測法,查詢策略基於二分查詢法。LigthWeightSet進行增、刪、改、查操作的相關API如下: 

 

7. PlainArray

 

PlainArray可用來儲存具有關聯關係的鍵值對集合,儲存元素中key是唯一的,並且對於PlainArray來說,其key的型別為number型別。每個key會對應一個value值,型別依據泛型的定義,PlainArray採用更加輕量級的結構,集合中的key值的查詢依賴於二分查詢演算法,然後對映到其他陣列中的value值。 

 

初始預設容量大小為16,每次擴容大小為原始容量的2倍。PlainArray的查詢策略基於二分查詢法。PlainArray進行增、刪、改、查操作的相關API如下: 

 

 

二、容器類的實現


下面我們將以ArrayList為例,為大家介紹,容器類的實現。包括容器類的初始化、容器類的介面呼叫、容器類物件模型的構建以及攔截器處理。 

 

(一)容器類初始化


在方舟開發框架中,通過NAPI的統一框架對外層提供容器類。下面,我們將以ArrayList為例,介紹基於NAPI的容器類的載入。如下圖所示,是容器類初始化流程,在NAPI載入的過程中,會通過ArkPrivate.Load介面載入對應的容器類。ArrayList在引擎中會初始化Constructor以及Prototype並返回,最後應用側可以獲得該容器類並使用。 

 

圖1 容器類初始化流程

 

(二)容器類介面呼叫


在方舟開發框架中,容器類API的呼叫流程如圖2所示,使用者先通過new ArrayList進入引擎得到對應的arraylist物件,然後可以通過add介面向物件中新增元素,元素最終會新增到一片和該arraylist繫結的記憶體空間。可以通過[]操作符進行元素獲取,對於容器類而言,引擎會直接通過快速路徑訪問到元素儲存位置,返回該值。 

 

圖2 容器類API的呼叫流程

 

(三)容器類物件模型


在方舟開發框架中,構造容器類物件模型的流程如下圖所示,在執行時禁止再向物件上新增Properties屬性,ArrayList借用物件模型中的elements位置儲存元素。 

 

圖3 容器類物件模型的構造流程

 

  • 實現說明:通過elements儲存陣列元素,Length為陣列中元素個數,陣列Capatity可以通過elements的長度獲取。
  • 擴容策略:ArrayList –> 1.5倍
  • 初始分配容量:ArrayList -> 10

(注:TS中的實現,擴容策略及初始分配容量不感知) 

 

(四)攔截器處理


攔截器處理,是指通過禁止掉一些影響物件行為的操作,比如delete、setPrototype等,在執行時(Runtime)維護一個高效的容器類物件。如圖4所示,以ArrayList為例,ArkCompiler內部攔截的操作主要涉及DeleteProperty、DefineProperty、GetProperty、SetPrototype、GetOwnPropertyKeys、HasProperty等操作限制陣列的holy新增,以及更改屬性的attributes等操作,保證了不需要做JSArray必須做的holy 判斷、writable 判斷等操作。 

 

圖4 攔截器處理

 

三、容器類API的使用


通過上文的介紹,相信大家對容器類已經有了比較深刻的認識。那麼,我們怎麼使用容器類API呢?

 

本文列舉常用的典型容器的使用示例,包括匯入模組、增加元素、訪問元素及修改等操作:

 

// ArrayList
import ArrayList from '@ohos.util.ArrayList' // 匯入ArrayList模組
let arrayList = new ArrayList();
arrayList.add("a");
arrayList.add(1);    // 增加元素
print(arrayList[0]); // 訪問元素
arrayList[0] = one"; // 修改元素
print(arrayList[0]);


// Vector
import Vector from '@ohos.util.Vector'  // 匯入Vector模組
let vector = new Vector();
vector.add("a");
let b = [1, 2, 3];
vector.add(b);
vector.add(false); // 增加元素
print(vector[0]);  // 訪問元素
print(vector.getFirstElement()); // 訪問元素


// Deque
import Deque from '@ohos.util.Deque'  // 匯入Deque模組
let deque = new Deque;
deque.insertFront("a");
deque.insertFront(1); // 增加元素
print(deque[0]);      // 訪問元素
deque[0] = "one";     // 修改元素
print(deque[0]);


// Stack
import Stack from '@ohos.util.Stack'  // 匯入Stack模組  
let stack = new Stack();
stack.push("a");
stack.push(1);   // 增加元素
print(stack[0]); // 訪問元素
stack.pop();     // 彈出元素
print(stack.length);


// List
import List from '@ohos.util.List'  // 匯入List模組
let list = new List;
list.add("a");
list.add(1);
let b = [1, 2, 3];
list.add(b);        // 增加元素
print(list[0]);     // 訪問元素
print(list.get(0)); // 訪問元素


// HashMap
import HashMap from '@ohos.util.HashMap'   // 匯入HashMap模組
let hashMap = new HashMap();
hashMap.set("a", 123);
hashMap.set(4, 123);      // 增加元素
print(hashMap.hasKey(4)); // 判斷是否含有某元素
print(hashMap.get("a"));  // 訪問元素


// TreeMap
import TreeMap from '@ohos.util.TreeMap'   // 匯入TreeMap模組
let treeMap = new TreeMap();
treeMap.set("a", 123);
treeMap.set("6", 356);           // 增加元素
print(treeMap.get("a"));         // 訪問元素
print(treeMap.getFirstKey("a")); // 訪問首元素
print(treeMap.getLastKey("a"));  // 訪問尾元素


// LightWeightMap
import LightWeightMap from '@ohos.util.LightWeightMap' // 匯入LightWeightMap模組
let lightWeightMap = new LightWeightMap();
lightWeightMap.set("x", 123);
lightWeightMap.set("8", 356);   // 增加元素
print(lightWeightMap.get("a")); // 訪問元素
print(lightWeightMap.get("x")); // 訪問元素
print(lightWeightMap.getIndexOfKey("8")); // 訪問元素


// PlainArray
import PlainArray from '@ohos.util.PlainArray'   // 匯入PlainArray模組
let plainArray = new PlainArray();
plainArray.add(1, "sdd");
plainArray.add(2, "sff");      // 增加元素
print(plainArray.get(1));      // 訪問元素
print(plainArray.getKeyAt(1)); // 訪問元素

 


至此以上就是本期全部內容,期待廣大開發者通過方舟開發框架的容器類開發出更多高效能的應用。

 

 

掃碼新增開發者小助手微信 

獲取更多HarmonyOS開發資源和開發者活動資訊

相關文章