一、 Arrays.asList() 的三大架構隱患
在 Java 8 以前,開發者最常使用 Arrays.asList(...) 將多個元素或陣列轉換為 List,但其底層封裝帶來了極易踩雷的副作用:
行為限制:該內部類別繼承自 AbstractList,卻未覆寫 add()、remove()、clear() 等結構性變更方法。
執行崩潰:若對其呼叫 list.add("test"),執行期直接噴出 UnsupportedOperationException。
改陣列動 List:修改原陣列的元素值,list.get() 取得的值會跟著改變。
改 List 動陣列:對 List 呼叫允許的 list.set(0, "newVal"),原陣列對應位置的數值也會被同步篡改,破壞封裝性。
傳入基本型別陣列(如 int[] arr = {1, 2, 3};):
因泛型型別擦除無法支援基本型別,JVM 會將整個 int[] 陣列當作單一物件。
Arrays.asList(arr) 回傳的實際型態為 List<int[]>,長度 list.size() 是 1 而不是 3。
二、 Collections 工具類的防禦性包裝與假不可變
「非真正不可變(Not Truly Immutable)」的隱憂:
Wrapper 內部直接指向外部傳入的 originalList 引用。
若呼叫端依然保留對 originalList 的引用並執行 originalList.add("evil"),這個唯讀 Wrapper 取得的內容依然會同步被更新。
結論:它只是「不可直接透過此視圖修改(Unmodifiable View)」,並非記憶體層面真正的不可變集合(Immutable Collection)。
迭代器(Iterator)未保護地雷:即使使用了 synchronizedList,在執行 for-each 或遍歷時,迭代過程並未被自動加鎖,多執行緒同時寫入時依然會拋出 ConcurrentModificationException,必須由開發者手動在外部寫 synchronized (list) { ... } 進行雙重鎖定。
三、 現代 Java 9+ 原生不可變集合:List.of() / Set.of() / Map.of()
Java 9 引入的工廠方法徹底解決了過去十幾年集合初始化的痛點。
任何呼叫 add()、set()、remove() 的操作,一律在編譯與執行期拋出 UnsupportedOperationException。
不存在背後被原始陣列或原始集合修改的副作用。
List.of()、Set.of()、Map.of() 嚴禁包含任何 null 元素 / Key / Value:
List.of("A", null, "C"); // 建立時立即拋出 NullPointerException
架構價值:強迫開發者在資料進入集合的第一時間完成非空校驗,徹底消滅日後延遲噴出 NPE 的隱蔽臭蟲。
List.of() 在底層針對長度進行了特化實作:
長度為 0:使用全域共用單例 ImmutableCollections.EMPTY_LIST。
長度為 1 或 2:分別有專屬的 List12 實作,內部直接用欄位儲存元素,連底層陣列物件都不用建,極致節省 Heap 記憶體並提升存取速度。
Set.of() 在每次 JVM 啟動時都會注入隨機鹽值(Salt),故意在不同次執行時給出不同的遍歷順序,從語法層面強迫工程師不得寫出依賴順序的脆弱程式碼。