作者:liuxin,华为高级工程师
容器类,顾名思义就是存储的类,用于存储各种数据类型的元素,并具备一系列处理数据元素的方法。在方舟开发框架中,容器类采用了类似静态的语言来实现,并通过NAPI框架对外提供。通过对存储位置以及属性的限制,让每种类型的数据都能在完成自身功能的基础上剪除冗余分支,保证了数据的高效访问,提升了应用的性能。
本期,我们将为大家介绍方舟开发框架中容器类的各种类型以及相关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 容器类对象模型的构造流程
(注:TS中的实现,扩容策略及初始分配容量不感知)
(四)拦截器处理
拦截器处理,是指通过禁止掉一些影响对象行为的操作,比如delete、setPrototype等,在运行时(Runtime)维护一个高效的容器类对象。如图4所示,以ArrayList为例,ArkCompiler内部拦截的操作主要涉及DeleteProperty、DefineProperty、GetProperty、SetPrototype、GetOwnPropertyKeys、HasProperty等操作限制数组的holy添加,以及更改属性的attributes等操作,保证了不需要做JSArray必须做的holy 判断、writable 判断等操作。

图4 拦截器处理
通过上文的介绍,相信大家对容器类已经有了比较深刻的认识。那么,我们怎么使用容器类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开发资源和开发者活动资讯
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po