當前位置:
首頁 > 知識 > Java集合-ArrayList

Java集合-ArrayList

一直要總結java集合中的知識,不知道應該如何下筆。覺得集合太多東西了,寫細了太難了,寫粗了又感覺寫不好。不管如何覺得還是要堅持的寫一寫基礎這一類的東西,為了提高自己的編程基礎。本來覺的自己對這些已經很熟悉,最近見過一些大神後發現差距太大了,瞬間懵了,只能在加強學習了。

一、ArrayList是什麼?

ArrayList是實現List介面的動態數組,所謂動態是指它的大小是可變的。實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 介面外,此類還提供一些方法來操作內部用來存儲列表的數組的大小。

既然是數組,肯定就有容量。每個ArrayList對象都有一個容量,該容量是用來表示可以存放多少個數據在裡面,即是數組的大小(默認是10)。當然,動態的肯定就會自動增加,每次我們往裡面添加數據的時候,它都會進行擴容檢查,檢查完擴容會擴大為原來的1.5倍,擴容操作帶來數據向新數組的重新拷貝,影響性能,所以如果我們知道具體業務數據量,在構造ArrayList時可以給ArrayList指定一個初始容量,這樣就會減少擴容時數據的拷貝問題。當然在添加大量元素前,應用程序也可以使用ensureCapacity操作來增加ArrayList實例的容量,這可以減少遞增式再分配的數量。

ArrayList的底層實現是不同步,多線程操作會出現問題,這一點大家要注意。可以插入重複數據,可以插入Null。

二、ArrayList源碼分析

2.1、ArrayList定義

    public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable

ArrayList的定義,繼承AbstractList,實現List, RandomAccess, Cloneable, java.io.Serializable,其中AbstractList實現了 List 的一些位置相關操作(比如 get,set,add,remove),是第一個實現隨機訪問方法的集合類,但不支持添加和替換。RandomAccess代表該類是否支持隨機訪問,Cloneable類支持克隆,Serializable支持序列化。

2.2、底層使用數組

private static final long serialVersionUID = 8683452581122892189L;//serialVersionUID作用是序列化時保持版本的兼容性,即在版本升級時反序列化仍保持對象的唯一性。

private transient Object elementData;//Object數組,transient關鍵字不知道的同學自己查資料去,帶transient關鍵字的變數不會序列化。ArrayList容器,基本操作都是基於該數組進行操作的。

private int size;//數組的大小。

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//要分配的數組的最大大小。

2.3、構造函數

ArrayList有三個構造函數:

ArrayList:默認構造函數,提供初始容量為10的空列表。

ArrayList(int initialCapacity):構造一個具有指定初始容量的空列表。

ArrayList(Collection c):構造一個包含指定 collection 的元素的列表,這些元素是按照該 collection 的迭代器返回它們的順序排列的。

  /**
* 構造具有指定初始容量的空列表。.
*
* @param 初始容量列表的初始容量
* @throws IllegalArgumentException 如果指定的初始容量為負會拋出非法異常。
*/
public ArrayList(int initialCapacity) {
super;
if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; } /** * 構建一個初始容量為十的空列表. */ public ArrayList { this(10); } /** * 構造包含指定元素的列表。 * 集合,按集合返回的順序 * 迭代器 * * @param c的集合,其元素將放在這個列表中。 * @throws NullPointerException 如果指定的集合為null為拋出這個空指針異常。 */ public ArrayList(Collection c) {
elementData = c.toArray;//如果為空,這裡會拋異常
size = elementData.length;
// c.toArray might (incorrectly) not return Object (see 6260652)
if (elementData.getClass != Object.class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
}

2.4、add方法

ArrayList提供了add(E e)、add(int index, E element)、addAll(Collection c)、addAll(int index, Collection c)、set(int index, E element)這個五個方法來實現ArrayList增加。

我就拿一個來講了,懂的一個,其他應該都懂了。

  public boolean add(E e) {
ensureCapacityInternal(size + 1); //增加操作次數
elementData[size++] = e;//把值放到最後一位
return true;
  }
  private void ensureCapacityInternal(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)//如果數組真實存儲大於數組容量就增加
grow(minCapacity);
  }
 private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;//原來的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);//新的容量為舊的1.5倍
if (newCapacity - minCapacity < 0)//增加了還小就按真實的來 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0)//如果比最大還大
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);//複製數組
 }
private static int hugeCapacity(int minCapacity) {//注意是個靜態方法
if (minCapacity < 0) // overflow 小於0,拋異常 throw new OutOfMemoryError; return (minCapacity > MAX_ARRAY_SIZE) ?//大於MAX_ARRAY_SIZE就設置為MAX_VALUE,否則就MAX_ARRAY_SIZE
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

2.5、remove方法

ArrayList提供了remove(int index)、remove(Object o)、removeRange(int fromIndex, int toIndex)、removeAll四個方法進行元素的刪除。

remove(int index):移除此列表中指定位置上的元素。

  public E remove(int index) {//根據下標刪除
rangeCheck(index);//檢查是否越界
modCount++;//操作增加
E oldValue = elementData(index);//獲得下標的值
int numMoved = size - index - 1;//要移動的數據
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//數組往前移
elementData[--size] = null; // 讓GC工作
return oldValue;//返回舊值
  }
  private void rangeCheck(int index) {//檢查越界
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
  }
  private String outOfBoundsMsg(int index) {//是不是感覺經常空間這個,哈哈
return "Index: "+index+", Size: "+size;
  }
  @SuppressWarnings("unchecked")
  E elementData(int index) {
return (E) elementData[index];
  }

remove(Object o):如果存在移除此列表中首次出現的指定元素。

  public boolean remove(Object o) {
if (o == null) {//如果為null
for (int index = 0; index < size; index++) if (elementData[index] == null) {//==來比較 fastRemove(index); return true;//存在並刪除返回true } } else {//如果不為null for (int index = 0; index < size; index++) if (o.equals(elementData[index])) {//equals來比較 fastRemove(index);//存在並刪除返回true return true; } } return false;//不存在返回false   }   private void fastRemove(int index) { modCount++;//操作數增加 int numMoved = size - index - 1;//移動次數 if (numMoved > 0)//移動
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
  }

2.6、get方法

  public E get(int index) {
rangeCheck(index);//檢查是否越界
return elementData(index);//取值
  }

2.7、注意subList方法

  public static void main(String[] args) {
List list1 = new ArrayList;
list1.add(1);
list1.add(2);
// 通過構造函數新建一個包含list1的列表 list2
List list2 = new ArrayList(list1);
// 通過subList生成一個與list1一樣的列表 list3
List list3 = list1.subList(0, list1.size);
// 修改list3
list3.add(3);
System.out.println(list1.size);
System.out.println("list1 == list2:" + list1.equals(list2));
System.out.println("list1 == list3:" + list1.equals(list3));

  }

上面一段代碼我感覺大部人都會認為結果是2個false,list2是新構造的肯定與list1不一樣,list3是截取的肯定也不一樣。所以會認為都是false,我們深入subList去看看。

public List subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);//檢查下標和大小
return new SubList(this, 0, fromIndex, toIndex);//新創建一個SubList對象,注意傳入的是this,代表了要截取的對象
}
private class SubList extends AbstractList implements RandomAccess {//我擦,跟list差不多的感覺
private final AbstractList parent;
private final int parentOffset;
private final int offset;
int size;//我擦,熟悉的感覺
SubList(AbstractList parent,int offset, int fromIndex, int toIndex) {//剛剛的構造
this.parent = parent;//this.parent = parent;而parent就是在前面傳遞過來的list,也就是說this.parent就是原始list的引用。
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;//操作數也是跟原來的一樣
}

原來sublist裡面操作的是原來的list,並沒有生成新的list,導致2個其實是一樣的。

所以上面正確的結果是:

list1 == list2:false list1 == list3:true

三、ArrayList總結

因為它是基於數組實現的,主要有如下特點:

1、插入、刪除比較慢,因為插入、刪除需要移動數據位置。

2、可以重複插入數據、可以插入null。

3、查找比較快,可以直接使用下標。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

easygen通用代碼生成框架
vue-schart:vue.js 的圖表組件
Java基礎——關於訪問許可權的一道例題

TAG:科技優家 |