集合类操作优化经验总结-Java开发Java经验技巧_第1页
集合类操作优化经验总结-Java开发Java经验技巧_第2页
集合类操作优化经验总结-Java开发Java经验技巧_第3页
集合类操作优化经验总结-Java开发Java经验技巧_第4页
集合类操作优化经验总结-Java开发Java经验技巧_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、集合类操作优化经验总结-编程开发技术集合类操作优化经验总结原文出处:ibm周明耀在实际的项目开发中会冇很多的对象,如何高效、方便地管理对象,成为影响程 序性能与可维护性的重要环节。java提供了集合框架来解决此类问题,线性表、 链表、哈希表等是常用的数据结构,在进行java开发时,jdk已经为我们捉供 了一系列相应的类来实现基本的数据结构,所有类都在订这个包里, 清单1描述了集合类的关系。清单1.集合类之间关系collection卜 list| -linkedlist| 卜arraylist| vector| lstacklsetmap(-hash table(-hashmaplwcaklla

2、shmap本文讲的就是集合框架的使用经验总结,注意,本文所冇代码基于jdk7o集合接口col lection 接口collection是最基木的集合接口,一个collection代表一组object,即 collection的元素(elements)。一些collection允许相同的元素、支持对 元素进行排序,另一些则不行。jdk不提供直接继承口 collection的类,jdk提 供的类都是继承自collection的子接口,如list和set。所有实现 collection接口的类都必须提供两个标准的构造函数,无参数的构造函数用于 创建一个空的collection,有一个collecti

3、on参数的构造函数用于创建一个 新的collection,这个新的collection与传入的collection冇相同的元素, 后一个构造函数允许用户复制一个collectiono 如何遍历collection中的每一个元素? 不论collection的实际类型如何,它都支持一个iterator()的方法,该方法 返冋一个迭代子,使用该迭代子即可逐一访问collection中每一个元素。典型 的用法如下:iterator it = collection. iteratoro ; / 获得一个迭代子while (it. hasnext () object obj = it. next () ;

4、 / 得到下一个元素collection接口派生的两个接口是list和set。collection接口提供的主要方法:1. boolean add(object o)添加对彖到集合;2. boolean remove(object o)删除指定的对象;3. int size()返回当前集合中元索的数量;4. boolean contains(object o)查找集合屮是否有指定的对象;5. boolean isemptyo判断集合是否为空;6. iterator iterator()返回一个迭代器;7. boolean containsall(collection c)査找集合中是否有集合c

5、中的丿匸素;8. boolean addall(collection c)将集合c中所有的元素添加给该集合;9. void clear()删除集合小所有元素;10. void removealkcollection c)从集合中删除c集合中也有的元素;11. void retainall(collection c)从集合中删除集合c中不包含的元素。list 接口list是冇序的collection,使用此接口能够精确的控制每个元索插入的位置。 用户能够使用索引(元素在list中的位置,类似于数组卜标)來访问list中 的元素,这类似于java的数组。和下文要提到的set不同,list允许有相同

6、 的元素。除了具有col lection接口必备的iterator ()方法外,list还提供一个 listiterator()方法,返冋一个 listiterator 接口。和标准的 iterator 接 口相比,listltcrator多了一些add() z类的方法,允许添加、删除、设定元 索、向前或向后遍历等功能。实现list接口的常用类有linkedlist, arraylist, vector 和 stack 等。list接口提供的主要方法:1. void add(int index,object element)在指定位置上添加一个对象;2. boolean addall(int

7、index,collection c)将集合c的元素添加到指定的位置;3. object get(int index)返回list中指定位置的元素;4. int indexof(object o)返回第一个岀现元素0的位置;5. object removeint(int index)删除指定位置的元索;6. object set(int index,object element)用元素 element 取代位置 index 上的元素,返 回被取代的元索。map 接口map没有继承collection接口。map捉供key到value的映射,一个map屮 不能包含相同的key,每个key只能映射

8、一个value。map接口提供3种集合 的视图,map的内容可以被当作一组key集合,一组value集合,或者一组 key-value 映射。map提供的主要方法:1. boolean equals(object o)比较对象;2. boolean remove(object o)删除一个对象;3. put(object key,object value)添加 key 和 valueorandomaccess 接口randomacccss接口是一个标志接口,本身并没有提供任何方法,任务凡是通过 调用randomaccess接口的对象都可以认为是支持快速随机访问的对象。此接口 的主要目的是标识那

9、些可支持快速随机访问的list实现。任何一个基于数组的 list实现都实现了 raodomaccess接口,而基于链表的实现则都没有。因为只 冇数组能够进行快速的随机访问,而对链表的随机访问需要进行链表的遍历。因 此,此接口的好处是,可以在应用程序中知道正在处理的list对象是否可以进 行快速随机访问,从而针对不同的list进行不同的操作,以捉高程序的性能。集合类介绍linkedlist 类linkcdlist实现了 list接口,允许null元素。此外linkedlist提供额夕卜 的get、remove、insert等方法在linkedlist的首部或尾部操作数据。这些 操作使得linke

10、dlist可被用作堆栈(stack)、队列(queue)或双向队列(deque)。 请注意linkedlist没有同步方法,它不是线程同步的,即如果多个线程同吋访 问一个list,则必须自己实现访问同步。一种解决方法是在创建list时构造 一个同步的list,方法如list list = collections synchronizedlist (new linkedlist ();arraylist 类arraylist实现了可变大小的数组。它允许所冇元素,包括nullosize>isempty> get、set等方法的运行时间为常数,但是add方法开销为分摊的常数,添加n 个元

11、素需要0(n)的吋间,其他的方法运行吋间为线性。每个arraylist实例都有一个容量(capacity),用于存储元素的数组的大小, 这个容量可随着不断添加新元素而自动增加。当需要插入大量元素时,在插入前 可以调用ensurecapacity方法来增加arraylist的容量以提高插入效率。和 linkedlist 样,arraylist 也是线程非同步的(unsynchronized)。arraylist提供的主要方法:1. boolean add(object o)将指定元素添加到列表的末尾;2. boolean add(int index,object element)在列表中指定位置

12、加入指定元素;3. boolean addall(collcction c)将指定集合添加到列表末尾;4. boolean addall(int index,collection c)在列表中指定位置加入指定集合;5. boolean clear()删除列表中所有元素;6. boolean clone()返回该列表实例的一个拷贝;7. boolean contains(object o)判断列表屮是否包含元素;8. boolean cnsurccapacity(int m)增加列表的容呈:,如果必须,该列表能够容纳m个 元素;9. object get(int index)返回列表中指定位置的

13、元素;10. int indexof(object elem)在列表中查找指定元素的卜标;11. int size()返回当前列表的元素个数。vector 类vector非常类似于arraylist,区别是vector是线程同步的。由vector创 建的iterator,虽然和arraylist创建的iterator是同一接口,但是,因为 vector是同步的,当一个iterator被创建而且正在被使用,另一个线程改变 t vector的状态(例如,添加或删除了一些元索),这时调用iterator的方 法时将抛出concurrcntmodificationexccption,因此必须捕获该异常

14、。stack 类stack继承口 vector,实现了一个后进先出的堆栈。stack提供5个额外的方 法使得vector得以被当作堆栈使用。除了基木的push和pop方法,还有 peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一 个元素在堆栈小的位置。注意,stack刚创建后是空栈。set类set是一种不包含重复的元素的collection,即任意的两个元素el和e2都 有el. equals (e2)二false。set最多有一个null兀素。很明显,set的构造函 数有一个约束条件,传入的collection参数不能包含重复的元索。请注意,必 须小心操作可变

15、对彖(mutable object),如果一个set中的可变元素改变了 自身状态,这可能会导致一些问题。hashtable 类hashtable继承map接口,实现了一个基于key-value映射的哈希表。任何非 空(non-null)的对象都可作为key或者value。添加数据使用put (key, value),取岀数据使用get (key),这两个基本操作的时间开销为常数。hashtable通过initial capacity和load factor两个参数调整性能。通常 缺省的load factor 0. 75较好地实现了吋间和空间的均衡。增大load factor 可以节省空间但相应

16、的查找时间将壇大,会影响像get和put这样的操作。使 用ilashteiblc的简单示例,将1、2、3这三个数字放到iiashtablc里面,他 们的key分别是” one”、” two”、” three",代码如清单2所示。清单2 . hashtable示例hashtable numbers = new hashtable(); numbers.put( "one”,new integer (1); numbers, put( "two”,new integer(2); numbersput( "three" , new integer(3

17、);如果我们需要取出一个数,比女h 2,可以用相应的key来取出,代码如清单3所/ji o清单3.从hastable读取数据integer n 二(integer)numbers, get( "two” );system .out. printl n( "two 二” + n);由于作为key的对象将通过计算其散列函数来确定与z对应的value的位置, 因此任何作为key的对彖都必须实现hashcode和equals方法0 hashcode和 equals方法继承自根类object,如果你用自定义的类当作key的话,要相当 小心,按照散列函数的定义,如果两个对象相同,即ob

18、jl. equals(obj2)=true, 贝lj它们的ilashcode必须相同,但如果两个对象不同,则它们的hashcode不一 定不同,如呆两个不同对彖的hashcode相同,这种现彖称为冲突,冲突会导致 操作哈希表的时间开销增大,所以尽量定义好的hashcodeo方法,能加快哈希 表的操作。如果相同的对象有不同的hashcode,对哈希表的操作会! 1|现意想不到的结果(期 待的get方法返回null),要避免这种问题,最好同时复写equals方法和 hashcode方法,而不要只写其中一个。hashmap 类hashmap和hashtable类似,不同之处在于hashmap是线程非

19、同步的,并且允 许 null,即 null value 和 null key。但是将 hashmap 视为 collection 时 (values()方法可返回collection),其迭代子操作吋间开销和hashmap的 容量成比例。因此,如果迭代操作的性能相当重要的话,不要将hashmap的初 始化容量设得过高,或者load factor参数设置过低。weakhashmap 类weakhashmap是一种改进的hashmap,它对key实彳亍"弱引用”,如果一个key 不再被外部所引用,那么该key可以被gc冋收。集合类实践arraylist> vcctor> li

20、nkedlist 均来自 abstractlist 的实现,血 abstractlist 直接实现了 list 接口,并扩展自 abstarctcollectiono arraylist和vector使用了数组实现,arraylist没有对任何一个方法提供线 程同步,因此不是线程安全的,vector中绝大部分方法都做了线程同步,是一 种线程安全的实现。linkedlist使用了循坏双向链表数据结构,由一系列表项 连接而成,一个表项总是包含3个部分,元素内容、前驱表项和后驱表项。当arraylist对容量的需求超过当前数组的大小时,需要进行扩容。扩容过程 中,会进行大量的数组复制操作,而数组复制

21、时,最终将调用system, arraycopy () 方法。linkedlist由于使用了链表的结构,因此不需要维护容量的大小,然而 每次的元索壇加都需要新建一个entry对象,并进行更多的赋值操作,在频繁 的系统调用卜,对性能会产生一定的影响,在不间断地生成新的对象还是占用了 一定的资源。而因为数组的连续性,因此总是在尾端增加元素时,只冇在空间不 足时才产生数组扩容和数组复制。arraylist是基于数组实现的,而数组是一块连续的内存空间,如果在数组的任 意位置插入元索,必然导致在该位置后的所有元索需要重新排列,因此其效率较 茅,尽可能将数据插入到尾部。linkedlist不会因为插入数据

22、导致性能卜降。arraylist的每一次有效的元索删除操作后都要进行数组的重组,并且删除的元 素位置越靠前,数组重组时的开销越大,要删除的元素位置越靠后,开销越小。 linkedlist耍移除中间的数据需耍便利完半个list。清单4. arraylist和linkedlist使用代码import java. util. arraylist;import java.util. linkedlist;public class arraylistandlinkedlist public static void mdin(string args) long start 二 system, currcn

23、ttimcmillis();arraylist list = new arraylist (); object obj = new object();for (int i=0;i<5000000;i+)1 ist. add (obj);long end = system. currenttimemillis(); system out. printin(end-start);start 二 system, currenttimemi11i s(); linkcdlist listl 二 new linkcdlist(); object objl = new object();for (i

24、nt i=0;i<5000000;i+) listl. add(objl);end 二 system, currcnttimcm订lis(); system, out. printin(endstart);start 二 system, currenttimem订lis();object obj2 = new object();for (int i=0;i<1000;i+)list, add(0, obj2);end 二 system, currenttimem订lis(); system.out. println(end-start);start = system. curren

25、ttimemillis(); object obj3 = new object();for(int i=0;i<1000;i+) listl.add(objl);1jend = system. currenttimemillis(); system out. printin(end-start);start 二 system, currenttimemi11i s(); list.remove(0);end = system. currenttimemillis(); system out. printin(end-start);start 二 system, currenttimemi

26、11i s(); listl. remove(250000);end 二 system, currenttimcm订lis(); system, out. printin(endstart);清单5.运行输出639129669690015hashmap是将key做hash算法,然后将hash值映射到内存地址,直接取得 key所对应的数据。在ibshmap中,底层数据结构使用的是数组,所谓的内存 地址即数组的下标索引。hashmap的高性能需要保证以下几点:1. hash算法必须是高效的;2. hash值到内存地址(数纟r索引)的算法是快速的;3. 根据内存地址(数组索引)可以直接取得对应的值。

27、hashmap实际上是一个链表的数组。前面已经介绍过,基于iiashmap的链表方 式实现机制,只要hashcode ()和hash()方法实现得足够好,能够尽可能地减 少冲突的产生,那么对hashmap的操作儿乎等价于对数组的随机访问操作,具 有很好的性能。但是,如果hashcode ()或者hash()方法实现较差,在大量冲 突产生的情况下,hashmap事实上就退化为几个链表,对hashmap的操作等价 于遍历链表,此时性能很差。hashmap的一个功能缺点是它的无序性,被存入到hashmap屮的元素,在遍丿力 hashmap时,其输出是无序的。如果希望元素保持输入的顺序,可以使用 li

28、nkedhashmap 替代。linkedhashmap继承自hashmap,具有高效性,同时在hashmap的基础上,又 在内部增加了一个链表,用以存放兀素的顺序。hashmap通过hash算法可以最快速地进行put()和get ()操作o treemap则 提供了一种完全不同的map实现。从功能上讲,treemap有着比hashmap更为 强大的功能,它实现了 sortedmap接口,这意味着它可以对元素进行排序。treemap的性能略微低于hashmapo如果在开发中需要对元索进行排序,那么使 用ibshmap便无法实现这种功能,使用treemap的迭代输出将会以元素顺序进 行。link

29、edhashmap是基于元素进入集合的顺序或者被访问的先后顺序排序,treemap则是基于元素的固有顺序(由comparator或者comparable确定)。linkedhashmap是根据元素增加或者访问的先后顺序进行排序,而treemap则 根据元索的key进行排序。清单6所示代码演示了使用treemap实现业务逻辑的排序。清单6. treemap实现排序 import java.util.iterator; import java. util. map; import java, uti1. treemap;public class student implements compara

30、ble<student> public string name;public int score;public student (string name, int score) this, name = name;this.score 二 score;©override/告诉treemap如何排序public int compareto(student o) / todo auto-generated method stub if (o. scorethis. score) return 1;else if(o. score>this. score) return

31、t;return 0;©overridepublic string tostring() stringbuffcr sb 二 new stringbuffcr(); sb.append("name:);sb.append(name);sb. append( );sb. append("score:);sb.append(score);return sb. tostringo ;public static void main(string args) treemap map 二 new treemap();student si = new student("

32、;1", 100);student s2 = new student ("2", 99); student s3 = new student ("3", 97);student s4 二 new student 91); map. put(si, new studentdetailtnfo(sl); map.put (s2, new studentdetailtnfo(s2); map.put (s3, new studentdetailinfo(s3); map.put (s4, new studentdetaillnfo(s4);/打印分数

33、位于s4和s2之间的人 map mapl = (treemap)map). submap (s4, s2);for (iterator itcrator二mop 1. kcysct () itcrator () ; itcrator. hasnext () ;) student key = (student)iterator, next();system out. printin(key+/z-/z+map. get (key); system, out. println(submap end);打印分数比si低的人 mapl=(treemap)map). headmap(sl);for(it

34、erator iterator二map1. keyset(). iterator();iterator. hasnext();) student key 二(student)iterator. next();system, out. print in (kcy+-+meip get (key);system out. println(z,submap end");打印分数比si高的人 mapl=(treemap)map). tailmap(sl);for(iterator iterator=mapl. keyset(). iterator();iterator. hasnext();

35、) student key = (student)iterator, next();system, out. ptintln(key+-+map. get(key);system, out. println("submap end");class studentdetailinfostudent s; public studentdetaillnfo(student s) this, s 二 s;©override public string tostringo return s. name + s detail information'7;清单7 运行输

36、岀name:4 score:91->4 s name:3 score:97->3" s submap endname:4 score:91->4, s name:3 score:97->3" s name:2 score:99->2" s submap endname:1 score:100>1'submap enddetail information detail informationdetail infonnation detail information detail informationdetai 1 inf

37、ormotionweakhashmap特点是当除了自身冇对key的引用外,如果此key没冇其他引 用,那么此map会自动丢弃该值。如清单8所示代码声明了两个map对彖, 一个是hashmap, 个是weakhashmap,同口寸向两个map小放入a、b两个对 象,当hashmap删除a,并且a、b都指向null时,weakhashmap中的a将 口动被回收掉。出现这个状况的原因是,对于a对象而言,当ilashmap删除并 月将a指向null后,除了 weakhashmap屮还保存a外已经没冇指向a的指 针了,所以weakhashmap会自动舍弃掉a,而对于b对彖虽然指向了 null, 但has

38、hmap屮述有指向b的指针,所以weakhashmap将会保留b对象。清单8. weakhashmap示例代码 import java. util. ilashmap;import java.util. iterator;import java.util. map;import java. util. weakhashmap;public class wcakllashmaptcst public static void main(string args) throws exception string a = new string (/za/z);string b 二 new string(

39、b);map weakmap = new weakhashmap();map map 二 new ilashmap ();map. put (a, z,aaa/z);map. put (b, bbb);weakmap. put(a, aaa);weakmap. put(b, bbb);map. remove (a);a二null;b=null;system, gc ();tt erator i = map. e nt ryset () .it erator();while (i. hasnext () map. entry en = (map. entry) i. next ();system

40、, out. ptintln("map:+en. getkey () +:+en. getvalue ();iterator j 二 wcakmap. cntrysct(). iterator(); while (j. hasnext () map. entry en = (map. entry) j. next ();system, out. print in ("weakmap :+e n. getkey ()+:+e n. getvalue ();清单9 运行输岀 map:b:bbbweakmap:b:bbbweakhashmap主要通过expungestaleent

41、ries这个函数來实现移除其内部不用 的条目,从而达到口动释放内存的目的。基木上只要对weakhashmap的内容进 行访问就会调用这个函数,从而达到清除其内部不再为外部引用的条目。但是如 果预先生成了 weakhashmap,而在gc以前乂不曾访问该weakhashmap,那不是 就不能释放内存了吗?清单 10. weakhashmaptest 1 import java, ut订arraylist;import java.util. list;import java.util. weakhashmap;public class weakhashmaptest1 public static

42、void main(string args) throws exccption list<weakhashmap<byte, byte>> maps = new arralist<weakhashmap<byte , byte>>();for (int i 二 0; i < 1000; i+) weakhashmap<byte, byte> d 二 new weakhashmap<byte, byte >();d. put(new byte10001000, new byte10001000);maps, add (

43、d);system, gc ();system. err. println(i);不改变任何jvm参数的情况运行清单10所示代码,由于java默认内存是 64m,抛出内存溢出了错误。清单11.运行输出241242243exception in thread "main java. lang. outofmemoryerror: java heap space at weakhashmaptes11. main(weakhashmaptes11.java:10)果不其然,weakhashmap这个吋候并没有自动帮我们释放不用的内存。清单12 所示代码不会出现内存溢出问题。清单 12.

44、weakhashmaptest2import java.util. arraylist;import java.uti1. list;import java.util. weakhashmap;public class wcakilashmaptcst2 public static void main(string args) throws exception list<weakhashmap<byte, byte>> maps = newarraylist<weakhashmap<byte, byte>>();for (int i 二 0; i

45、 < 1000; i+) wcakilashmap<bytc , bytc> d = new wcakilashmap<bytc, byte >();d. put(new byte10001000, new byte10001000);maps, add (d);system, gc ();system. err println(i);for (int j = 0; j < i; j+) system, err. printin(j + " size" + maps get (j). size ();运行结果发现这次测试输出正常,不再出现

46、内存溢出问题。总的来说,weakhashmap并不是你什么也干它就能自动释放内部不用的对象的, 而是在你访问它的内容的时候释放内部不用的对象。wcakllashmap 实现弱引用,是因为它的 entry<k, v>是继承 口 wcakrcfcrcncc<k> 的,在weakhashmap$entry<k, v>的类定义及构造函数里而如清单13所示。清单13. weakhashmap类定义 private static class entry<k, v> extends weakreference<k>implements map. entryk, v> entry(k key, v value, referencequeuek> queue, int hash, entry<k,v> next) super (key, queue);this.value = value;this, hash = hash;this, next 二 next;请注意它构造父类的语句:"super (key, queue);” ,传入的是key,因此key 才是进行弱引用的,value是直接强引用关联在this, value z屮。在s

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论