Java 集合 ArrayList 需要知道的几个问题
問:Arraylist 的動態擴容機制是如何自動增加的?簡單說說你理解的流程?
答:當在 ArrayList 中增加一個對象時 Java 會去檢查 Arraylist 以確保已存在的數組中有足夠的容量來存儲這個新對象(默認為 10,最大容量為 int 上限,減 8 是為了容錯),如果沒有足夠容量就新建一個長度更長的數組(原來的1.5倍),舊的數組就會使用 Arrays.copyOf 方法被復制到新的數組中去,現有的數組引用指向了新的數組。下面代碼展示為 Java 1.8 中通過 ArrayList.add 方法添加元素時,內部會自動擴容,擴容流程如下:
public boolean add(E e) {//確保容量夠用,內部會嘗試擴容,如果需要ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true;} //在未指定容量的情況下,容量為DEFAULT_CAPACITY = 10//并且在第一次使用時創建容器數組,在存儲過一次數據后,數組的真實容量至少DEFAULT_CAPACITYprivate void ensureCapacityInternal(int minCapacity) {//判斷當前的元素容器是否是初始的空數組if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//如果是默認的空數組,則 minCapacity 至少為DEFAULT_CAPACITYminCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);}//通過該方法進行真實準確擴容嘗試的操作private void ensureExplicitCapacity(int minCapacity) {modCount++;//記錄List的結構修改的次數//需要擴容if (minCapacity - elementData.length > 0)grow(minCapacity);}private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//擴容操作private void grow(int minCapacity) {//原來的容量int oldCapacity = elementData.length;//新的容量 = 原來的容量 + (原來的容量的一半)int newCapacity = oldCapacity + (oldCapacity >> 1);//如果計算的新的容量比指定的擴容容量小,那么就使用指定的容量if (newCapacity - minCapacity < 0)newCapacity = minCapacity;//如果新的容量大于MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8)//那么就使用hugeCapacity進行容量分配if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// minCapacity is usually close to size, so this is a win://創建長度為newCapacity的數組,并復制原來的元素到新的容器,完成ArrayList的內部擴容elementData = Arrays.copyOf(elementData, newCapacity);}private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}問:請寫出下面代碼片段的運行結果及原因?
ArrayList<Integer> list = new ArrayList<Integer>();list.add(1);list.add(2);list.add(3);Integer[] array1 = new Integer[3];list.toArray(array1);Integer[] array2 = list.toArray(new Integer[0]);System.out.println(Arrays.equals(array1, array2));// 1 結果是什么?為什么? Integer[] array = { 1, 2, 3 };List<Integer> list = Arrays.asList(array);list.add(4);// 2 結果是什么?為什么? Integer[] array = { 1, 2, 3 };List<Integer> list = new ArrayList<Integer>(Arrays.asList(array));list.add(4);// 3 結果是什么?為什么?1 輸出為 true,因為 ArrayList 有兩個方法可以返回數組 Object[] toArray() 和 <T> T[] toArray(T[] a),第一個方法返回的數組是通過 Arrays.copyOf 實現的,第二個方法如果參數數組長度足以容納所有元素就使用參數數組,否則新建一個數組返回(這里也是使用copyOf來實現的),所以結果為 true。
public <T> T[] toArray(T[] a) {if (a.length < size)// Make a new array of a's runtime type, but my contents:return (T[]) Arrays.copyOf(elementData, size, a.getClass());System.arraycopy(elementData, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}2 會拋出 UnsupportedOperationException 異常,因為 Arrays 的 asList 方法返回的是一個 Arrays 內部類的 ArrayList 對象,這個對象沒有實現 add、remove 等方法,只實現了 set 等方法,所以通過 Arrays.asList 轉換的列表不具備結構可變性。
public static <T> List<T> asList(T... a) {return new ArrayList<>(a);}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);}@Overridepublic int size() {return a.length;}@Overridepublic 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;}@Overridepublic E get(int index) {return a[index];}@Overridepublic E set(int index, E element) {E oldValue = a[index];a[index] = element;return oldValue;}@Overridepublic 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;}@Overridepublic boolean contains(Object o) {return indexOf(o) != -1;}@Overridepublic Spliterator<E> spliterator() {return Spliterators.spliterator(a, Spliterator.ORDERED);}@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);for (E e : a) {action.accept(e);}}@Overridepublic 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]);}}@Overridepublic void sort(Comparator<? super E> c) {Arrays.sort(a, c);}}3 當然可以正常運行咯,不可變結構的 Arrays 的 ArrayList 通過構造放入了真正的萬能 ArrayList,自然就可以操作咯。
問:為什么 ArrayList 的增加或刪除操作相對來說效率比較低?能簡單解釋下為什么嗎?
答:ArrayList 在小于擴容容量的情況下其實增加操作效率是非常高的,在涉及擴容的情況下添加操作效率確實低,刪除操作需要移位拷貝,效率是低點。因為 ArrayList 中增加(擴容)或者是刪除元素要調用 System.arrayCopy 這種效率很低的方法進行處理,所以如果遇到了數據量略大且需要頻繁插入或刪除的操作效率就比較低了,具體可查看 ArrayList 的 add 和 remove 方法實現,但是 ArrayList 頻繁訪問元素的效率是非常高的,因此遇到類似場景我們應該盡可能使用 LinkedList 進行替代效率會高一些。
問:簡單說說 Array 和 ArrayList 的區別?
答:這題相當小兒科,Array 可以包含基本類型和對象類型,ArrayList 只能包含對象類型;Array 的大小是固定的,ArrayList 的大小是動態變化的;ArrayList 提供了更多的方法和特性,譬如 addAll()、removeAll()、iterator() 等。
?
閱讀原文請關注微信公眾號:碼農每日一題
轉載于:https://www.cnblogs.com/lostyears/p/8529594.html
總結
以上是生活随笔為你收集整理的Java 集合 ArrayList 需要知道的几个问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL5.7.17源码编译安装与配置
- 下一篇: Color色彩