日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java基础系列:集合基础(1)

發(fā)布時間:2025/3/20 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java基础系列:集合基础(1) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

數(shù)組和第一類對象

無論使用的數(shù)組屬于什么類型,數(shù)組標識符實際都是指向真實對象的一個句柄。那些對象本身是在內(nèi)存
“堆”里創(chuàng)建的。堆對象既可“隱式”創(chuàng)建(即默認產(chǎn)生),亦可“顯式”創(chuàng)建(即明確指定,用一個 new
表達式)。堆對象的一部分(實際是我們能訪問的唯一字段或方法)是只讀的length(長度)成員,它告訴
我們那個數(shù)組對象里最多能容納多少元素。對于數(shù)組對象,“ []”語法是我們能采用的唯一另類訪問方法。

對象數(shù)組和基本數(shù)據(jù)類型數(shù)組在使用方法上幾乎是完全一致的。唯一的差別在于對象數(shù)組容納的是句柄,而基本數(shù)據(jù)類型數(shù)組容納的是具體的數(shù)值

public class ArraySize {public static void main(String[] args) {// Arrays of objects:Weeble[] a; // Null handleWeeble[] b = new Weeble[5]; // Null handlesWeeble[] c = new Weeble[4];for (int i = 0; i < c.length; i++)c[i] = new Weeble();Weeble[] d = { new Weeble(), new Weeble(), new Weeble() };// Compile error: variable a not initialized:// !System.out.println("a.length=" + a.length);System.out.println("b.length = " + b.length);// The handles inside the array are// automatically initialized to null:for (int i = 0; i < b.length; i++)System.out.println("b[" + i + "]=" + b[i]);System.out.println("c.length = " + c.length);System.out.println("d.length = " + d.length);a = d;System.out.println("a.length = " + a.length);// Java 1.1 initialization syntax:a = new Weeble[] { new Weeble(), new Weeble() };System.out.println("a.length = " + a.length);// Arrays of primitives:int[] e; // Null handleint[] f = new int[5];int[] g = new int[4];for (int i = 0; i < g.length; i++)g[i] = i * i;int[] h = { 11, 47, 93 };// Compile error: variable e not initialized:// !System.out.println("e.length=" + e.length);System.out.println("f.length = " + f.length);// The primitives inside the array are// automatically initialized to zero:for (int i = 0; i < f.length; i++)System.out.println("f[" + i + "]=" + f[i]);System.out.println("g.length = " + g.length);System.out.println("h.length = " + h.length);e = h;System.out.println("e.length = " + e.length);// Java 1.1 initialization syntax:e = new int[] { 1, 2 };System.out.println("e.length = " + e.length);} }

輸出如下:
b.length = 5
b[0]=null
b[1]=null
b[2]=null
b[3]=null
b[4]=null
c.length = 4
d.length = 3
a.length = 3
a.length = 2
f.length = 5
f[0]=0
f[1]=0
f[2]=0
f[3]=0
f[4]=0
g.length = 4
h.length = 3
e.length = 3
e.length = 2

其中,數(shù)組 a 只是初始化成一個 null 句柄。此時,編譯器會禁止我們對這個句柄作任何實際操作,除非已正
確地初始化了它。數(shù)組 b 被初始化成指向由 Weeble 句柄構(gòu)成的一個數(shù)組,但那個數(shù)組里實際并未放置任何
Weeble 對象。然而,我們?nèi)匀豢梢圆樵兡莻€數(shù)組的大小,因為 b 指向的是一個合法對象。

換言之,我們只知道數(shù)組對象的大小或容量,不知其實際容納了多少個元素。

盡管如此,由于數(shù)組對象在創(chuàng)建之初會自動初始化成 null,所以可檢查它是否為 null,判斷一個特定的數(shù)組“空位”是否容納一個對象。類似地,由基本數(shù)據(jù)類型構(gòu)成的數(shù)組會自動初始化成零(針對數(shù)值類型)、 null(字符類型)或者false(布爾類型)

數(shù)組 c 顯示出我們首先創(chuàng)建一個數(shù)組對象,再將 Weeble 對象賦給那個數(shù)組的所有“空位”。數(shù)組 d 揭示出
“集合初始化”語法,從而創(chuàng)建數(shù)組對象(用 new 命令明確進行,類似于數(shù)組 c),然后用 Weeble 對象進行
初始化,全部工作在一條語句里完成。
下面這個表達式:

a = d;

向我們展示了如何取得同一個數(shù)組對象連接的句柄,然后將其賦給另一個數(shù)組對象,向我們展示了如何取得同一個數(shù)組對象連接的句柄,然后將其賦給另一個數(shù)組對象

1.基本數(shù)據(jù)類型集合
集合類只能容納對象句柄。但對一個數(shù)組,卻既可令其直接容納基本類型的數(shù)據(jù),亦可容納指向?qū)ο蟮木?
柄。利用象 Integer、 Double 之類的“ 封裝器”類,可將基本數(shù)據(jù)類型的值置入一個集合里。

無論將基本類型的數(shù)據(jù)置入數(shù)組,還是將其封裝進入位于集合的一個類內(nèi),都涉及到執(zhí)行效率的問題。顯
然,若能創(chuàng)建和訪問一個基本數(shù)據(jù)類型數(shù)組,那么比起訪問一個封裝數(shù)據(jù)的集合,前者的效率會高出許多。

數(shù)組的返回

假定我們現(xiàn)在想寫一個方法,同時不希望它僅僅返回一樣東西,而是想返回一系列東西。此時,象C 和 C++這樣的語言會使問題復雜化,因為我們不能返回一個數(shù)組,只能返回指向數(shù)組的一個指針。這樣就非常麻煩,因為很難控制數(shù)組的“存在時間”,它很容易造成內(nèi)存“漏洞”的出現(xiàn)。

Java 采用的是類似的方法,但我們能“返回一個數(shù)組”。當然,此時返回的實際仍是指向數(shù)組的指針。但在Java 里,我們永遠不必擔心那個數(shù)組的是否可用—— 只要需要,它就會自動存在。而且垃圾收集器會在我們完成后自動將其清除

public class IceCream {static String[] flav = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl","Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream","Mud Pie" };static String[] flavorSet(int n) {// Force it to be positive & within bounds:n = Math.abs(n) % (flav.length + 1);String[] results = new String[n];int[] picks = new int[n];for(int i = 0; i < picks.length; i++)picks[i] = -1;for(int i = 0; i < picks.length; i++) {retry:while(true) {int t =(int)(Math.random() * flav.length);for(int j = 0; j < i; j++)213if(picks[j] == t) continue retry;picks[i] = t;results[i] = flav[t];break;}}return results;}public static void main(String[] args) {for (int i = 0; i < 20; i++) {System.out.println("flavorSet(" + i + ") = ");String[] fl = flavorSet(flav.length);for (int j = 0; j < fl.length; j++)System.out.println("\t" + fl[j]);}} }

flavorSet()方法創(chuàng)建了一個名為 results 的 String 數(shù)組。該數(shù)組的大小為 n—— 具體數(shù)值取決于我們傳遞給方法的自變量。隨后,它從數(shù)組 flav 里隨機挑選一些“香料”( Flavor),并將它們置入 results 里,并最終返回 results。返回數(shù)組與返回其他任何對象沒什么區(qū)別—— 最終返回的都是一個句柄。

另一方面,注意當 flavorSet()隨機挑選香料的時候,它需要保證以前出現(xiàn)過的一次隨機選擇不會再次出現(xiàn)。為達到這個目的,它使用了一個無限 while 循環(huán),不斷地作出隨機選擇,直到發(fā)現(xiàn)未在 picks 數(shù)組里出現(xiàn)過的一個元素為止(當然,也可以進行字串比較,檢查隨機選擇是否在 results 數(shù)組里出現(xiàn)過,但字串比較的效率比較低)。若成功,就添加這個元素,并中斷循環(huán)( break),再查找下一個( i 值會遞增)。但假若 t 是一個已在 picks 里出現(xiàn)過的數(shù)組,就用標簽式的 continue 往回跳兩級,強制選擇一個新 t。 用一個調(diào)試程序可以很清楚地看到這個過程。

集合

為容納一組對象,最適宜的選擇應(yīng)當是數(shù)組。而且假如容納的是一系列基本數(shù)據(jù)類型,更是必須采用數(shù)組。

缺點:類型未知

使用 Java 集合的“缺點”是在將對象置入一個集合時丟失了類型信息。之所以會發(fā)生這種情況,是由于當初編寫集合時,那個集合的程序員根本不知道用戶到底想把什么類型置入集合。若指示某個集合只允許特定的類型,會妨礙它成為一個“常規(guī)用途”的工具,為用戶帶來麻煩。為解決這個問題,集合實際容納的是類型為 Object 的一些對象的句柄。

當然,也要注意集合并不包括基本數(shù)據(jù)類型,因為它們并不是從“任何東西”繼承來的。
Java 不允許人們?yōu)E用置入集合的對象。假如將一條狗扔進一個貓的集合,那么仍會將集合內(nèi)的所有東西都看作貓,所以在使用那條狗時會得到一個“違例”錯誤。在同樣的意義上,假若試圖將一條狗的句柄“造型”到一只貓,那么運行期間仍會得到一個“違例”錯誤

class Cat {private int catNumber;Cat(int i) {catNumber = i;}void print() {System.out.println("Cat #" + catNumber);} }class Dog {private int dogNumber;Dog(int i) {dogNumber = i;}void print() {System.out.println("Dog #" + dogNumber);} }public class CatsAndDogs {public static void main(String[] args) {Vector cats = new Vector();for (int i = 0; i < 7; i++)cats.addElement(new Cat(i));// Not a problem to add a dog to cats:cats.addElement(new Dog(7));for (int i = 0; i < cats.size(); i++)((Cat) cats.elementAt(i)).print();// Dog is detected only at run-time} }
  • 錯誤有時并不顯露出來
    在某些情況下,程序似乎正確地工作,不造型回我們原來的類型。第一種情況是相當特殊的: String 類從編譯器獲得了額外的幫助,使其能夠正常工作。只要編譯器期待的是一個String 對象,但它沒有得到一個,就會自動調(diào)用在 Object 里定義、并且能夠由任何 Java 類覆蓋的 toString()方法。這個方法能生成滿足要求的String 對象,然后在我們需要的時候使用。因此,為了讓自己類的對象能顯示出來,要做的全部事情就是覆蓋toString()方法。
class Mouse {private int mouseNumber;Mouse(int i) {mouseNumber = i;}// Magic method:public String toString() {return "This is Mouse #" + mouseNumber;}void print(String msg) {if (msg != null)System.out.println(msg);System.out.println("Mouse number " + mouseNumber);} }class MouseTrap {static void caughtYa(Object m) {Mouse mouse = (Mouse) m; // Cast from Objectmouse.print("Caught one!");} }public class WorksAnyway {public static void main(String[] args) {Vector mice = new Vector();for(int i = 0; i < 3; i++)mice.addElement(new Mouse(i));for(int i = 0; i < mice.size(); i++) {// No cast necessary, automatic call// to Object.toString():System.out.println("Free mouse: " + mice.elementAt(i));MouseTrap.caughtYa(mice.elementAt(i));}} }

可在 Mouse 里看到對 toString()的重定義代碼。在 main()的第二個 for 循環(huán)中,可發(fā)現(xiàn)下述語句:

System.out.println("Free mouse: " + mice.elementAt(i));

在“ +”后,編譯器預(yù)期看到的是一個 String 對象。 elementAt()生成了一個 Object,所以為獲得希望的String,編譯器會默認調(diào)用 toString()。但不幸的是,只有針對 String 才能得到象這樣的結(jié)果;其他任何類型都不會進行這樣的轉(zhuǎn)換。

隱藏造型的第二種方法已在 Mousetrap 里得到了應(yīng)用。 caughtYa()方法接收的不是一個 Mouse,而是一個Object。隨后再將其造型為一個 Mouse。當然,這樣做是非常冒失的,因為通過接收一個 Object,任何東西都可以傳遞給方法。然而,假若造型不正確—— 如果我們傳遞了錯誤的類型—— 就會在運行期間得到一個違例錯誤。這當然沒有在編譯期進行檢查好,但仍然能防止問題的發(fā)生。注意在使用這個方法時毋需進行造型:
MouseTrap.caughtYa(mice.elementAt(i));

  • 生成能自動判別類型的 Vector
    一個更“健壯”的方案是用 Vector 創(chuàng)建一個新類,使其只接收我們指定的
    類型,也只生成我們希望的類型。
class Gopher {private int gopherNumber;Gopher(int i) {gopherNumber = i;}void print(String msg) {if (msg != null)System.out.println(msg);System.out.println("Gopher number " + gopherNumber);} }class GopherTrap {static void caughtYa(Gopher g) {g.print("Caught one!");} }class GopherVector {private Vector v = new Vector();public void addElement(Gopher m) {v.addElement(m);}public Gopher elementAt(int index) {return (Gopher) v.elementAt(index);}public int size() {return v.size();}public static void main(String[] args) {GopherVector gophers = new GopherVector();for (int i = 0; i < 3; i++)gophers.addElement(new Gopher(i));for (int i = 0; i < gophers.size(); i++)GopherTrap.caughtYa(gophers.elementAt(i));} }

新的 GopherVector 類有一個類型為 Vector 的 private 成員(從 Vector 繼承有些麻煩,理由稍后便知),而且方法也和 Vector 類似。然而,它不會接收和產(chǎn)生普通 Object,只對 Gopher 對象
感興趣。
由于 GopherVector 只接收一個 Gopher(地鼠),所以假如我們使用:
gophers.addElement(new Pigeon());
就會在編譯期間獲得一條出錯消息。采用這種方式,盡管從編碼的角度看顯得更令人沉悶,但可以立即判斷出是否使用了正確的類型。注意在使用 elementAt()時不必進行造型—— 它肯定是一個 Gopher

枚舉器

容納各種各樣的對象正是集合的首要任務(wù)。在 Vector 中, addElement()便是我們插入對象采用的方法,而 elementAt()是
提取對象的唯一方法。 Vector 非常靈活,我們可在任何時候選擇任何東西,并可使用不同的索引選擇多個元素。
若從更高的角度看這個問題,就會發(fā)現(xiàn)它的一個缺陷:需要事先知道集合的準確類型,否則無法使用。乍看來,這一點似乎沒什么關(guān)系。但假若最開始決定使用Vector,后來在程序中又決定(考慮執(zhí)行效率的原因)改變成一個 List(屬于 Java1.2 集合庫的一部分),這時又該如何做呢?
我們通常認為反復器是一種“輕量級”對象;也就是說,創(chuàng)建它只需付出極少的代價。但也正是由于這個原因,我們常發(fā)現(xiàn)反復器存在一些似乎很奇怪的限制。例如,有些反復器只能朝一個方向移動。
Java 的 Enumeration(枚舉,注釋②)便是具有這些限制的一個反復器的例子。除下面這些外,不可再用它
做其他任何事情:
(1) 用一個名為 elements()的方法要求集合為我們提供一個 Enumeration。我們首次調(diào)用它的 nextElement()
時,這個 Enumeration 會返回序列中的第一個元素。
(2) 用 nextElement() 獲得下一個對象。
(3) 用 hasMoreElements()檢查序列中是否還有更多的對象

class Hamster {private int hamsterNumber;Hamster(int i) {hamsterNumber = i;}public String toString() {return "This is Hamster #" + hamsterNumber;} }class Printer {static void printAll(Enumeration e) {while (e.hasMoreElements())System.out.println(e.nextElement().toString());} }public class HamsterMaze {public static void main(String[] args) {Vector v = new Vector();for (int i = 0; i < 3; i++)v.addElement(new Hamster(i));Printer.printAll(v.elements());} }

仔細研究一下打印方法:

static void printAll(Enumeration e) { while(e.hasMoreElements()) System.out.println( e.nextElement().toString()); }

注意其中沒有與序列類型有關(guān)的信息。我們擁有的全部東西便是Enumeration。為了解有關(guān)序列的情況,一個 Enumeration 便足夠了:可取得下一個對象,亦可知道是否已抵達了末尾。取得一系列對象,然后在其中遍歷,從而執(zhí)行一個特定的操作—— 這是一個頗有價值的編程概念

總結(jié)

以上是生活随笔為你收集整理的java基础系列:集合基础(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。