当前位置:七道奇文章资讯编程技术Java编程
日期: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的成立速度:
//: 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)构建器.一样地,只有在事实证明确切存在性能瓶颈后,才应关心这些方面的问题——先用起来,再按照需求加快速度.
  以上是“决意实施策划[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
  • 决意实施策划
  • 本文地址: 与您的QQ/BBS好友分享!
    • 好的评价 如果您觉得此文章好,就请您
        0%(0)
    • 差的评价 如果您觉得此文章差,就请您
        0%(0)

    文章评论评论内容只代表网友观点,与本站立场无关!

       评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
    Copyright © 2020-2022 www.xiamiku.com. All Rights Reserved .