1. 程式人生 > >java.util包詳解

java.util包詳解

介紹Java的實用工具類庫java.util包。在這個包中,Java提供了一些實用的方法和資料結構。本章介紹Java的實用工具類庫java.util包。在這個包中,Java提供了一些實用的方法和資料結構。例如,Java提供日期(Data)類、日曆(Calendar)類來產生和獲取日期及時間,提供隨機數(Random)類產生各種型別的隨機數,還提供了堆疊(Stack)、向量(Vector) 、位集合(Bitset)以及雜湊表(Hashtable)等類來表示相應的資料結構。
  圖8.1給出了java.util包的基本層次結構圖。下面我們將具體介紹其中幾個重要的類。
           ┌java.util.BitSet
           │java.util.Calendar
           │      └java.util.GregorianCalendar
           │java.util.Date
           │java.util.Dictionary
           │      └java.util.Hashtable
           │             └java.util.Properties
           │java.util.EventObject
           │java.util.ResourceBundle
       ┌普通類┤      ├java.util.ListResourceBundle
       │   │      └java.util.PropertyResourceBundle
       │   │java.util.Local
       │   │java.util.Observable
       │   │java.util.Random
       │   │java.util.StringTokenizer
       │   │java.util.Vector
       │   │      └java.util.Stack
  Java.util┤   └java.util.TimeZone
       │          └java.util.SimpleTimeZone
       │   ┌java.util.Enumeration
       ├接 口┤java.util.EventListener
       │   └java.util.Observer
       │   ┌java.util.EmptyStackException
       └異常類┤java.util.MissingResourceException
           │java.util.NoSuchElementException
           └java.util.TooManyListenersException
       圖8.1 java.util包的基本層次結構


8.2 日期類Date

  Java在日期類中封裝了有關日期和時間的資訊,使用者可以通過呼叫相應的方法來獲取系統時間或設定日期和時間。Date類中有很多方法在JDK1.0公佈後已經過時了,在8.3中我們將介紹JDK1.0中新加的用於替代Date的功能的其它類。
  在日期類中共定義了六種建構函式。
  (1)public Date()
  建立的日期類物件的日期時間被設定成建立時刻相對應的日期時間。
  例 Date today=new Date();//today被設定成建立時刻相對應的日期時間。
  (2)public Date (long date)
  long 型的引數date可以通過呼叫Date類中的static方法parse(String s)來獲得。
  例 long l=Date.parse("Mon 6 Jan 1997 13:3:00");
    Date day=new Date(l);
  //day中時間為1997年 1月6號星期一,13:3:00。
  (3)public Date(String s)
  按字串s產生一日期物件。s的格式與方法parse中字串引數的模式相同。
  例 Date day=new Date("Mon 6 Jan 1997 13:3:00");
  //day 中時間為1997年1月6號星期一,13:3:00.
  (4)public Date(int year,int month,int date)
  (5)public Date(int year,int month,int date,int hrs,int min)
  (6)public Date(int year,int month,int date,int hrs,int min,int sec)
  按給定的引數建立一日期物件。
  引數說明:
  year的值為:需設定的年份-1900。例如需設定的年份是1997則year的值應為97,即1997-1900的結果。所以Date中可設定的年份最小為1900;
  month的值域為0~11,0代表1月,11表代表12月;
  date的值域在1~31之間;
  hrs的值域在0~23之間。從午夜到次日凌晨1點間hrs=0,從中午到下午1點間hrs=12;
  min和sec的值域在0~59之間。
  例 Date day=new Date(11,3,4);
  //day中的時間為:04-Apr-11 12:00:00 AM
另外,還可以給出不正確的引數。
  例 設定時間為1910年2月30日,它將被解釋成3月2日。
  Date day=new Date(10,1,30,10,12,34);
  System.out.println("Day's date is:"+day);
  //列印結果為:Day's date is:Web Mar 02 10:13:34 GMT+08:00 1910
  下面我們給出一些Date類中常用方法。
  (1)public static long UTC(int year,int month,int date,int hrs. int min,int sec)
  該方法將利用給定引數計算UTC值。UTC是一種計時體制,與GMT(格林威治時間)的計時體系略有差別。UTC計時體系是基於原子時鐘的,而GTMT計時體系是基於天文學觀測的。計算中使用的一般為GMT計時體系。
  (2)public static long parse(String s)
  該方法將字串s轉換成一個long型的日期。在介紹構造方法Date(long date)時曾使用過這個方法。
  字串s有一定的格式,一般為:
  (星期 日 年 時間GMT+時區)
  若不註明時區,則為本地時區。
  (3)public void setMonth(int month)
  (4)public int getMonth()
  這兩個方法分別為設定和獲取月份值。
  獲取的月份的值域為0~11,0代表1月,11代表12月。
  (5)public String toString()
  (6)public String toLocalString()
  (7)public String toGMTString()
  將給定日期物件轉換成不同格式的字串。它們對應的具體的格式可參看例子8.1。
  (8)public int getTimezoneOffset()
  該方法用於獲取日期物件的時區偏移量。
  例8.1中對上面介紹的Date類中的基本方法進行了具體的應用,並列印了相應的結果。由於使用了一些過時的方法,所以編譯時會有警告資訊。另外,由於本例中的時間表示與平臺有關,不同的JDK版本對此處理不完全相同,因此不同版本的JDK執行本例的結果可能有細微差異。
  例8.1 DateApp.java
  import java.lang.System;
  import java.util.Date;
  public class DateApp{
   public static void main(String args[]){
    Date today=new Date();
    //today中的日期被設成建立時刻的日期和時間,假設建立時刻為1997年3月
    //23日17時51分54秒。
    System.out.println("Today's date is "+today);
    //返回一般的時間表示法,本例中結果為
    //Today's date is Fri May 23 17:51:54 1997
    System.out.println("Today's date(Internet GMT)is:"
     +today.toGMTString());
    //返回結果為GMT時間表示法,本例中結果為
    //Today's date(Internet GMT)is: 23 May 1997 09:51:54:GMT
    System.out.println("Today's date(Locale) is:"
     +today.toLocaleString());
    //返回結果為本地習慣的時間表示法,結果為
    //Today's date(Locale)is:05/23/97 17:51:54
    System.out.println("Today's year is: "+today.getYear());
    System.out.println("Today's month is: "+(today.getMonth()+1));
    System.out.println("Today's date is: "+today.getDate());
    //呼叫Date類中方法,獲取年月日的值。
    //下面呼叫了不同的構造方法來建立Date類的物件。
    Date day1=new Date(100,1,23,10,12,34);
    System.out.println("Day1's date is: "+day1);
    Date day2=new Date("Sat 12 Aug 1996 13:3:00");
    System.out.println("Day2's date is: "+day2);
    long l= Date.parse("Sat 5 Aug 1996 13:3:00 GMT+0800");
    Date day3= new Date(l);
    System.out.println("Day3's date(GMT)is: "+day3.toGMTString());
    System.out.println("Day3's date(Locale)is: "
     +day3.toLocaleString());
    System.out.println("Day3's time zone offset is:"
     +day3.getTimezoneOffset());
   }
  }

  執行結果(JDK1.3版,與原文不同,原文是JDK1.0版):
  E:/java/tutorial/java01>java DateApp
  Today's date is Thu Dec 27 17:58:16 CST 2001
  Today's date(Internet GMT)is:27 Dec 2001 09:58:16 GMT
  Today's date(Locale) is:2001-12-27 17:58:16
  Today's year is: 101
  Today's month is: 12
  Today's date is: 27
  Day1's date is: Wed Feb 23 10:12:34 CST 2000
  Day2's date is: Fri Aug 12 13:03:00 CST 1996
  Day3's date(GMT)is: 5 Aug 1996 05:03:00 GMT
  Day3's date(Locale)is: 1996-8-5 13:03:00
  Day3's time zone offset is:-480

  E:/java/tutorial/java01>

8.3 日曆類Calendar

  在早期的JDK版本中,日期(Date)類附有兩大功能:(1)允許用年、月、日、時、分、秒來解釋日期:(2)允許對錶示日期的字串進行格式化和句法分析。在JDK1.1中提供了類Calendar來完成第一種功能,類DateFormat來完成第二項功能。dateFormat是java.text包中的一個類。與Date類有所不同的是,DateFormat類接受用各種語言和不同習慣表示的日期字串。本節將介紹java.util包中的類Calendar及其它新增加的相關的類。
  類Calendar是一個抽象類,它完成日期(Date)類和普通日期表示法(即用一組整型域如YEAR,MONTH,DAY,HOUR表示日期)之間的轉換。
  由於所使用的規則不同,不同的日曆系統對同一個日期的解釋有所不同。在JDK1.1中提供了Calendar類一個子類GregorianCalendar??它實現了世界上普遍使用的公曆系統。當然使用者也可以通過繼承Calendar類,並增加所需規則,以實現不同的日曆系統。
  第GregorianCalendar繼承了Calendar類。本節將在介紹類GregorianCalendar的同時順帶介紹Calendar類中的相關方法。
  類GregorianCalendar提供了七種建構函式:
  (1)public GregorianCalendar()
  建立的物件中的相關值被設定成指定時區,預設地點的當前時間,即程式執行時所處的時區、地點的當前時間。
  (2)public GregorianCalendar(TimeZone zone)
  建立的物件中的相關值被設定成指定時區zone,預設地點的當前時間。
  (3)public GregorianCalendar(Locale aLocale)
  建立的物件中的相關值被設定成預設時區,指定地點aLocale的當前時間。
  (4)public GregorianCalendar(TimeZone zone,Local aLocale)
  建立的物件中的相關值被設定成指定時區,指定地點的當前時間。
  上面使用到的類TimeZone的性質如下:
  TimeZone是java.util包中的一個類,其中封裝了有關時區的資訊。每一個時區對應一組ID。類TimeZone提供了一些方法完成時區與對應ID兩者之間的轉換。
  (Ⅰ)已知某個特定的ID,可以呼叫方法
  public static synchronized TimeZone getTimeZone(String ID)
來獲取對應的時區物件。
  例 太平洋時區的ID為PST,用下面的方法可獲取對應於太平洋時區的時區物件:
  TimeZone tz=TimeZone.getTimeZone("PST");
  呼叫方法getDefault()可以獲取主機所處時區的物件。
  TimeZone tz=TimeZone.getDefault();
  (Ⅱ)呼叫以下方法可以獲取時區的ID
  ■public static synchronized String[] getavailableIDs(int rawOffset)
  根據給定時區偏移值獲取ID陣列。同一時區的不同地區的ID可能不同,這是由於不同地區對是否實施夏時制意見不統一而造成的。
  例String s[]=TimeZone.getAvailableIDs(-7*60*60*1000);
  列印s,結果為s[0]=PNT,s[1]=MST
  ■public static synchronized String[] getAvailableIDs()
  獲取提供的所有支援的ID。
  ■public String getID()
  獲取特定時區物件的ID。
  例 TimeZone tz=TimeZone.getDefault();
  String s=tz.getID();
  列印s,結果為s=CTT。
  上面使用類的物件代表了一個特定的地理、政治或文化區域。Locale只是一種機制,它用來標識一類物件,Local本身並不包含此類物件。
  要獲取一個Locale的物件有兩種方法:
  (Ⅰ)呼叫Locale類的構造方法
  Locale(String language,String country)
  Locale(String language,String country,String variant)
  引數說明:language??在ISO-639中定義的程式碼,由兩個小寫字母組成。
       country??在ISO-3166中定義的程式碼,由兩個大寫字母組成。
       variant??售貨商以及特定瀏覽器的程式碼,例如使用WIN代表Windows。
  (Ⅱ)呼叫Locale類中定義的常量
  Local類提供了大量的常量供使用者建立Locale物件。
  例 Locale.CHINA
    為中國建立一個Locale的物件。
  類TimeZone和類Locale中的其它方法,讀者可查閱API。
  (5)public GregorianCalendar(int year,int month,int date)
  (6)public GregorianCalendar(int year,int month,int date,int hour,int minute)
  (7)public GregorianCalendar(int year,int month,int date,int hour,int minute,int second)
  用給定的日期和時間建立一個GregorianCalendar的物件。
  引數說明:
  year-設定日曆物件的變數YEAR;month-設定日曆物件的變數MONTH;
  date-設定日曆物件的變數DATE;hour-設定日曆物件的變數HOUR_OF_DAY;
  minute-設定日曆物件的變數MINUTE;second-設定日曆物件的變數SECOND。
  與Date類中不同的是year的值沒有1900這個下限,而且year的值代表實際的年份。month的含義與Date類相同,0代表1月,11代表12月。
  例 GregorianCalendar cal=new GregorianCalendar(1991,2,4)
  cal的日期為1991年3月4號。
  除了與Date中類似的方法外,Calendar類還提供了有關方法對日曆進行滾動計算和數學計算。計算規則由給定的日曆系統決定。進行日期計算時,有時會遇到資訊不足或資訊不實等特殊情況。Calendar採取了相應的方法解決這些問題。當資訊不足時將採用預設設定,在GregorianCalendar類中預設設定一般為YEAR=1970,MONTH=JANUARY,DATE=1。
  當資訊不實時,Calendar將按下面的次序優先選擇相應的Calendar的變數組合,並將其它有衝突的資訊丟棄。
  MONTH+DAY_OF_MONTH
  MONTH+WEEK_OF_MONTH+DAY_OF_WEEK
  MONTH+DAY_OF_WEEK_OF_MONTH+DAY_OF_WEEK
  DAY_OF+YEAR
  DAY_OF_WEEK_WEEK_OF_YEAR
  HOUR_OF_DAY

8.4 隨機數類Random

  Java實用工具類庫中的類java.util.Random提供了產生各種型別隨機數的方法。它可以產生int、long、float、double以及Goussian等型別的隨機數。這也是它與java.lang.Math中的方法Random()最大的不同之處,後者只產生double型的隨機數。
  類Random中的方法十分簡單,它只有兩個構造方法和六個普通方法。
  構造方法:
  (1)public Random()
  (2)public Random(long seed)
  Java產生隨機數需要有一個基值seed,在第一種方法中基值預設,則將系統時間作為seed。
  普通方法:
  (1)public synonronized void setSeed(long seed)
  該方法是設定基值seed。
  (2)public int nextInt()
  該方法是產生一個整型隨機數。
  (3)public long nextLong()
  該方法是產生一個long型隨機數。
  (4)public float nextFloat()
  該方法是產生一個Float型隨機數。
  (5)public double nextDouble()
  該方法是產生一個Double型隨機數。
  (6)public synchronized double nextGoussian()
  該方法是產生一個double型的Goussian隨機數。
  例8.2 RandomApp.java。
  //import java.lang.*;
  import java.util.Random;

  public class RandomApp{
   public static void main(String args[]){
    Random ran1=new Random();
    Random ran2=new Random(12345);
    //建立了兩個類Random的物件。
    System.out.println("The 1st set of random numbers:");
    System.out.println("/t Integer:"+ran1.nextInt());
    System.out.println("/t Long:"+ran1.nextLong());
    System.out.println("/t Float:"+ran1.nextFloat());
    System.out.println("/t Double:"+ran1.nextDouble());
    System.out.println("/t Gaussian:"+ran1.nextGaussian());
    //產生各種型別的隨機數
    System.out.print("The 2nd set of random numbers:");
    for(int i=0;i<5;i++){
     System.out.println(ran2.nextInt()+" ");
     if(i==2) System.out.println();
     //產生同種型別的不同的隨機數。
     System.out.println();//原文如此
    }
   }
  }

  執行結果:
  E:/java01>java RandomApp
  The 1st set of random numbers:
    Integer:-173899656
    Long:8056223819738127077
    Float:0.6293638
    Double:0.7888394520265607
    Gaussian:0.5015701094568733
  The 2nd set of random numbers:1553932502
  -2090749135
  -287790814
  -355989640
  -716867186
  E:/java01>

8.5 向量類Vector

  Java.util.Vector提供了向量(Vector)類以實現類似動態陣列的功能。在Java語言中。正如在一開始就提到過,是沒有指標概念的,但如果能正確靈活地使用指標又確實可以大大提高程式的質量,比如在C、C++中所謂"動態陣列"一般都由指標來實現。為了彌補這點缺陷,Java提供了豐富的類庫來方便程式設計者使用,Vector類便是其中之一。事實上,靈活使用陣列也可完成向量類的功能,但向量類中提供的大量方法大大方便了使用者的使用。
  建立了一個向量類的物件後,可以往其中隨意地插入不同的類的物件,既不需顧及型別也不需預先選定向量的容量,並可方便地進行查詢。對於預先不知或不願預先定義陣列大小,並需頻繁進行查詢、插入和刪除工作的情況,可以考慮使用向量類。
  向量類提供了三種構造方法:
  public vector()
  public vector(int initialcapacity,int capacityIncrement)
  public vector(int initialcapacity)
  使用第一種方法,系統會自動對向量物件進行管理。若使用後兩種方法,則系統將根據引數initialcapacity設定向量物件的容量(即向量物件可儲存資料的大小),當真正存放的資料個數超過容量時,系統會擴充向量物件的儲存容量。引數capacityIncrement給定了每次擴充的擴充值。當capacityIncrement為0時,則每次擴充一倍。利用這個功能可以優化儲存。
  在Vector類中提供了各種方法方便使用者使用:
  ■插入功能
  (1)public final synchronized void addElement(Object obj)
  將obj插入向量的尾部。obj可以是任何類的物件。對同一個向量物件,可在其中插入不同類的物件。但插入的應是物件而不是數值,所以插入數值時要注意將數值轉換成相應的物件。
  例 要插入一個整數1時,不要直接呼叫v1.addElement(1),正確的方法為:
  Vector v1=new Vector();
  Integer integer1=new Integer(1);
  v1.addElement(integer1);
  (2)public final synchronized void setElementAt(object obj,int index)
  將index處的物件設成obj,原來的物件將被覆蓋。
  (3)public final synchronized void insertElementAt(Object obj,int index)
  在index指定的位置插入obj,原來物件以及此後的物件依次往後順延。
  ■刪除功能
  (1)public final synchronized void removeElement(Object obj)
  從向量中刪除obj。若有多個存在,則從向量頭開始試,刪除找到的第一個與obj相同的向量成員。
  (2)public final synchronized void removeAllElement()
  刪除向量中所有的物件。
  (3)public final synchronized void removeElementlAt(int index)
  刪除index所指的地方的物件。
  ■查詢搜尋功能
  (1)public final int indexOf(Object obj)
  從向量頭開始搜尋obj ,返回所遇到的第一個obj對應的下標,若不存在此obj,返回-1。
  (2)public final synchronized int indexOf(Object obj,int index)
  從index所表示的下標處開始搜尋obj。
  (3)public final int lastIndexOf(Object obj)
  從向量尾部開始逆向搜尋obj。
  (4)public final synchronized int lastIndexOf(Object obj,int index)
  從index所表示的下標處由尾至頭逆向搜尋obj。
  (5)public final synchronized Object firstElement()
  獲取向量物件中的首個obj。
  (6)public final synchronized Object lastelement()
  獲取向量物件中的最後一個obj。
  瞭解了向量的最基本的方法後,我們來看一下例8.3VectorApp.java。
  例8.3 VectorApp.java。
  import java.util.Vector;
  import java.lang.*;//這一句不應該要,但原文如此
  import java.util.Enumeration;
  public class VectorApp{
   public static void main(String[] args){
    Vector v1=new Vector();
    Integer integer1=new Integer(1);
    v1.addElement("one");
    //加入的為字串物件
    v1.addElement(integer1);
    v1.addElement(integer1);
    //加入的為Integer的物件
    v1.addElement("two");
    v1.addElement(new Integer(2));
    v1.addElement(integer1);
    v1.addElement(integer1);
    System.out.println("The vector v1 is:/n/t"+v1);
    //將v1轉換成字串並列印
    v1.insertElementAt("three",2);
    v1.insertElementAt(new Float(3.9),3);
    System.out.println("The vector v1(used method insertElementAt()) is:/n/t "+v1);
    //往指定位置插入新的物件,指定位置後的物件依次往後順延
    v1.setElementAt("four",2);
    System.out.println("The vector v1(used method setElementAt()) is:/n/t "+v1);
    //將指定位置的物件設定為新的物件
    v1.removeElement(integer1);
    //從向量物件v1中刪除物件integer1由於存在多個integer1所以從頭開始
    //找,刪除找到的第一個integer1
    Enumeration enum=v1.elements();
    System.out.print("The vector v1(used method removeElement())is:");
    while(enum.hasMoreElements())
    System.out.print(enum.nextElement()+" ");
    System.out.println();
    //使用列舉類(Enumeration)的方法來獲取向量物件的每個元素
    System.out.println("The position of object 1(top-to-bottom):"
     + v1.indexOf(integer1));
    System.out.println("The position of object 1(tottom-to-top):"
     +v1.lastIndexOf(integer1));
    //按不同的方向查詢物件integer1所處的位置
    v1.setSize(4);
    System.out.println("The new vector(resized the vector)is:"+v1);
    //重新設定v1的大小,多餘的元素被行棄
   }
  }
  執行結果:
  E:/java01>java VectorApp
  The vector v1 is:
     [one, 1, 1, two, 2, 1, 1]
  The vector v1(used method insertElementAt()) is:
     [one, 1, three, 3.9, 1, two, 2, 1, 1]
  The vector v1(used method setElementAt()) is:
     [one, 1, four, 3.9, 1, two, 2, 1, 1]
  The vector v1(used method removeElement())is:one four 3.9 1 two 2 1 1
  The position of object 1(top-to-bottom):3
  The position of object 1(tottom-to-top):7
  The new vector(resized the vector)is:[one, four, 3.9, 1]
  E:/java01>
  從例8.3執行的結果中可以清楚地瞭解上面各種方法的作用,另外還有幾點需解釋。
  (1)類Vector定義了方法
  public final int size()
  此方法用於獲取向量元素的個數。它的返回值是向是中實際存在的元素個數,而非向量容量。可以呼叫方法capactly()來獲取容量值。
  方法:
  public final synchronized void setsize(int newsize)
  此方法用來定義向量大小。若向量物件現有成員個數已超過了newsize的值,則超過部分的多餘元素會丟失。
  (2)程式中定義了Enumeration類的一個物件
  Enumeration是java.util中的一個介面類,在Enumeration中封裝了有關列舉資料集合的方法。
  在Enumeration中提供了方法hawMoreElement()來判斷集合中是束還有其它元素和方法nextElement()來獲取下一個元素。利用這兩個方法可以依次獲得集合中元素。
  Vector中提供方法:
  public final synchronized Enumeration elements()
  此方法將向量物件對應到一個列舉型別。java.util包中的其它類中也大都有這類方法,以便於使用者獲取對應的列舉型別。

8.6 棧類Stack

  Stack類是Vector類的子類。它向用戶提供了堆疊這種高階的資料結構。棧的基本特性就是先進後出。即先放入棧中的元素將後被推出。Stack類中提供了相應方法完成棧的有關操作。
  基本方法:
  public Object push(Object Hem)
  將Hem壓入棧中,Hem可以是任何類的物件。
  public Object pop()
  彈出一個物件。
  public Object peek()
  返回棧頂元素,但不彈出此元素。
  public int search(Object obj)
  搜尋物件obj,返回它所處的位置。
  public boolean empty()
  判別棧是否為空。
  例8.4 StackApp.java使用了上面的各種方法。
  例8.4 StackApp.java。
  import java.lang.*;
  import java.util.*;
  public class StackApp{
   public static void main(String args[]){
    Stack sta=new Stack();
    sta.push("Apple");
    sta.push("banana");
    sta.push("Cherry");
    //壓入的為字串物件
    sta.push(new Integer(2));
    //壓入的為Integer的物件,值為2
    sta.push(new Float(3.5));
    //壓入的為Float的物件,值為3.5
    System.out.println("The stack is,"+sta);
    //對應棧sta
    System.out.println("The top of stack is:"+sta.peek());
    //對應棧頂元素,但不將此元素彈出
    System.out.println("The position of object Cherry is:"
    +sta.search("cherry"));
    //列印物件Cherry所處的位置
    System.out.print("Pop the element of the stack:");
    while(!sta.empty())
    System.out.print(sta.pop()+" ");
    System.out.println();
    //將棧中的元素依次彈出並列印。與第一次列印的sta的結果比較,可看出棧
    //先進後出的特點
   }
  }
  執行結果(略)


8.7 雜湊表類Hashtable

  雜湊表是一種重要的儲存方式,也是一種常見的檢索方法。其基本思想是將關係碼的值作為自變數,通過一定的函式關係計算出對應的函式值,把這個數值解釋為結點的儲存地址,將結點存入計算得到儲存地址所對應的儲存單元。檢索時採用檢索關鍵碼的方法。現在雜湊表有一套完整的演算法來進行插入、刪除和解決衝突。在Java中雜湊表用於儲存物件,實現快速檢索。
  Java.util.Hashtable提供了種方法讓使用者使用雜湊表,而不需要考慮其雜湊表真正如何工作。
  雜湊表類中提供了三種構造方法,分別是:
  public Hashtable()
  public Hashtable(int initialcapacity)
  public Hashtable(int initialCapacity,float loadFactor)
  引數initialCapacity是Hashtable的初始容量,它的值應大於0。loadFactor又稱裝載因子,是一個0.0到0.1之間的float型的浮點數。它是一個百分比,表明了雜湊表何時需要擴充,例如,有一雜湊表,容量為100,而裝載因子為0.9,那麼當雜湊表90%的容量已被使用時,此雜湊表會自動擴充成一個更大的雜湊表。如果使用者不賦這些引數,系統會自動進行處理,而不需要使用者操心。
  Hashtable提供了基本的插入、檢索等方法。
  ■插入
  public synchronized void put(Object key,Object value)
給物件value設定一關鍵字key,並將其加到Hashtable中。若此關鍵字已經存在,則將此關鍵字對應的舊物件更新為新的物件Value。這表明在雜湊表中相同的關鍵字不可能對應不同的物件(從雜湊表的基本思想來看,這也是顯而易見的)。
  ■檢索
  public synchronized Object get(Object key)
  根據給定關鍵字key獲取相對應的物件。
  public synchronized boolean containsKey(Object key)
  判斷雜湊表中是否包含關鍵字key。
  public synchronized boolean contains(Object value)
  判斷value是否是雜湊表中的一個元素。
  ■刪除
  public synchronized object remove(object key)
  從雜湊表中刪除關鍵字key所對應的物件。
  public synchronized void clear()
  清除雜湊表
  另外,Hashtalbe還提供方法獲取相對應的列舉集合:
  public synchronized Enumeration keys()
  返回關鍵字對應的列舉物件。
  public synchronized Enumeration elements()
  返回元素對應的列舉物件。
  例8.5 Hashtable.java給出了使用Hashtable的例子。
  例8.5 Hashtalbe.java。
  //import java.lang.*;
  import java.util.Hashtable;
  import java.util.Enumeration;
  public class HashApp{
   public static void main(String args[]){
    Hashtable hash=new Hashtable(2,(float)0.8);
    //建立了一個雜湊表的物件hash,初始容量為2,裝載因子為0.8

    hash.put("Jiangsu","Nanjing");
    //將字串物件"Jiangsu"給定一關鍵字"Nanjing",並將它加入hash
    hash.put("Beijing","Beijing");
    hash.put("Zhejiang","Hangzhou");

    System.out.println("The hashtable hash1 is: "+hash);
    System.out.println("The size of this hash table is "+hash.size());
    //列印hash的內容和大小

    Enumeration enum1=hash.elements();
    System.out.print("The element of hash is: ");
    while(enum1.hasMoreElements())
     System.out.print(enum1.nextElement()+" ");
    System.out.println();
    //依次列印hash中的內容
    if(hash.containsKey("Jiangsu"))
     System.out.println("The capatial of Jiangsu is "+hash.get("Jiangsu"));
    hash.remove("Beijing");
    //刪除關鍵字Beijing對應物件
    System.out.println("The hashtable hash2 is: "+hash);
    System.out.println("The size of this hash table is "+hash.size());
   }
  }

  執行結果:
  The hashtable hash1 is: {Beijing=Beijing, Zhejiang=Hangzhou, Jiangsu=Nanjing}
  The size of this hash table is 3
  The element of hash is: Beijing Hangzhou Nanjing
  The capatial of Jiangsu is Nanjing
  The hashtable hash2 is: {Zhejiang=Hangzhou, Jiangsu=Nanjing}
  The size of this hash table is 2

  Hashtable是Dictionary(字典)類的子類。在字典類中就把關鍵字對應到資料值。字典類是一個抽象類。在java.util中還有一個類Properties,它是Hashtable的子類。用它可以進行與物件屬性相關的操作。

8.8 位集合類BitSet

  位集合類中封裝了有關一組二進位制資料的操作。
  我們先來看一下例8.6 BitSetApp.java。
  例8.6 BitSetApp.java
  //import java.lang.*;
  import java.util.BitSet;
  public class BitSetApp{
   private static int n=5;
   public static void main(String[] args){
    BitSet set1=new BitSet(n);
    for(int i=0;i<n;i++) set1.set(i);
    //將set1的各位賦1,即各位均為true
    BitSet set2= new BitSet();
    set2=(BitSet)set1.clone();
    //set2為set1的拷貝
    set1.clear(0);
    set2.clear(2);
    //將set1的第0位set2的第2位清零
    System.out.println("The set1 is: "+set1);
    //直接將set1轉換成字串輸出,輸出的內容是set1中值true所處的位置
    //列印結果為The set1 is:{1,2,3,4}
    System.out.println("The hash code of set2 is: "+set2.hashCode());
    //列印set2的hashCode
    printbit("set1",set1);
    printbit("set2",set2);
    //呼叫列印程式printbit(),列印物件中的每一個元素
    //列印set1的結果為The bit set1 is: false true true true true
    set1.and(set2);
    printbit("set1 and set2",set1);
    //完成set1 and set2,並列印結果
    set1.or(set2);
    printbit("set1 or set2",set1);
    //完成set1 or set2,並列印結果
    set1.xor(set2);
    printbit("set1 xor set2",set1);
    //完成set1 xor set2,並列印結果
   }
   //列印BitSet物件中的內容
   public static void printbit(String name,BitSet set){
    System.out.print("The bit "+name+" is: ");
    for(int i=0;i<n;i++)
     System.out.print(set.get(i)+" ");
    System.out.println();
   }
  }

  執行結果:
  The set1 is: {1, 2, 3, 4}
  The hash code of set2 is: 1225
  The bit set1 is: false true true true true
  The bit set2 is: true true false true true
  The bit set1 and set2 is: false true false true true
  The bit set1 or set2 is: true true false true true
  The bit set1 xor set2 is: false false false false false

  程式中使用了BitSet類提供的兩種構造方法:
    public BitSet();
    public BitSet(int n);
  引數n代表所建立的BitSet類的物件的大小。BitSet類的物件的大小在必要時會由系統自動擴充。
  其它方法:
  public void set(int n)
  將BitSet物件的第n位設定成1。
  public void clear(int n)
  將BitSet物件的第n位清零。
  public boolean get(int n)
  讀取位集合物件的第n位的值,它獲取的是一個布林值。當第n位為1時,返回true;第n位為0時,返回false。
  另外,如在程式中所示,當把一BitSet類的物件轉換成字串輸出時,輸出的內容是此物件中true所處的位置。
  在BitSet中提供了一組位操作,分別是:
  public void and(BitSet set)
  public void or(BitSet set)
  public void xor(BitSet set)
利用它們可以完成兩個位集合之間的與、或、異或操作。
  BitSet類中有一方法public int size()來取得位集合的大小,它的返回值與初始化時設定的位集合大小n不一樣,一般為64。

線性表,連結串列,雜湊表是常用的資料結構,在進行Java開發時,JDK已經為我們提供了一系列相應的類來實現基本的資料結構。這些類均在java.util包中。本文試圖通過簡單的描述,向讀者闡述各個類的作用以及如何正確使用這些類。

Collection
List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
Set
Map
├Hashtable
├HashMap
└WeakHashMap

Collection介面
  Collection是最基本的集合介面,一個Collection代表一組Object,即Collection的元素(Elements)。一些Collection允許相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接繼承自Collection的類,Java SDK提供的類都是繼承自Collection的“子介面”如List和Set。
  所有實現Collection介面的類都必須提供兩個標準的建構函式:無引數的建構函式用於建立一個空的Collection,有一個Collection引數的建構函式用於建立一個新的Collection,這個新的Collection與傳入的Collection有相同的元素。後一個建構函式允許使用者複製一個Collection。
  如何遍歷Collection中的每一個元素?不論Collection的實際型別如何,它都支援一個iterator()的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection中每一個元素。典型的用法如下:
    Iterator it = collection.iterator(); // 獲得一個迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一個元素
    }


  由Collection介面派生的兩個介面是List和Set。

List介面
  List是有序的Collection,使用此介面能夠精確的控制每個元素插入的位置。使用者能夠使用索引(元素在List中的位置,類似於陣列下標)來訪問List中的元素,這類似於Java的陣列。
和下面要提到的Set不同,List允許有相同的元素。
  除了具有Collection介面必備的iterator()方法外,List還提供一個listIterator()方法,返回一個ListIterator介面,和標準的Iterator介面相比,ListIterator多了一些add()之類的方法,允許新增,刪除,設定元素,還能向前或向後遍歷。
  實現List介面的常用類有LinkedList,ArrayList,Vector和Stack。

LinkedList類
  LinkedList實現了List介面,允許null元素。此外LinkedList提供額外的get,remove,insert方法在LinkedList的首部或尾部。這些操作使LinkedList可被用作堆疊(stack),佇列(queue)或雙向佇列(deque)。
  注意LinkedList沒有同步方法。如果多個執行緒同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在建立List時構造一個同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

ArrayList類
  ArrayList實現了可變大小的陣列。它允許所有元素,包括null。ArrayList沒有同步。
size,isEmpty,get,set方法執行時間為常數。但是add方法開銷為分攤的常數,新增n個元素需要O(n)的時間。其他的方法執行時間為線性。
  每個ArrayList例項都有一個容量(Capacity),即用於儲存元素的陣列的大小。這個容量可隨著不斷新增新元素而自動增加,但是增長演算法並沒有定義。當需要插入大量元素時,在插入前可以呼叫ensureCapacity方法來增加ArrayList的容量以提高插入效率。
  和LinkedList一樣,ArrayList也是非同步的(unsynchronized)。

Vector類
  Vector非常類似ArrayList,但是Vector是同步的。由Vector建立的Iterator,雖然和ArrayList建立的Iterator是同一介面,但是,因為Vector是同步的,當一個Iterator被建立而且正在被使用,另一個執行緒改變了Vector的狀態(例如,新增或刪除了一些元素),這時呼叫Iterator的方法時將丟擲ConcurrentModificationException,因此必須捕獲該異常。

Stack 類
  Stack繼承自Vector,實現一個後進先出的堆疊。Stack提供5個額外的方法使得Vector得以被當作堆疊使用。基本的push和pop方法,還有peek方法得到棧頂的元素,empty方法測試堆疊是否為空,search方法檢測一個元素在堆疊中的位置。Stack剛建立後是空棧。

Set介面
  Set是一種不包含重複的元素的Collection,即任意的兩個元素e1和e2都有e1.equals(e2)=false,Set最多有一個null元素。
  很明顯,Set的建構函式有一個約束條件,傳入的Collection引數不能包含重複的元素。
  請注意:必須小心操作可變物件(Mutable Object)。如果一個Set中的可變元素改變了自身狀態導致Object.equals(Object)=true將導致一些問題。

Map介面
  請注意,Map沒有繼承Collection介面,Map提供key到value的對映。一個Map中不能包含相同的key,每個key只能對映一個value。Map介面提供3種集合的檢視,Map的內容可以被當作一組key集合,一組value集合,或者一組key-value對映。

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可以節省空間但相應的查詢時間將增大,這會影響像get和put這樣的操作。
使用Hashtable的簡單示例如下,將1,2,3放到Hashtable中,他們的key分別是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));

  要取出一個數,比如2,用相應的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);

  由於作為key的物件將通過計算其雜湊函式來確定與之對應的value的位置,因此任何作為key的物件都必須實現hashCode和equals方法。hashCode和equals方法繼承自根類Object,如果你用自定義的類當作key的話,要相當小心,按照雜湊函式的定義,如果兩個物件相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個物件不同,則它們的hashCode不一定不同,如果兩個不同物件的hashCode相同,這種現象稱為衝突,衝突會導致操作雜湊表的時間開銷增大,所以儘量定義好的hashCode()方法,能加快雜湊表的操作。
  如果相同的物件有不同的hashCode,對雜湊表的操作會出現意想不到的結果(期待的get方法返回null),要避免這種問題,只需要牢記一條:要同時複寫equals方法和hashCode方法,而不要只寫其中一個。
  Hashtable是同步的。

HashMap類
  HashMap和Hashtable類似,不同之處在於HashMap是非同步的,並且允許null,即null value和null key。,但是將HashMap視為Collection時(values()方法可返回Collection),其迭代子操作時間開銷和HashMap的容量成比例。因此,如果迭代操作的效能相當重要的話,不要將HashMap的初始化容量設得過高,或者load factor過低。

WeakHashMap類
  WeakHashMap是一種改進的HashMap,它對key實行“弱引用”,如果一個key不再被外部所引用,那麼該key可以被GC回收。

總結
  如果涉及到堆疊,佇列等操作,應該考慮用List,對於需要快速插入,刪除元素,應該使用LinkedList,如果需要快速隨機訪問元素,應該使用ArrayList。
  如果程式在單執行緒環境中,或者訪問僅僅在一個執行緒中進行,考慮非同步的類,其效率較高,如果多個執行緒可能同時操作一個類,應該使用同步的類。
  要特別注意對雜湊表的操作,作為key的物件要正確複寫equals和hashCode方法。
  儘量返回介面而非實際的型別,如返回List而非ArrayList,這樣如果以後需要將ArrayList換成LinkedList時,客戶端程式碼不用改變。這就是針對抽象程式設計。