日期:2011-03-22 16:17:00 来源:本站整理
决意实施策划[Java编程]
本文“决意实施策划[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
从早些时刻的那幅表示图可以看出,实际上只有三个调集组件:Map,List和Set.并且每个接口只有两种或三种实施筹划.若需利用由一个特定的接口供应的功效,若何才能决意到底采纳哪一种筹划呢?
为理解这个问题,必须熟习到每种差别的实施筹划都有自己的特点、长处和缺陷.比方在那张表示图中,可以看到Hashtable,Vector和Stack的“特点”是它们都属于“传统”类,所以不会干扰原有的代码.但在另一方面,应尽大概避免为新的(Java 1.2)代码利用它们.
其他调集间的差别普通都可归纳为它们具体是由什么“后推”的.换言之,取决于物理意义上用于实施目标接口的数据构造是什么.比方,ArrayList,LinkedList以及Vector(大致等价于ArrayList)都实现了List接口,所以无论选用哪一个,我们的程序城市得到近似的后果.但是,ArrayList(以及Vector)是由一个数组后推得到的;而LinkedList是按照通例的双重链接列表方法实现的,因为每个单独的对象都包含了数据以及指向列表内前后元素的句柄.恰是由于这个缘由,假定想在一个列表中部举行大量插入和删除操作,那么LinkedList无疑是最得当的挑选(LinkedList还有一些额外的功效,成立于AbstractSequentialList中).若非如此,就甘愿挑选ArrayList,它的速度大概要快一些.
作为另一个例子,Set既可作为一个ArraySet实现,亦可作为HashSet实现.ArraySet是由一个ArrayList后推得到的,计划成只支持少量元素,分外合适要求成立和删除大量Set对象的场所利用.但是,一旦需求在自己的Set中包容大量元素,ArraySet的性能就会大打折扣.写一个需求Set的程序时,应默许挑选HashSet.并且只有在某些特别情形下(对性能的晋升有急迫的需求),才应切换到ArraySet.
1. 决意利用何种List
为领会各种List实施筹划间的差别,最简便的办法就是举行一次性能查验.下述代码的作用是成立一个内部底子类,将其作为一个测试床利用.然后为每次查验都成立一个匿名内部类.每个这样的内部类都由一个test()办法调用.操纵这种办法,可以便利增添和删除测试项目.
//: ListPerformance.java // Demonstrates performance differences in Lists package c08.newcollections; import java.util.*; public class ListPerformance { private static final int REPS = 100; private abstract static class Tester { String name; int size; // Test quantity Tester(String name, int size) { this.name = name; this.size = size; } abstract void test(List a); } private static Tester[] tests = { new Tester("get", 300) { void test(List a) { for(int i = 0; i < REPS; i++) { for(int j = 0; j < a.size(); j++) a.get(j); } } }, new Tester("iteration", 300) { void test(List a) { for(int i = 0; i < REPS; i++) { Iterator it = a.iterator(); while(it.hasNext()) it.next(); } } }, new Tester("insert", 1000) { void test(List a) { int half = a.size()/2; String s = "test"; ListIterator it = a.listIterator(half); for(int i = 0; i < size * 10; i++) it.add(s); } }, new Tester("remove", 5000) { void test(List a) { ListIterator it = a.listIterator(3); while(it.hasNext()) { it.next(); it.remove(); } } }, }; public static void test(List a) { // A trick to print out the class name: System.out.println("Testing " + a.getClass().getName()); for(int i = 0; i < tests.length; i++) { Collection1.fill(a, tests[i].size); System.out.print(tests[i].name); long t1 = System.currentTimeMillis(); tests[i].test(a); long t2 = System.currentTimeMillis(); System.out.println(": " + (t2 - t1)); } } public static void main(String[] args) { test(new ArrayList()); test(new LinkedList()); } } ///:~
内部类Tester是一个抽象类,用于为特定的测试供应一个底子类.它包含了一个要在测试开始时打印的字串、一个用于计算测试次数或元素数目的size参数、用于初始化字段的一个构建器以及一个抽象办法test().test()做的是最实际的测试工作.各种范例的测试都集合到一个地方:tests数组.我们用担当于Tester的差别匿名内部类来初始化该数组.为增添或删除一个测试项目,只需在数组里简单地增添或移去一个内部类定义便可,其他全部工作都是自动举行的.
首先用元素填充传送给test()的List,然后对tests数组中的测试计时.由于测试用机械的差别,后果当然也会有所辨别.这个程序的目标是揭暴露差别调集范例的相对性能对比.下面是某一次运行得到的后果:
范例 获得 反复 插入 删除
ArrayList 110 270 1920 4780
LinkedList 1870 7580 170 110
可以看出,在ArrayList中举行随机拜候(即get())以及循环反复是最划得来的;但关于LinkedList倒是一个不小的开销.但另一方面,在列表中部举行插入和删除操作关于LinkedList来说却比ArrayList划算得多.我们最好的做法大概是先挑选一个ArrayList作为自己的默许起点.今后若发现由于大量的插入和删除造成了性能的降低,再考虑换成LinkedList不迟.
2. 决意利用何种Set
可在ArraySet以及HashSet间作出挑选,具体取决于Set的大小(假如需求从一个Set中得到一个次序列表,请用TreeSet;注释⑧).下面这个测试程序将有助于大家作出这方面的决意:
//: SetPerformance.java package c08.newcollections; import java.util.*; public class SetPerformance { private static final int REPS = 200; private abstract static class Tester { String name; Tester(String name) { this.name = name; } abstract void test(Set s, int size); } private static Tester[] tests = { new Tester("add") { void test(Set s, int size) { for(int i = 0; i < REPS; i++) { s.clear(); Collection1.fill(s, size); } } }, new Tester("contains") { void test(Set s, int size) { for(int i = 0; i < REPS; i++) for(int j = 0; j < size; j++) s.contains(Integer.toString(j)); } }, new Tester("iteration") { void test(Set s, int size) { for(int i = 0; i < REPS * 10; i++) { Iterator it = s.iterator(); while(it.hasNext()) it.next(); } } }, }; public static void test(Set s, int size) { // A trick to print out the class name: System.out.println("Testing " + s.getClass().getName() + " size " + size); Collection1.fill(s, size); for(int i = 0; i < tests.length; i++) { System.out.print(tests[i].name); long t1 = System.currentTimeMillis(); tests[i].test(s, size); long t2 = System.currentTimeMillis(); System.out.println(": " + ((double)(t2 - t1)/(double)size)); } } public static void main(String[] args) { // Small: test(new TreeSet(), 10); test(new HashSet(), 10); // Medium: test(new TreeSet(), 100); test(new HashSet(), 100); // Large: test(new HashSet(), 1000); test(new TreeSet(), 1000); } } ///:~
⑧:TreeSet在本书写作时还没有成为一个正式的特点,但在这个例子中可以很轻松地为其增添一个测试.
最后对ArraySet的测试只有500个元素,而不是1000个,因为它太慢了.
范例 测试大小 增添 包含 反复
Type |
Test size |
Add |
Contains |
Iteration |
10 |
22.0 |
11.0 |
16.0 |
|
TreeSet |
100 |
22.5 |
13.2 |
12.1 |
1000 |
31.1 |
18.7 |
11.8 |
|
10 |
5.0 |
6.0 |
27.0 |
|
HashSet |
100 |
6.6 |
6.6 |
10.9 |
1000 |
7.4 |
6.6 |
9.5 |
举行add()以及contains()操作时,HashSet明显要比ArraySet超卓得多,并且性能明显与元素的多寡关系不大.普通编写程序的时刻,几近永久用不着利用ArraySet.
3. 决意利用何种Map
挑选差别的Map实施筹划时,注意Map的大小关于性能的影响是最大的,下面这个测试程序清楚地阐示了这一点:
//: MapPerformance.java // Demonstrates performance differences in Maps package c08.newcollections; import java.util.*; public class MapPerformance { private static final int REPS = 200; public static Map fill(Map m, int size) { for(int i = 0; i < size; i++) { String x = Integer.toString(i); m.put(x, x); } return m; } private abstract static class Tester { String name; Tester(String name) { this.name = name; } abstract void test(Map m, int size); } private static Tester[] tests = { new Tester("put") { void test(Map m, int size) { for(int i = 0; i < REPS; i++) { m.clear(); fill(m, size); } } }, new Tester("get") { void test(Map m, int size) { for(int i = 0; i < REPS; i++) for(int j = 0; j < size; j++) m.get(Integer.toString(j)); } }, new Tester("iteration") { void test(Map m, int size) { for(int i = 0; i < REPS * 10; i++) { Iterator it = m.entries().iterator(); while(it.hasNext()) it.next(); } } }, }; public static void test(Map m, int size) { // A trick to print out the class name: System.out.println("Testing " + m.getClass().getName() + " size " + size); fill(m, size); for(int i = 0; i < tests.length; i++) { System.out.print(tests[i].name); long t1 = System.currentTimeMillis(); tests[i].test(m, size); long t2 = System.currentTimeMillis(); System.out.println(": " + ((double)(t2 - t1)/(double)size)); } } public static void main(String[] args) { // Small: test(new Hashtable(), 10); test(new HashMap(), 10); test(new TreeMap(), 10); // Medium: test(new Hashtable(), 100); test(new HashMap(), 100); test(new TreeMap(), 100); // Large: test(new HashMap(), 1000); test(new Hashtable(), 1000); test(new TreeMap(), 1000); } } ///:~
由于Map的大小是最严重的问题,所以程序的计时测试按Map的大小(或容量)来分割时间,以便得到令人信服的测试后果.下面列出一系列后果(在你的机械上大概差别):
范例 测试大小 置入 取出 反复
Type |
Test size |
Put |
Get |
Iteration |
---|---|---|---|---|
|
10 |
11.0 |
5.0 |
44.0 |
Hashtable |
100 |
7.7 |
7.7 |
16.5 |
1000 |
8.0 |
8.0 |
14.4 |
|
|
10 |
16.0 |
11.0 |
22.0 |
TreeMap |
100 |
25.8 |
15.4 |
13.2 |
1000 |
33.8 |
20.9 |
13.6 |
|
|
10 |
11.0 |
6.0 |
33.0 |
HashMap |
100 |
8.2 |
7.7 |
13.7 |
1000 |
8.0 |
7.8 |
11.9 |
即便大小为10,ArrayMap的性能也要比HashMap差——除反复循环时以外.而在利用Map时,反复的作用普通并不重要(get()普通是我们时间花得最多的地方).TreeMap供应了超卓的put()以及反复时间,但get()的性能并不佳.但是,我们为什么仍旧需求利用TreeMap呢?这样一来,我们可以不把它作为Map利用,而作为成立次序列表的一种途径.树的本质在于它老是次序布列的,没必要分外举行排序(它的排序方法即刻就要讲到).一旦填充了一个TreeMap,便可以调用keySet()来得到键的一个Set“气象”.然后用toArray()产生包含了那些键的一个数组.随后,可用static办法Array.binarySearch()快速查找排好序的数组中的内容.当然,大概只有在HashMap的行为不可承受的时刻,才需求采取这种做法.因为HashMap的计划目标就是举行快速的检索操作.最后,当我们利用Map时,主要的挑选应当是HashMap.只有在极少数情形下才需求考虑其他办法.
此外,在上面那张表里,有另一本性能问题没有反映出来.下述程序用于测试差别范例Map的成立速度:
在写这个程序期间,TreeMap的成立速度比其他两种范例明显快得多(但你应亲身尝试一下,因为据说新版本大概会改进ArrayMap的性能).考虑到这方面的缘由,同时由于前述TreeMap超卓的put()性能,所以假如需求成立大量Map,并且只有在今后才需求触及大量检索操作,那么最佳的战略就是:成立和填充TreeMap;今后检索量增大的时刻,再将重要的TreeMap转换成HashMap——利用HashMap(Map)构建器.一样地,只有在事实证明确切存在性能瓶颈后,才应关心这些方面的问题——先用起来,再按照需求加快速度.//: MapCreation.java // Demonstrates time differences in Map creation package c08.newcollections; import java.util.*; public class MapCreation { public static void main(String[] args) { final long REPS = 100000; long t1 = System.currentTimeMillis(); System.out.print("Hashtable"); for(long i = 0; i < REPS; i++) new Hashtable(); long t2 = System.currentTimeMillis(); System.out.println(": " + (t2 - t1)); t1 = System.currentTimeMillis(); System.out.print("TreeMap"); for(long i = 0; i < REPS; i++) new TreeMap(); t2 = System.currentTimeMillis(); System.out.println(": " + (t2 - t1)); t1 = System.currentTimeMillis(); System.out.print("HashMap"); for(long i = 0; i < REPS; i++) new HashMap(); t2 = System.currentTimeMillis(); System.out.println(": " + (t2 - t1)); } } ///:~
以上是“决意实施策划[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |
- ·上一篇文章:未支持的操作
- ·下一篇文章:操纵Maps
- ·中查找“决意实施策划”更多相关内容
- ·中查找“决意实施策划”更多相关内容
评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论