引言
在Java開發中,陣列與集合之間的轉換是一個常見的操作。Arrays.asList()
方法因其簡潔的語法和便捷的使用方式,被廣泛應用於將陣列轉換為集合(如List
)。然而,這個看似簡單的方法背後卻隱藏著一個大坑,如果不小心處理,可能會導致線上事故,本文也是本人自身專案遇到的慘痛經歷,希望能夠透過講解這個問題,讓大家在實際工作中避開這個坑。
事故回顧
這個問題是出現開發一個電商平臺的訂單系統時,遇到了類似的問題。需要將一個訂單ID陣列轉換為一個List
, 並在後續的業務邏輯中向這個List
中新增新的訂單ID。由於個人經驗不足,使用了Arrays.asList()
方法,部署到線上導致在新增新訂單ID時丟擲了UnsupportedOperationException
異常, 整個訂單處理流程中斷,線上系統出現了嚴重的故障。
影響分析
這次事故對電商平臺造成了巨大的影響:
使用者體驗下降:由於訂單處理流程中斷,使用者無法正常下單,導致使用者體驗大幅下降。
業務中斷:訂單系統的故障直接影響到了整個電商平臺的業務運營,造成了大量的訂單積壓。
經濟損失:由於業務中斷,平臺失去了大量的潛在收入,給公司帶來了不小的經濟損失。
信任危機:頻繁的系統故障讓使用者對平臺的信任度下降,可能導致使用者流失。
後面也是及時修復,吸取本次事故教訓。
事故問題描述
首先先簡單描述一下這個事故問題,廢話不多說,直接看實際業務程式碼,也就將一個數組轉換為List
,並對這個List
進行增刪操作。
Integer[] arr = {1, 2}; List<Integer> list = Arrays.asList(arr); list.add(3);
上述程式碼看起來,沒問題,編譯器也沒有報錯。但是執行這段程式碼時,會丟擲UnsupportedOperationException
異常,提示我們不支援新增操作。這究竟是怎麼回事呢?
所以這是不是個大坑,如果在實際開發中沒有進行自測,必定不會釀成重大線上事故。
問題分析
上述已經知道這個使用Arrays.asList()將陣列裝換成List會在add情況會報錯,接下來深入原始碼進行分析。
Arrays.asList()的內部實現
Arrays.asList(arr)
方法實際上返回的是一個Arrays
類的內部類ArrayList
,而不是我們常用的java.util.ArrayList
。這個內部類ArrayList
繼承自AbstractList
,並沒有實現add
和remove
方法。
Arrays
類的內部類ArrayList
完整原始碼如下:
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, java.io.Serializable { private static final long serialVersionUID = -2764017481108945198L; private final E[] a; ArrayList(E[] array) { a = Objects.requireNonNull(array); } @Override public int size() { return a.length; } @Override public Object[] toArray() { return a.clone(); } @Override @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { int size = size(); if (a.length < size) return Arrays.copyOf(this.a, size, (Class<? extends T[]>) a.getClass()); System.arraycopy(this.a, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } @Override public E get(int index) { return a[index]; } @Override public E set(int index, E element) { E oldValue = a[index]; a[index] = element; return oldValue; } @Override public int indexOf(Object o) { E[] a = this.a; if (o == null) { for (int i = 0; i < a.length; i++) if (a[i] == null) return i; } else { for (int i = 0; i < a.length; i++) if (o.equals(a[i])) return i; } return -1; } @Override public boolean contains(Object o) { return indexOf(o) != -1; } @Override public Spliterator<E> spliterator() { return Spliterators.spliterator(a, Spliterator.ORDERED); } @Override public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); for (E e : a) { action.accept(e); } } @Override public void replaceAll(UnaryOperator<E> operator) { Objects.requireNonNull(operator); E[] a = this.a; for (int i = 0; i < a.length; i++) { a[i] = operator.apply(a[i]); } } @Override public void sort(Comparator<? super E> c) { Arrays.sort(a, c); } }
可以看到並沒有實現add和remove方法,這兩個方法在父類中,預設丟擲UnsupportedOperationException異常。
具體原因
下面是內部原始碼的分析
內部類
ArrayList
的限制:Arrays.asList(arr)
返回的內部類ArrayList
是一個固定長度的列表,它沒有實現add
和remove
方法。當我們呼叫
list.add(3)
時,實際上呼叫的是AbstractList
中的add
方法,而這個方法直接丟擲了UnsupportedOperationException
異常。原始碼分析:
AbstractList
的add
方法如下:public void add(int index, E element) { throw new UnsupportedOperationException(); }
這個方法沒有具體實現,而是直接丟擲異常。
對比java.util.ArrayList類,很明顯實現add
和remove
方法,並沒有直接丟擲UnsupportedOperationException
異常
解決方案
透過上述原始碼,爲了避免這個問題,需要使用java.util.ArrayList
對Arrays.asList(arr)
返回的列表進行封裝,而不是直接使用,這樣就可以正常進行增刪操作了,詳細步驟如下;
詳細步驟
在上述程式碼的基礎上進行程式碼最佳化修改。
建立陣列:
Integer[] arr = {1, 2};
轉換為List:
List<Integer> list = Arrays.asList(arr);
封裝為可變List:這個就是問題解決的關鍵,使用
java.util.ArrayList
對Arrays.asList(arr)
進行封裝ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(arr));
進行增刪操作:
arrayList.add(3); // 正常新增元素 arrayList.remove(1); // 正常刪除元素
程式碼示例
最終完整程式碼如下:
public class Arrays_BugDemo { public static void main(String[] args) { Integer[] arr = {1, 2}; List<Integer> list = Arrays.asList(arr); // 使用ArrayList進行封裝 ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(arr)); try { list.add(3); // 這裏會報錯 } catch (UnsupportedOperationException e) { System.out.println("list.add(3) 報錯: " + e.getMessage()); } arrayList.add(3); // 現在可以正常新增元素 arrayList.forEach(System.out::println); } }
執行結果,程式可以正常執行,並且最終arrayList可以新增新元素。
總結
透過本文的詳細講解,我們可以得出以下幾點結論:
Arrays.asList(arr)
返回的是一個固定長度的List
集合,沒有add
和remove
具體實現方法,不能進行增刪操作,否則會報錯UnsupportedException
。如果需要進行增刪操作,可以使用
java.util.ArrayList
進行封裝,如:new ArrayList<>(Arrays.asList(arr))
。
希望本文能夠幫助大家避免類似的線上事故,提高程式碼的健壯性和可靠性。在實際開發中,務必注意Arrays.asList()
的使用場景,確保程式碼的正確性和穩定性。同時,也應該從這次事故中吸取教訓,加強程式碼審查和測試,避免類似問題的再次發生。