决定实施方案

从早些时候的那幅示意图可以看出,实际上只有三个集合组件: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的创建速度:
 

//: 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));
  }
} ///:~

在写这个程序期间,TreeMap的创建速度比其他两种类型明显快得多(但你应亲自尝试一下,因为据说新版本可能会改善ArrayMap的性能)。考虑到这方面的原因,同时由于前述TreeMap出色的put()性能,所以如果需要创建大量Map,而且只有在以后才需要涉及大量检索操作,那么最佳的策略就是:创建和填充TreeMap;以后检索量增大的时候,再将重要的TreeMap转换成HashMap——使用HashMap(Map)构建器。同样地,只有在事实证明确实存在性能瓶颈后,才应关心这些方面的问题——先用起来,再根据需要加快速度。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索int
, 测试
, test
, system
, size
, 一个
, arraywalk和arraymap
, s:iterator遍历
, ArrayMap
ArrayMap源码
设计思维决定方案优劣、实施方案、项目实施方案、精准扶贫实施方案、追赶超越实施方案,以便于您获取更多的相关知识。

时间: 2024-10-14 14:38:30

决定实施方案的相关文章

从零开始学习jQuery (八) 插播:jQuery实施方案

一.摘要 本系列文章将带您进入jQuery的精彩世界,其中有很多作者具体的使用经验和解决方案,即使你会使用jQuery也能在阅读中发现些许秘籍. 本篇文章属于临时插播,用于介绍我在本公司的jQuery实施方案. 二.前言 有了前几章扎实的基础知识我们已经可以在项目中投入使用jQuery了.再继续深入学习jQuery前插播一下我的jQuery实施方案. 每个公司的情况都不同.比如我们公司的页面文件都为用户控件,物理路径和虚拟路径没有绝对的关系,所以无法使用相对路径(否则生产环境中会找不到文件).

Windows系统远程管理实施方案

对于很多的微软系统的管理员来说,都面临着一个以怎样的安全方式管理远程系统的问题!在Unix系统中,这个答案十分简单:使用SSH 协议,这是足够安全和有效的.在SSH方式下,我们不仅能在命令行下管理远程系统,我们也能通过使用隧 道技术(Tunnlling)运行远程X-Window.在传输过程中通过使用强壮的加密算法,以防止传送的数据被未经授权的访问. 令人遗憾的是,如果把远程安全访问应用于微软操作系统就不是一件非常容易的事了.首先,仅仅NT 终端服务器,2000服务器和XP安装有远程管理服务( 终

福州发布《关于运用大数据加强对市场主体服务和监管实施方案》

福州政府网站消息,日前,福州市出台<关于运用大数据加强对市场主体服务和监管实施方案>(以下简称<方案>),<方案>提出,到2017年,建立健全政府对市场主体服务和监管相关的大数据资源的统筹规划.归集整合.共享开放的管理协调机制:到2018年,健全完善大数据应用基础设施,着力拓展.提升重点领域的大数据应用:到2020年,政府服务和监管的精细性.针对性.有效性.预见性显著增强,运用大数据成为提高政府治理能力的重要手段. 根据<方案>了解到,福州市为政府运用大数据

网络营销策划实施方案 如何达到效果的可预测性

中介交易 SEO诊断 淘宝客 云主机 技术大厅 笔者从今年三月份开始每个月都接到客户的网络营销整合咨询,也创作了六篇网络营销策划实施方案,遗憾的是只有一份中标并配合实施,以下是笔者的实践经验之谈,留下QQ100315930希望与相关人士沟通交流进步! 一.网络推广与网络营销的关系 企业网商在做网络营销与策划的时候首先要弄清一组概念即网络推广与网络营销的区分: 什么是网络推广?作为网络营销策划者必须熟知百度百科"网络推广"所介绍的各种网络推广方法并且都实际应用过,对推广方法具体怎么应用都

从顾客接触点开始——CRM实施方案的设计

如果有这样两个CRM(客户关系管理)项目小组同时开始工作,其中一个主要由软件设计工程师组成,他们对软件市场上最新的开发工具了如指掌,懂得如何设计最漂亮最有趣的互联网主页,并在午餐的时候争论的话题是瘦客户端与JAVA程序,毫无疑问,他们都是技术精英:而另一个项目小组的成员则面孔各异,有的人设计过广告,有的人作过销售,有的人有财务专长,他们也许并非个个都精通互联网技术,但是都有着丰富的与客户打交道的经验,这两个小组进行的CRM项目,哪一个更容易成功呢? 答案可能出乎预料.从德勤咨询公司对全世界225

深圳公布创建中国软件名城分解实施方案

加快高新南区软件产业基地建设 李舒瑜 深圳特区报讯 (记者 李舒瑜)我市着力打造中国软件名城,使软件业务收入保持20%以上的快速增长,规模占全国.全省比重分别超过10%和60%.<深圳市创建中国软件名城分解实施方案>昨天通过新一期政府公报公布. 软件业务收入保持20%以上的快速增长 根据<实施方案>,深圳中国软件名城创建试点工作总体目标是建设有气魄.有特色.有贡献的中国软件名城,提升深圳软件和信息服务业综合实力,创造"深圳质量". 一方面,软件业综合实力强大,软

上海市加快推进城市配送物流发展实施方案

上海市政府网站公布了<上海市加快推进城市配送物流发展实施方案>,提出到2015年上海市将初步建成一个接轨国际物流.服务长三角地区城市群的高效.绿色.便捷的城市配送物流服务网络. 据介绍,目前上海城市配送物流的基础设施初具规模.西北综合物流园区已成为快速消费品.医药品等物流配送中心的集聚地,城际中转.城市配送功能逐步增强.同时在上海市外环线附近,一批拥有现代化物流设施的配送中心建成并投入运营.但是规模化.系统化的城市配送物流体系尚未形成,面向社区和商业中心的城市末端配送物流设施不足,城市配送运力

“宽带中国”战略及实施方案》

对实现目标做出了明确规划.如今,2013年还剩不到百日,这项实施方案的进展如何?在宽带中国推进过程中,哪些因素制约了宽带的提速?记者带着这些问题,在北京.上海.广东地区展开了调查.城乡目标完成难度不同家住上海徐家汇的陈先生一直用电信2M宽带,几个月前,陈先生得知可以免费将2M宽带升为10M光纤,他当即选择升级.陈先生说:"同样的价格,也不用交改装费,网速却提高了一大截,谁都会选择升级啊."据了解,免费的"铜退光进"正是电信公司的网络提速政策之一.而网络提速的背后,正

基于blog平台的营销:博客推广实施方案

中介交易 SEO诊断 淘宝客 云主机 技术大厅 根据中国互联网络信息中心(CNNIC)去年7月发布<第22次中国互联网络发展状况统计报告>显示,08年中国网民已经赶超美国,成为第一大国.网络购物人数已经达到7400万,年增长率达到60%.艾瑞咨询联合淘宝发布的<2008年度网购市场发展报告>显示,08年国内网购市场首次突破了千亿元大关.达到1200亿,较上年增长了128.5%. 这份官方资料显示,电子商务购物大潮正以不可阻挡的趋势向我们袭来,而且网民质素也在不断提升,加之淘宝网店近