首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 核心技術:從數組到集合框架的對象容納之道

     編輯:Java 綜合教學

前言:為什麼需要「容器」?

在《Java 編程思想》第十章中,Bruce Eckel 指出:若程序僅處理固定數量、生命週期已知的對象,設計將極其簡單。但現實中,我們往往在運行時才能確定需要多少對象、甚至具體類型。為了解決「隨時隨地創建任意數量對象」的需求,Java 提供了數組與集合類(Collection Classes)兩大機制。本文將深入剖析兩者的異同、優劣,並通過大量代碼示例演示最佳實踐。

一、數組:高效但僵化的「第一公民」

1.1 核心特性:速度與類型的雙重保障

  • 訪問速度最快:數組是線性連續內存,隨機訪問 O(1),無方法調用開銷。
  • 編譯期類型檢查:`String[]` 只能存 String,防止「貓狗混裝」導致的運行期 `ClassCastException`。
  • 長度不可變:創建即固定,擴容需手動 `new` 更大數組並 `System.arraycopy`。
  • 邊界檢查:JVM 自動拋出 `ArrayIndexOutOfBoundsException`,犧牲微小性能換取安全。

1.2 數組對象的本質

數組標識符本質是指向堆內存對象的引用。堆對象包含:

  • 唯一可訪問字段:`length`(容量,非實際元素數)
  • 唯一訪問語法:`[]` 索引器

示例:多種初始化方式與引用傳遞

// 對象數組:自動初始化為 null
Weeble[] b = new Weeble[5];  // 5 個 null 引用
Weeble[] c = new Weeble[4];
for (int i = 0; i < c.length; i++) c[i] = new Weeble(); // 逐個實例化
Weeble[] d = { new Weeble(), new Weeble(), new Weeble() }; // 集合初始化

// 基本類型數組:自動初始化為 0/false/'\u0000'
int[] f = new int[5]; // {0,0,0,0,0}
int[] h = { 11, 47, 93 };

// 引用傳遞:a、d 指向同一堆對象
Weeble[] a = d;
a = new Weeble[] { new Weeble(), new Weeble() }; // Java 1.1 動態初始化語法

1.3 返回數組:垃圾回收器的禮物

C/C++ 返回指針易發生內存洩漏;Java 返回數組引用,由 GC 自動管理生命週期:

static String[] flavorSet(int n) {
    n = Math.abs(n) % (flav.length + 1);
    String[] results = new String[n];
    int[] picks = new int[n];
    Arrays.fill(picks, -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++)
                if (picks[j] == t) continue retry;
            picks[i] = t;
            results[i] = flav[t];
            break;
        }
    }
    return results; // 僅返回引用,GC 會在無引用時回收
}

二、集合框架:靈活但付出代價的「通用工具」

2.1 核心缺陷:類型擦除與向下轉型

早期集合(Vector、Stack、Hashtable)存儲 `Object` 引用,導致:

  1. 放入時類型信息丟失:編譯器不阻止把 `Dog` 放入「貓的集合」。
  2. 取出時必須強制轉型:`(Cat) cats.elementAt(i)`,若實為 `Dog` 拋 `ClassCastException`。
Vector cats = new Vector();
for (int i = 0; i < 7; i++) cats.addElement(new Cat(i));
cats.addElement(new Dog(7)); // 編譯通過,運行崩潰
for (int i = 0; i < cats.size(); i++)
    ((Cat) cats.elementAt(i)).print(); // 第 7 次拋異常

2.2 兩種繞過轉型的「副作用」

  • String 的特殊待遇:編譯器見 `+` 操作期待 String,自動調用 `toString()`,故集合中直接 `println(elementAt(i))` 可輸出。
  • 封裝器模式(類型安全包裝器):自定義 `GopherVector` 內部持有 `Vector`,`addElement(Gopher)`/`elementAt()` 返回 `Gopher`,將類型檢查前置到編譯期。
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(); }
}
// 使用:gophers.addElement(new Pigeon()); // 編譯報錯!

2.3 參數化類型的渴望

C++ `template`、Ada `generic` 早已解決此問題。Java 1.0/1.1 僅保留 `generic` 關鍵字未實現,直至 Java 5 引入泛型才徹底解決。(註:本文基於早期版本視角)

三、實戰建議:如何選擇?

場景推薦理由
已知固定大小、高頻隨機訪問、基本類型數組零開銷、類型安全、緩存友好
動態增減、未知數量、對象存儲ArrayList / LinkedList自動擴容、豐富 API、泛型支持
鍵值映射、快速查找HashMapO(1) 平均查找
線程安全舊代碼維護Vector / Hashtable同步方法,但性能差,新代碼用 ConcurrentHashMap

總結

數組是 Java 中「最高效、類型最安全」的容器,但缺乏彈性;早期集合犧牲類型安全換取通用性,需程序員自律或封裝器輔助。現代 Java 開發應優先使用泛型集合(`List`、`Map`),兼得效率與安全;僅在極致性能敏感、數據結構固定時直接操作數組。理解這段歷史演進,有助於寫出更健壯、可維護的代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved