一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Java集合系列之ArrayList源碼分析

Java集合系列之ArrayList源碼分析

2021-04-07 14:09勞夫子 JAVA教程

這篇文章主要為大家詳細介紹了Java集合系列之ArrayList源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下

本篇分析ArrayList的源碼,在分析之前先跟大家談一談數組。數組可能是我們最早接觸到的數據結構之一,它是在內存中劃分出一塊連續的地址空間用來進行元素的存儲,由于它直接操作內存,所以數組的性能要比集合類更好一些,這是使用數組的一大優勢。但是我們知道數組存在致命的缺陷,就是在初始化時必須指定數組大小,并且在后續操作中不能再更改數組的大小。在實際情況中我們遇到更多的是一開始并不知道要存放多少元素,而是希望容器能夠自動的擴展它自身的容量以便能夠存放更多的元素。ArrayList就能夠很好的滿足這樣的需求,它能夠自動擴展大小以適應存儲元素的不斷增加。它的底層是基于數組實現的,因此它具有數組的一些特點,例如查找修改快而插入刪除慢。本篇我們將深入源碼看看它是怎樣對數組進行封裝的。首先看看它的成員變量和三個主要的構造器。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//默認初始化容量
private static final int DEFAULT_CAPACITY = 10;
 
//空對象數組
private static final Object[] EMPTY_ELEMENTDATA = {};
 
//對象數組
private transient Object[] elementData;
 
//集合元素個數
private int size;
 
//傳入初始容量的構造方法
public ArrayList(int initialCapacity) {
  super();
  if (initialCapacity < 0) {
    throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
  }
  //新建指定容量的Object類型數組
  this.elementData = new Object[initialCapacity];
}
 
//不帶參數的構造方法
public ArrayList() {
  super();
  //將空的數組實例傳給elementData
  this.elementData = EMPTY_ELEMENTDATA;
}
 
//傳入外部集合的構造方法
public ArrayList(Collection<? extends E> c) {
  //持有傳入集合的內部數組的引用
  elementData = c.toArray();
  //更新集合元素個數大小
  size = elementData.length;
  //判斷引用的數組類型, 并將引用轉換成Object數組引用
  if (elementData.getClass() != Object[].class) {
    elementData = Arrays.copyOf(elementData, size, Object[].class);
  }
}

可以看到ArrayList的內部存儲結構就是一個Object類型的數組,因此它可以存放任意類型的元素。在構造ArrayList的時候,如果傳入初始大小那么它將新建一個指定容量的Object數組,如果不設置初始大小那么它將不會分配內存空間而是使用空的對象數組,在實際要放入元素時再進行內存分配。下面再看看它的增刪改查方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//增(添加)
public boolean add(E e) {
  //添加前先檢查是否需要拓展數組, 此時數組長度最小為size+1
  ensureCapacityInternal(size + 1);
  //將元素添加到數組末尾
  elementData[size++] = e;
  return true;
}
 
 
//增(插入)
public void add(int index, E element) {
  //插入位置范圍檢查
  rangeCheckForAdd(index);
  //檢查是否需要擴容
  ensureCapacityInternal(size + 1);
  //挪動插入位置后面的元素
  System.arraycopy(elementData, index, elementData, index + 1, size - index);
  //在要插入的位置賦上新值
  elementData[index] = element;
  size++;
}
 
//刪
public E remove(int index) {
  //index不能大于size
  rangeCheck(index);
  modCount++;
  E oldValue = elementData(index);
  int numMoved = size - index - 1;
  if (numMoved > 0) {
    //將index后面的元素向前挪動一位
    System.arraycopy(elementData, index+1, elementData, index, numMoved);
  }
  //置空引用
  elementData[--size] = null;
  return oldValue;
}
 
//改
public E set(int index, E element) {
  //index不能大于size
  rangeCheck(index);
  E oldValue = elementData(index);
  //替換成新元素
  elementData[index] = element;
  return oldValue;
}
 
//查
public E get(int index) {
  //index不能大于size
  rangeCheck(index);
  //返回指定位置元素
  return elementData(index);
}

每次添加一個元素到集合中都會先檢查容量是否足夠,否則就進行擴容,擴容的細節下面會講到。我們先看具體增刪改查要注意的地方。
增(添加):僅是將這個元素添加到末尾。操作快速。
增(插入):由于需要移動插入位置后面的元素,并且涉及數組的復制,所以操作較慢。
刪:由于需要將刪除位置后面的元素向前挪動,也會設計數組復制,所以操作較慢。
改:直接對指定位置元素進行修改,不涉及元素挪動和數組復制,操作快速。
查:直接返回指定下標的數組元素,操作快速。
通過源碼看到,由于查找和修改直接定位到數組下標,不涉及元素挪動和數組復制所以較快,而插入刪除由于要挪動元素,涉及到數組復制,操作較慢。并且每次添加操作還可能進行數組擴容,也會影響到性能。下面我們看看ArrayList是怎樣動態擴容的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private void ensureCapacityInternal(int minCapacity) {
  //如果此時還是空數組
  if (elementData == EMPTY_ELEMENTDATA) {
    //和默認容量比較, 取較大值
    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
  }
  //數組已經初始化過就執行這一步
  ensureExplicitCapacity(minCapacity);
}
 
private void ensureExplicitCapacity(int minCapacity) {
  modCount++;
  //如果最小容量大于數組長度就擴增數組
  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;
  }
  //檢驗新的容量是否超過最大數組容量
  if (newCapacity - MAX_ARRAY_SIZE > 0) {
    newCapacity = hugeCapacity(minCapacity);
  }
  //拷貝原來的數組到新數組
  elementData = Arrays.copyOf(elementData, newCapacity);
}

每次添加元素前會調用ensureCapacityInternal這個方法進行集合容量檢查。在這個方法內部會檢查當前集合的內部數組是否還是個空數組,如果是就新建默認大小為10的Object數組。如果不是則證明當前集合已經被初始化過,那么就調用ensureExplicitCapacity方法檢查當前數組的容量是否滿足這個最小所需容量,不滿足的話就調用grow方法進行擴容。在grow方法內部可以看到,每次擴容都是增加原來數組長度的一半,擴容實際上是新建一個容量更大的數組,將原先數組的元素全部復制到新的數組上,然后再拋棄原先的數組轉而使用新的數組。至此,我們對ArrayList中比較常用的方法做了分析,其中有些值得注意的要點:

1. ArrayList底層實現是基于數組的,因此對指定下標的查找和修改比較快,但是刪除和插入操作比較慢。
2. 構造ArrayList時盡量指定容量,減少擴容時帶來的數組復制操作,如果不知道大小可以賦值為默認容量10。
3. 每次添加元素之前會檢查是否需要擴容,每次擴容都是增加原有容量的一半。
4. 每次對下標的操作都會進行安全性檢查,如果出現數組越界就立即拋出異常。
5. ArrayList的所有方法都沒有進行同步,因此它不是線程安全的。
6. 以上分析基于JDK1.7,其他版本會有些出入,因此不能一概而論。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/liuyun1995/p/8286829.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本三级s级在线播放 | 成人网欧美亚洲影视图片 | 成人啪啪漫画全文阅读 | 免费看黄色大片 | 成人人免费夜夜视频观看 | 性的张力 | 色淫阁小说 | 九九久久国产精品免费热6 九九精品视频一区二区三区 | 脱jk裙的美女露小内内无遮挡 | japanesexxxx在线播放 | bt天堂午夜国产精品 | 日本艳鉧动漫1~6完整版在 | a毛片久久免费观看 | 高清一级做a爱免费视 | 拔插拔插.com| 精品在线免费观看视频 | 91视频国产自拍 | 午夜影院0606| 国产精品资源在线观看 | 男女羞羞的视频 | 亚洲成人伦理 | 欧美日韩视频在线成人 | 欧美精品一区二区在线观看播放 | 欧美日韩国内 | 日韩欧美一区二区三区免费看 | 亚洲无人区乱码中文字幕 | 国产91一区二区在线播放不卡 | 无人区在线观看免费视频国语 | 色妞女女女女女bbbb | 免费一看一级欧美 | 性派对videofreeparty | 亚洲成年人在线观看 | 97午夜视频 | 国产一区二区三区久久精品小说 | 亚洲 欧美 偷自乱 图片 | 9966国产精品视频 | 国产精品毛片高清在线完整版 | 久久精品一区二区三区资源网 | 免费观看美女被cao视频 | 日韩v | 久久re视频精品538在线 |