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