真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java編程思想對(duì)象的容納實(shí)例詳解

Java提供了容納對(duì)象(或者對(duì)象的句柄)的多種方式,接下來我們具體看看都有哪些方式。

創(chuàng)新互聯(lián)專注于亳州網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供亳州營銷型網(wǎng)站建設(shè),亳州網(wǎng)站制作、亳州網(wǎng)頁設(shè)計(jì)、亳州網(wǎng)站官網(wǎng)定制、重慶小程序開發(fā)服務(wù),打造亳州網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供亳州網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

有兩方面的問題將數(shù)組與其他集合類型區(qū)分開來:效率和類型。對(duì)于Java來說,為保存和訪問一系列對(duì)象(實(shí)際是對(duì)象的句柄)數(shù)組,最有效的方法莫過于數(shù)組。數(shù)組實(shí)際代表一個(gè)簡單的線性序列,它使得元素的訪問速度非???,但我們卻要為這種速度付出代價(jià):創(chuàng)建一個(gè)數(shù)組對(duì)象時(shí),它的大小是固定的,而且不可在那個(gè)數(shù)組對(duì)象的“存在時(shí)間”內(nèi)發(fā)生改變。可創(chuàng)建特定大小的一個(gè)數(shù)組,然后假如用光了存儲(chǔ)空間,就再創(chuàng)建一個(gè)新數(shù)組,將所有句柄從舊數(shù)組移到新數(shù)組。這屬于“矢量”(Vector)類的行為。然而,由于為這種大小的靈活性要付出較大的代價(jià),所以我們認(rèn)為矢量的效率并沒有數(shù)組高。

   在Java中,無論使用的是數(shù)組還是集合,都會(huì)進(jìn)行范圍檢查——若超過邊界,就會(huì)獲得一個(gè)RuntimeException(運(yùn)行期違例)錯(cuò)誤。幾種常見的集合類:Vector(矢量)、Stack(堆棧)以及Hashtable(散列表)。這些類都涉及對(duì)對(duì)象的處理——好象它們沒有特定的類型。換言之,它們將其當(dāng)作Object類型處理(Object類型是Java中所有類的“根”類)。從某個(gè)角度看,這種處理方法是非常合理的:我們僅需構(gòu)建一個(gè)集合,然后任何Java對(duì)象都可以進(jìn)入那個(gè)集合(除基本數(shù)據(jù)類型外——可用Java的基本類型封裝類將其作為常數(shù)置入集合,或者將其封裝到自己的類內(nèi),作為可以變化的值使用)。這再一次反映了數(shù)組優(yōu)于常規(guī)集合:創(chuàng)建一個(gè)數(shù)組時(shí),可令其容納一種特定的類型。

  對(duì)象數(shù)組容納的是句柄,而基本數(shù)據(jù)類型數(shù)組容納的是具體的數(shù)值。

1、集合

   為容納一組對(duì)象,最適宜的選擇應(yīng)當(dāng)是數(shù)組。而且假如容納的是一系列基本數(shù)據(jù)類型,更是必須采用數(shù)組。當(dāng)我們編寫程序時(shí),通常并不能確切地知道最終需要多少個(gè)對(duì)象。有些時(shí)候甚至想用更復(fù)雜的方式來保存對(duì)象。為解決這個(gè)問題,Java提供了四種類型的“集合類”:Vector(矢量)、BitSet(位集)、Stack(堆棧)以及Hashtable(散列表)。與擁有集合功能的其他語言相比,盡管這兒的數(shù)量顯得相當(dāng)少,但仍然能用它們解決數(shù)量驚人的實(shí)際問題。

這些集合類具有形形色色的特征。例如,Stack實(shí)現(xiàn)了一個(gè)LIFO(先入先出)序列,而Hashtable是一種“關(guān)聯(lián)數(shù)組”,允許我們將任何對(duì)象關(guān)聯(lián)起來。除此以外,所有Java集合類都能自動(dòng)改變自身的大小。所以,我們?cè)诰幊虝r(shí)可使用數(shù)量眾多的對(duì)象,同時(shí)不必?fù)?dān)心會(huì)將集合弄得有多大。

  1.1 缺點(diǎn):類型未知

使用Java集合的“缺點(diǎn)”是在將對(duì)象置入一個(gè)集合時(shí)丟失了類型信息。之所以會(huì)發(fā)生這種情況,是由于當(dāng)初編寫集合時(shí),那個(gè)集合的程序員根本不知道用戶到底想把什么類型置入集合。若指示某個(gè)集合只允許特定的類型,會(huì)妨礙它成為一個(gè)“常規(guī)用途”的工具,為用戶帶來麻煩。為解決這個(gè)問題,集合實(shí)際容納的是類型為Object的一些對(duì)象的句柄。這種類型當(dāng)然代表Java中的所有對(duì)象,因?yàn)樗撬蓄惖母?。?dāng)然,也要注意這并不包括基本數(shù)據(jù)類型,因?yàn)樗鼈儾⒉皇菑摹叭魏螙|西”繼承來的。這是一個(gè)很好的方案,只是不適用下述場(chǎng)合:

    (1) 將一個(gè)對(duì)象句柄置入集合時(shí),由于類型信息會(huì)被拋棄,所以任何類型的對(duì)象都可進(jìn)入我們的集合——即便特別指示它只能容納特定類型的對(duì)象。舉個(gè)例子來說,雖然指示它只能容納貓,但事實(shí)上任何人都可以把一條狗扔進(jìn)來。

    (2) 由于類型信息不復(fù)存在,所以集合能肯定的唯一事情就是自己容納的是指向一個(gè)對(duì)象的句柄。正式使用它之前,必須對(duì)其進(jìn)行造型,使其具有正確的類型。

2、Stack

Stack有時(shí)也可以稱為“后入先出”(LIFO)集合。換言之,我們?cè)诙褩@镒詈蟆皦喝搿钡臇|西將是以后第一個(gè)“彈出”的。和其他所有Java集合一樣,我們壓入和彈出的都是“對(duì)象”,所以必須對(duì)自己彈出的東西進(jìn)行“造型”。
一種很少見的做法是拒絕使用Vector作為一個(gè)Stack的基本構(gòu)成元素,而是從Vector里“繼承”一個(gè)Stack。這樣一來,它就擁有了一個(gè)Vector的所有特征及行為,另外加上一些額外的Stack行為。很難判斷出設(shè)計(jì)者到底是明確想這樣做,還是屬于一種固有的設(shè)計(jì)。

下面是一個(gè)簡單的堆棧示例,它能讀入數(shù)組的每一行,同時(shí)將其作為字串壓入堆棧。

import java.util.*;
public class Stacks {
 static String[] months = { 
  "January", "February", "March", "April",
  "May", "June", "July", "August", "September",
  "October", "November", "December" };
 public static void main(String[] args) {
  Stack stk = new Stack();
  for(int i = 0; i < months.length; i++)
   stk.push(months[i] + " ");
  System.out.println("stk = " + stk);
  // Treating a stack as a Vector:
  stk.addElement("The last line");
  System.out.println(
   "element 5 = " + stk.elementAt(5));
  System.out.println("popping elements:");
  while(!stk.empty())
   System.out.println(stk.pop());
 }
}

months數(shù)組的每一行都通過push()繼承進(jìn)入堆棧,稍后用pop()從堆棧的頂部將其取出。要聲明的一點(diǎn)是,Vector操作亦可針對(duì)Stack對(duì)象進(jìn)行。這可能是由繼承的特質(zhì)決定的——Stack“屬于”一種Vector。因此,能對(duì)Vector進(jìn)行的操作亦可針對(duì)Stack進(jìn)行,例如elementAt()方法。

3、Hashtable

 Vector允許我們用一個(gè)數(shù)字從一系列對(duì)象中作出選擇,所以它實(shí)際是將數(shù)字同對(duì)象關(guān)聯(lián)起來了。但假如我們想根據(jù)其他標(biāo)準(zhǔn)選擇一系列對(duì)象呢?堆棧就是這樣的一個(gè)例子:它的選擇標(biāo)準(zhǔn)是“最后壓入堆棧的東西”。這種“從一系列對(duì)象中選擇”的概念亦可叫作一個(gè)“映射”、“字典”或者“關(guān)聯(lián)數(shù)組”。從概念上講,它看起來象一個(gè)Vector,但卻不是通過數(shù)字來查找對(duì)象,而是用另一個(gè)對(duì)象來查找它們!這通常都屬于一個(gè)程序中的重要進(jìn)程。

在Java中,這個(gè)概念具體反映到抽象類Dictionary身上。該類的接口是非常直觀的size()告訴我們其中包含了多少元素;isEmpty()判斷是否包含了元素(是則為true);put(Object key, Object value)添加一個(gè)值(我們希望的東西),并將其同一個(gè)鍵關(guān)聯(lián)起來(想用于搜索它的東西);get(Object key)獲得與某個(gè)鍵對(duì)應(yīng)的值;而remove(Object Key)用于從列表中刪除“鍵-值”對(duì)。還可以使用枚舉技術(shù):keys()產(chǎn)生對(duì)鍵的一個(gè)枚舉(Enumeration);而elements()產(chǎn)生對(duì)所有值的一個(gè)枚舉。這便是一個(gè)Dictionary(字典)的全部。

Dictionary的實(shí)現(xiàn)過程并不麻煩。下面列出一種簡單的方法,它使用了兩個(gè)Vector,一個(gè)用于容納鍵,另一個(gè)用來容納值:

import java.util.*;
public class AssocArray extends Dictionary {
 private Vector keys = new Vector();
 private Vector values = new Vector();
 public int size() { return keys.size(); }
 public boolean isEmpty() {
  return keys.isEmpty();
 }
 public Object put(Object key, Object value) {
  keys.addElement(key);
  values.addElement(value);
  return key;
 }
 public Object get(Object key) {
  int index = keys.indexOf(key);
  // indexOf() Returns -1 if key not found:
  if(index == -1) return null;
  return values.elementAt(index);
 }
 public Object remove(Object key) {
  int index = keys.indexOf(key);
  if(index == -1) return null;
  keys.removeElementAt(index);
  Object returnval = values.elementAt(index);
  values.removeElementAt(index);
  return returnval;
 }
 public Enumeration keys() {
  return keys.elements();
 }
 public Enumeration elements() {
  return values.elements();
 }
 // Test it:
 public static void main(String[] args) {
  AssocArray aa = new AssocArray();
  for(char c = 'a'; c <= 'z'; c++)
   aa.put(String.valueOf(c),
       String.valueOf(c)
       .toUpperCase());
  char[] ca = { 'a', 'e', 'i', 'o', 'u' };
  for(int i = 0; i < ca.length; i++)
   System.out.println("Uppercase: " +
       aa.get(String.valueOf(ca[i])));
 }
}

在對(duì)AssocArray的定義中,我們注意到的第一個(gè)問題是它“擴(kuò)展”了字典。這意味著AssocArray屬于Dictionary的一種類型,所以可對(duì)其發(fā)出與Dictionary一樣的請(qǐng)求。如果想生成自己的Dictionary,而且就在這里進(jìn)行,那么要做的全部事情只是填充位于Dictionary內(nèi)的所有方法(而且必須覆蓋所有方法,因?yàn)樗鼈儭龢?gòu)建器外——都是抽象的)。

Vector key和value通過一個(gè)標(biāo)準(zhǔn)索引編號(hào)鏈接起來。也就是說,如果用“roof”的一個(gè)鍵以及“blue”的一個(gè)值調(diào)用put()——假定我們準(zhǔn)備將一個(gè)房子的各部分與它們的油漆顏色關(guān)聯(lián)起來,而且AssocArray里已有100個(gè)元素,那么“roof”就會(huì)有101個(gè)鍵元素,而“blue”有101個(gè)值元素。而且要注意一下get(),假如我們作為鍵傳遞“roof”,它就會(huì)產(chǎn)生與keys.index.Of()的索引編號(hào),然后用那個(gè)索引編號(hào)生成相關(guān)的值矢量內(nèi)的值。

main()中進(jìn)行的測(cè)試是非常簡單的;它只是將小寫字符轉(zhuǎn)換成大寫字符,這顯然可用更有效的方式進(jìn)行。但它向我們揭示出了AssocArray的強(qiáng)大功能。

標(biāo)準(zhǔn)Java庫只包含Dictionary的一個(gè)變種,名為Hashtable(散列表,注釋③)。Java的散列表具有與AssocArray相同的接口(因?yàn)閮烧叨际菑腄ictionary繼承來的)。但有一個(gè)方面卻反映出了差別:執(zhí)行效率。若仔細(xì)想想必須為一個(gè)get()做的事情,就會(huì)發(fā)現(xiàn)在一個(gè)Vector里搜索鍵的速度要慢得多。但此時(shí)用散列表卻可以加快不少速度。不必用冗長的線性搜索技術(shù)來查找一個(gè)鍵,而是用一個(gè)特殊的值,名為“散列碼”。散列碼可以獲取對(duì)象中的信息,然后將其轉(zhuǎn)換成那個(gè)對(duì)象“相對(duì)唯一”的整數(shù)(int)。所有對(duì)象都有一個(gè)散列碼,而hashCode()是根類Object的一個(gè)方法。Hashtable獲取對(duì)象的hashCode(),然后用它快速查找鍵。這樣可使性能得到大幅度提升(④)。散列表的具體工作原理已超出了本書的范圍(⑤)——大家只需要知道散列表是一種快速的“字典”(Dictionary)即可,而字典是一種非常有用的工具。

③:如計(jì)劃使用RMI(在第15章詳述),應(yīng)注意將遠(yuǎn)程對(duì)象置入散列表時(shí)會(huì)遇到一個(gè)問題(參閱《Core Java》,作者Conrell和Horstmann,Prentice-Hall 1997年出版)

④:如這種速度的提升仍然不能滿足你對(duì)性能的要求,甚至可以編寫自己的散列表例程,從而進(jìn)一步加快表格的檢索過程。這樣做可避免在與Object之間進(jìn)行造型的時(shí)間延誤,也可以避開由Java類庫散列表例程內(nèi)建的同步過程。

⑤:我的知道的最佳參考讀物是《Practical Algorithms for Programmers》,作者為Andrew Binstock和John Rex,Addison-Wesley 1995年出版。

作為應(yīng)用散列表的一個(gè)例子,可考慮用一個(gè)程序來檢驗(yàn)Java的Math.random()方法的隨機(jī)性到底如何。在理想情況下,它應(yīng)該產(chǎn)生一系列完美的隨機(jī)分布數(shù)字。但為了驗(yàn)證這一點(diǎn),我們需要生成數(shù)量眾多的隨機(jī)數(shù)字,然后計(jì)算落在不同范圍內(nèi)的數(shù)字多少。散列表可以極大簡化這一工作,因?yàn)樗軐?duì)象同對(duì)象關(guān)聯(lián)起來(此時(shí)是將Math.random()生成的值同那些值出現(xiàn)的次數(shù)關(guān)聯(lián)起來)。如下所示:

//: Statistics.java
// Simple demonstration of Hashtable
import java.util.*;

class Counter { 
 int i = 1; 
 public String toString() { 
  return Integer.toString(i); 
 }
}
class Statistics {
 public static void main(String[] args) {
  Hashtable ht = new Hashtable();
  for(int i = 0; i < 10000; i++) {
   // Produce a number between 0 and 20:
   Integer r = 
    new Integer((int)(Math.random() * 20));
   if(ht.containsKey(r))
    ((Counter)ht.get(r)).i++;
   else
    ht.put(r, new Counter());
  }
  System.out.println(ht);
 }
}

在main()中,每次產(chǎn)生一個(gè)隨機(jī)數(shù)字,它都會(huì)封裝到一個(gè)Integer對(duì)象里,使句柄能夠隨同散列表一起使用(不可對(duì)一個(gè)集合使用基本數(shù)據(jù)類型,只能使用對(duì)象句柄)。containKey()方法檢查這個(gè)鍵是否已經(jīng)在集合里(也就是說,那個(gè)數(shù)字以前發(fā)現(xiàn)過嗎?)若已在集合里,則get()方法獲得那個(gè)鍵關(guān)聯(lián)的值,此時(shí)是一個(gè)Counter(計(jì)數(shù)器)對(duì)象。計(jì)數(shù)器內(nèi)的值i隨后會(huì)增加1,表明這個(gè)特定的隨機(jī)數(shù)字又出現(xiàn)了一次。

假如鍵以前尚未發(fā)現(xiàn)過,那么方法put()仍然會(huì)在散列表內(nèi)置入一個(gè)新的“鍵-值”對(duì)。在創(chuàng)建之初,Counter會(huì)自己的變量i自動(dòng)初始化為1,它標(biāo)志著該隨機(jī)數(shù)字的第一次出現(xiàn)。

為顯示散列表,只需把它簡單地打印出來即可。Hashtable toString()方法能遍歷所有鍵-值對(duì),并為每一對(duì)都調(diào)用toString()。Integer toString()是事先定義好的,可看到計(jì)數(shù)器使用的toString。一次運(yùn)行的結(jié)果(添加了一些換行)如下:

{19=526, 18=533, 17=460, 16=513, 15=521, 14=495,
 13=512, 12=483, 11=488, 10=487, 9=514, 8=523,
 7=497, 6=487, 5=480, 4=489, 3=509, 2=503, 1=475,
 0=505}

大家或許會(huì)對(duì)Counter類是否必要感到疑惑,它看起來似乎根本沒有封裝類Integer的功能。為什么不用int或Integer呢?事實(shí)上,由于所有集合能容納的僅有對(duì)象句柄,所以根本不可以使用整數(shù)。學(xué)過集合后,封裝類的概念對(duì)大家來說就可能更容易理解了,因?yàn)椴豢梢詫⑷魏位緮?shù)據(jù)類型置入集合里。然而,我們對(duì)Java封裝器能做的唯一事情就是將其初始化成一個(gè)特定的值,然后讀取那個(gè)值。也就是說,一旦封裝器對(duì)象已經(jīng)創(chuàng)建,就沒有辦法改變一個(gè)值。這使得Integer封裝器對(duì)解決我們的問題毫無意義,所以不得不創(chuàng)建一個(gè)新類,用它來滿足自己的要求。

 3.1 創(chuàng)建“關(guān)鍵”類

在前面的例子里,我們用一個(gè)標(biāo)準(zhǔn)庫的類(Integer)作為Hashtable的一個(gè)鍵使用。作為一個(gè)鍵,它能很好地工作,因?yàn)樗呀?jīng)具備正確運(yùn)行的所有條件。但在使用散列表的時(shí)候,一旦我們創(chuàng)建自己的類作為鍵使用,就會(huì)遇到一個(gè)很常見的問題。例如,假設(shè)一套天氣預(yù)報(bào)系統(tǒng)將Groundhog(土拔鼠)對(duì)象匹配成Prediction(預(yù)報(bào))。這看起來非常直觀:我們創(chuàng)建兩個(gè)類,然后將Groundhog作為鍵使用,而將Prediction作為值使用。如下所示:

//: SpringDetector.java
// Looks plausible, but doesn't work right.
import java.util.*;
class Groundhog {
 int ghNumber;
 Groundhog(int n) { ghNumber = n; }
}
class Prediction {
 boolean shadow = Math.random() > 0.5;
 public String toString() {
  if(shadow)
   return "Six more weeks of Winter!";
  else
   return "Early Spring!";
 }
}
public class SpringDetector {
 public static void main(String[] args) {
  Hashtable ht = new Hashtable();
  for(int i = 0; i < 10; i++)
   ht.put(new Groundhog(i), new Prediction());
  System.out.println("ht = " + ht + "\n");
  System.out.println(
   "Looking up prediction for groundhog #3:");
  Groundhog gh = new Groundhog(3);
  if(ht.containsKey(gh))
   System.out.println((Prediction)ht.get(gh));
 }
}

每個(gè)Groundhog都具有一個(gè)標(biāo)識(shí)號(hào)碼,所以赤了在散列表中查找一個(gè)Prediction,只需指示它“告訴我與Groundhog號(hào)碼3相關(guān)的Prediction”。Prediction類包含了一個(gè)布爾值,用Math.random()進(jìn)行初始化,以及一個(gè)toString()為我們解釋結(jié)果。在main()中,用Groundhog以及與它們相關(guān)的Prediction填充一個(gè)散列表。散列表被打印出來,以便我們看到它們確實(shí)已被填充。隨后,用標(biāo)識(shí)號(hào)碼為3的一個(gè)Groundhog查找與Groundhog #3對(duì)應(yīng)的預(yù)報(bào)。

看起來似乎非常簡單,但實(shí)際是不可行的。問題在于Groundhog是從通用的Object根類繼承的(若當(dāng)初未指定基礎(chǔ)類,則所有類最終都是從Object繼承的)。事實(shí)上是用Object的hashCode()方法生成每個(gè)對(duì)象的散列碼,而且默認(rèn)情況下只使用它的對(duì)象的地址。所以,Groundhog(3)的第一個(gè)實(shí)例并不會(huì)產(chǎn)生與Groundhog(3)第二個(gè)實(shí)例相等的散列碼,而我們用第二個(gè)實(shí)例進(jìn)行檢索。

大家或許認(rèn)為此時(shí)要做的全部事情就是正確地覆蓋hashCode()。但這樣做依然行不能,除非再做另一件事情:覆蓋也屬于Object一部分的equals()。當(dāng)散列表試圖判斷我們的鍵是否等于表內(nèi)的某個(gè)鍵時(shí),就會(huì)用到這個(gè)方法。同樣地,默認(rèn)的Object.equals()只是簡單地比較對(duì)象地址,所以一個(gè)Groundhog(3)并不等于另一個(gè)Groundhog(3)。

因此,為了在散列表中將自己的類作為鍵使用,必須同時(shí)覆蓋hashCode()和equals(),就象下面展示的那樣:

//: SpringDetector2.java
// If you create a class that's used as a key in
// a Hashtable, you must override hashCode()
// and equals().
import java.util.*;
class Groundhog2 {
 int ghNumber;
 Groundhog2(int n) { ghNumber = n; }
 public int hashCode() { return ghNumber; }
 public boolean equals(Object o) {
  return (o instanceof Groundhog2)
   && (ghNumber == ((Groundhog2)o).ghNumber);
 }
}
public class SpringDetector2 {
 public static void main(String[] args) {
  Hashtable ht = new Hashtable();
  for(int i = 0; i < 10; i++)
   ht.put(new Groundhog2(i),new Prediction());
  System.out.println("ht = " + ht + "\n");
  System.out.println(
   "Looking up prediction for groundhog #3:");
  Groundhog2 gh = new Groundhog2(3);
  if(ht.containsKey(gh))
   System.out.println((Prediction)ht.get(gh));
 }
}

注意這段代碼使用了來自前一個(gè)例子的Prediction,所以SpringDetector.java必須首先編譯,否則就會(huì)在試圖編譯SpringDetector2.java時(shí)得到一個(gè)編譯期錯(cuò)誤。

Groundhog2.hashCode()將土拔鼠號(hào)碼作為一個(gè)標(biāo)識(shí)符返回(在這個(gè)例子中,程序員需要保證沒有兩個(gè)土拔鼠用同樣的ID號(hào)碼并存)。為了返回一個(gè)獨(dú)一無二的標(biāo)識(shí)符,并不需要hashCode(),equals()方法必須能夠嚴(yán)格判斷兩個(gè)對(duì)象是否相等。
equals()方法要進(jìn)行兩種檢查:檢查對(duì)象是否為null;若不為null,則繼續(xù)檢查是否為Groundhog2的一個(gè)實(shí)例(要用到instanceof關(guān)鍵字)。即使為了繼續(xù)執(zhí)行equals(),它也應(yīng)該是一個(gè)Groundhog2。正如大家看到的那樣,這種比較建立在實(shí)際ghNumber的基礎(chǔ)上。這一次一旦我們運(yùn)行程序,就會(huì)看到它終于產(chǎn)生了正確的輸出(許多Java庫的類都覆蓋了hashcode()和equals()方法,以便與自己提供的內(nèi)容適應(yīng))。
 

  3.2 屬性:Hashtable的一種類型

  在第一個(gè)例子中,我們使用了一個(gè)名為Properties(屬性)的Hashtable類型。在那個(gè)例子中,下述程序行:

Properties p = System.getProperties();
p.list(System.out);

調(diào)用了一個(gè)名為getProperties()的static方法,用于獲得一個(gè)特殊的Properties對(duì)象,對(duì)系統(tǒng)的某些特征進(jìn)行描述。list()屬于Properties的一個(gè)方法,可將內(nèi)容發(fā)給我們選擇的任何流式輸出。也有一個(gè)save()方法,可用它將屬性列表寫入一個(gè)文件,以便日后用load()方法讀取。

盡管Properties類是從Hashtable繼承的,但它也包含了一個(gè)散列表,用于容納“默認(rèn)”屬性的列表。所以假如沒有在主列表里找到一個(gè)屬性,就會(huì)自動(dòng)搜索默認(rèn)屬性。

Properties類亦可在我們的程序中使用(第17章的ClassScanner.java便是一例)。在Java庫的用戶文檔中,往往可以找到更多、更詳細(xì)的說明。

4、 再論枚舉器

我們現(xiàn)在可以開始演示Enumeration(枚舉)的真正威力:將穿越一個(gè)序列的操作與那個(gè)序列的基礎(chǔ)結(jié)構(gòu)分隔開。在下面的例子里,PrintData類用一個(gè)Enumeration在一個(gè)序列中移動(dòng),并為每個(gè)對(duì)象都調(diào)用toString()方法。此時(shí)創(chuàng)建了兩個(gè)不同類型的集合:一個(gè)Vector和一個(gè)Hashtable。并且在它們里面分別填充Mouse和Hamster對(duì)象(本章早些時(shí)候已定義了這些類;注意必須先編譯HamsterMaze.java和WorksAnyway.java,否則下面的程序不能編譯)。由于Enumeration隱藏了基層集合的結(jié)構(gòu),所以PrintData不知道或者不關(guān)心Enumeration來自于什么類型的集合:

//: Enumerators2.java
// Revisiting Enumerations
import java.util.*;
class PrintData {
 static void print(Enumeration e) {
  while(e.hasMoreElements())
   System.out.println(
    e.nextElement().toString());
 }
}
class Enumerators2 {
 public static void main(String[] args) {
  Vector v = new Vector();
  for(int i = 0; i < 5; i++)
   v.addElement(new Mouse(i));
  Hashtable h = new Hashtable();
  for(int i = 0; i < 5; i++)
   h.put(new Integer(i), new Hamster(i));
  System.out.println("Vector");
  PrintData.print(v.elements());
  System.out.println("Hashtable");
  PrintData.print(h.elements());
 }
}

注意PrintData.print()利用了這些集合中的對(duì)象屬于Object類這一事實(shí),所以它調(diào)用了toString()。但在解決自己的實(shí)際問題時(shí),經(jīng)常都要保證自己的Enumeration穿越某種特定類型的集合。例如,可能要求集合中的所有元素都是一個(gè)Shape(幾何形狀),并含有draw()方法。若出現(xiàn)這種情況,必須從Enumeration.nextElement()返回的Object進(jìn)行下溯造型,以便產(chǎn)生一個(gè)Shape。

5、新集合

  新的集合庫考慮到了“容納自己對(duì)象”的問題,并將其分割成兩個(gè)明確的概念:

(1) 集合(Collection):一組單獨(dú)的元素,通常應(yīng)用了某種規(guī)則。在這里,一個(gè)List(列表)必須按特定的順序容納元素,而一個(gè)Set(集)不可包含任何重復(fù)的元素。相反,“包”(Bag)的概念未在新的集合庫中實(shí)現(xiàn),因?yàn)椤傲斜怼币烟峁┝祟愃频墓δ堋?/p>

(2) 映射(Map):一系列“鍵-值”對(duì)(這已在散列表身上得到了充分的體現(xiàn))。從表面看,這似乎應(yīng)該成為一個(gè)“鍵-值”對(duì)的“集合”,但假若試圖按那種方式實(shí)現(xiàn)它,就會(huì)發(fā)現(xiàn)實(shí)現(xiàn)過程相當(dāng)笨拙。這進(jìn)一步證明了應(yīng)該分離成單獨(dú)的概念。另一方面,可以方便地查看Map的某個(gè)部分。只需創(chuàng)建一個(gè)集合,然后用它表示那一部分即可。這樣一來,Map就可以返回自己鍵的一個(gè)Set、一個(gè)包含自己值的List或者包含自己“鍵-值”對(duì)的一個(gè)List。和數(shù)組相似,Map可方便擴(kuò)充到多個(gè)“維”,毋需涉及任何新概念。只需簡單地在一個(gè)Map里包含其他Map(后者又可以包含更多的Map,以此類推)。

在通讀了本章以后,相信大家會(huì)真正理解它實(shí)際只有三個(gè)集合組件:Map,List和Set。而且每個(gè)組件實(shí)際只有兩、三種實(shí)現(xiàn)方式(注釋⑥),而且通常都只有一種特別好的方式。只要看出了這一點(diǎn),集合就不會(huì)再令人生畏。

6、使用Collections

  boolean add(Object) *保證集合內(nèi)包含了自變量。如果它沒有添加自變量,就返回false(假)

boolean addAll(Collection) *添加自變量內(nèi)的所有元素。如果沒有添加元素,則返回true(真)

void clear() *?jiǎng)h除集合內(nèi)的所有元素

boolean contains(Object) 若集合包含自變量,就返回“真”

boolean containsAll(Collection) 若集合包含了自變量內(nèi)的所有元素,就返回“真”

boolean isEmpty() 若集合內(nèi)沒有元素,就返回“真”
Iterator iterator() 返回一個(gè)反復(fù)器,以用它遍歷集合的各元素

boolean remove(Object) *如自變量在集合里,就刪除那個(gè)元素的一個(gè)實(shí)例。如果已進(jìn)行了刪除,就返回“真”

boolean removeAll(Collection) *?jiǎng)h除自變量里的所有元素。如果已進(jìn)行了任何刪除,就返回“真”

boolean retainAll(Collection) *只保留包含在一個(gè)自變量里的元素(一個(gè)理論的“交集”)。如果已進(jìn)行了任何改變,就返回“真”int size() 返回集合內(nèi)的元素?cái)?shù)量

Object[] toArray() 返回包含了集合內(nèi)所有元素的一個(gè)數(shù)組

7、使用Lists

  List(接口) 順序是List最重要的特性;它可保證元素按照規(guī)定的順序排列。List為Collection添加了大量方法,以便我們?cè)贚ist中部插入和刪除元素(只推薦對(duì)LinkedList這樣做)。List也會(huì)生成一個(gè)ListIterator(列表反復(fù)器),利用它可在一個(gè)列表里朝兩個(gè)方向遍歷,同時(shí)插入和刪除位于列表中部的元素(同樣地,只建議對(duì)LinkedList這樣做)

ArrayList* 由一個(gè)數(shù)組后推得到的List。作為一個(gè)常規(guī)用途的對(duì)象容器使用,用于替換原先的Vector。允許我們快速訪問元素,但在從列表中部插入和刪除元素時(shí),速度卻嫌稍慢。一般只應(yīng)該用ListIterator對(duì)一個(gè)ArrayList進(jìn)行向前和向后遍歷,不要用它刪除和插入元素;與LinkedList相比,它的效率要低許多

LinkedList 提供優(yōu)化的順序訪問性能,同時(shí)可以高效率地在列表中部進(jìn)行插入和刪除操作。但在進(jìn)行隨機(jī)訪問時(shí),速度卻相當(dāng)慢,此時(shí)應(yīng)換用ArrayList。也提供了addFirst(),addLast(),getFirst(),getLast(),removeFirst()以及removeLast()(未在任何接口或基礎(chǔ)類中定義),以便將其作為一個(gè)規(guī)格、隊(duì)列以及一個(gè)雙向隊(duì)列使用

8、使用Sets

  Set(接口) 添加到Set的每個(gè)元素都必須是獨(dú)一無二的;否則Set就不會(huì)添加重復(fù)的元素。添加到Set里的對(duì)象必須定義equals(),從而建立對(duì)象的唯一性。Set擁有與Collection完全相同的接口。一個(gè)Set不能保證自己可按任何特定的順序維持自己的元素

HashSet* 用于除非常小的以外的所有Set。對(duì)象也必須定義hashCode()

ArraySet 由一個(gè)數(shù)組后推得到的Set。面向非常小的Set設(shè)計(jì),特別是那些需要頻繁創(chuàng)建和刪除的。對(duì)于小Set,與HashSet相比,ArraySet創(chuàng)建和反復(fù)所需付出的代價(jià)都要小得多。但隨著Set的增大,它的性能也會(huì)大打折扣。不需要HashCode()
TreeSet 由一個(gè)“紅黑樹”后推得到的順序Set。這樣一來,我們就可以從一個(gè)Set里提到一個(gè)順序集合

9、 使用Maps

  Map(接口) 維持“鍵-值”對(duì)應(yīng)關(guān)系(對(duì)),以便通過一個(gè)鍵查找相應(yīng)的值

HashMap* 基于一個(gè)散列表實(shí)現(xiàn)(用它代替Hashtable)。針對(duì)“鍵-值”對(duì)的插入和檢索,這種形式具有最穩(wěn)定的性能。可通過構(gòu)建器對(duì)這一性能進(jìn)行調(diào)整,以便設(shè)置散列表的“能力”和“裝載因子”

ArrayMap 由一個(gè)ArrayList后推得到的Map。對(duì)反復(fù)的順序提供了精確的控制。面向非常小的Map設(shè)計(jì),特別是那些需要經(jīng)常創(chuàng)建和刪除的。對(duì)于非常小的Map,創(chuàng)建和反復(fù)所付出的代價(jià)要比HashMap低得多。但在Map變大以后,性能也會(huì)相應(yīng)地大幅度降低

TreeMap 在一個(gè)“紅-黑”樹的基礎(chǔ)上實(shí)現(xiàn)。查看鍵或者“鍵-值”對(duì)時(shí),它們會(huì)按固定的順序排列(取決于Comparable或Comparator,稍后即會(huì)講到)。TreeMap最大的好處就是我們得到的是已排好序的結(jié)果。TreeMap是含有subMap()方法的唯一一種Map,利用它可以返回樹的一部分

總結(jié)

以上就是本文關(guān)于Java編程思想對(duì)象的容納實(shí)例詳解的全部內(nèi)容,希望能對(duì)大家有所幫助,也希望大家對(duì)本站多多支持!


文章標(biāo)題:Java編程思想對(duì)象的容納實(shí)例詳解
鏈接地址:http://weahome.cn/article/jcohhp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部