java j集合_JNotes/Java-集合篇(2)集合之List.md at master · harryjudy2240/JNotes · GitHub...
ArrayList
存儲機制
基于動態數組實現,默認長度 10 ;因為數組特性,其讀取速度快,增刪效率慢
因為寫入或讀取都是通過數組索引直接操作,所以,允許為 null 值, 且可以重復
當數組大小不滿足時需要增加存儲能力,就要將已經有數組的數據復制到新的存儲空間中。當從 ArrayList 的中間位置插入或者刪除元素時,需要對數組進行復制、移動、代價比較高。
因此,它適合隨機查找和遍歷,不適合插入和刪除。
類定義
在 jdk 8 中源碼如下
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{
private static final int DEFAULT_CAPACITY = 10; // 默認大小為 10
transient Object[] elementData; // 數組存儲
private int size;
}
ArrayList 實現了RandomAccess 接口, RandomAccess 是一個標志接口,表明實現這個這個接口的 List 集合是支持快速隨機訪問的。在 ArrayList 中,我們即可以通過元素的序號快速獲取元素對象,這就是快速隨機訪問。
ArrayList 實現了Cloneable 接口,即覆蓋了函數 clone(),能被克隆。
ArrayList 實現java.io.Serializable 接口,這意味著ArrayList支持序列化,能通過序列化去傳輸。
get()方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
通過以上代碼可以看出,ArrayList 通過索引讀取元素,所以其讀取元素效率高
add()方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 迭代時,用來判斷是否被其他線程修改
if (minCapacity - elementData.length > 0)
// 擴容
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 默認擴容 1.5 倍 ; >>1 相當于除 2
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
通過以上代碼發現,添加元素時會觸發擴容機制,當集合擴容時采用的是Arrays.copyOf(elementData, newCapacity),因此造成了新增元素速度慢
remove()方法
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;
return oldValue;
}
刪除方法同樣進行了數組復制,所以效率同樣不高
grow()方法
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 默認擴容 1.5 倍 ; >>1 相當于除 2
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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
1、默認初始容量 10,如果數據長度超過,則容量擴大為原來的 1.5倍,
2、然后會再判斷,擴容后的容量大小是否任然小于數據長度、是則,取為數據大小為最新容量大小
3、再判斷此時的最新容量大小是否超過最大容量值 MAX_ARRAY_SIZE,
4、是則,判斷數據大小是否超過最大容量值 ,
數據大小超過最大容量,則取 Integer 的最大值作為最新容量大小。
數據大小沒有最大容量,則取最大容量作為最新容量大小。
線程安全
ArrayList 線程不安全原因:使用數組存儲,當前多線程時,當對同一位置操作時,可能會被覆蓋,或者讀取到了未更新的數據等一系列問題。
線程安全解決方案
使用 Collections.synchronizedList(); 得到一個線程安全的 ArrayList。
使用 CopyOnWriteArrayList代替:讀寫分離的安全型并發集合對象,寫操作需要加鎖,防止并發寫入時導致寫入數據丟失。寫操作在一個復制的數組上進行,寫操作結束之后需要把原始數組指向新的復制數組。讀操作還是在原始數組中進行,讀寫分離,互不影響。
使用場景:適合讀多寫少的應用場景。
缺點 1 :內存占用:在寫操作時需要復制一個新的數組,使得內存占用為原來的兩倍左右;
缺點 2 :數據不一致:讀操作不能讀取實時性的數據,因為部分寫操作的數據還未同步到讀數組中。
List 和Array轉換
List 轉Array:使用 list.toArray()
Array 轉 List:使用 Arrays.asList(array)
//List 轉Array
List list = new ArrayList();
Object[] array=list.toArray();
//Array 轉 List
// 方式 1
List list = Arrays.asList(array); // 有缺陷
// 方式 2
List list = new ArrayList(Arrays.asList(array));
// 方式 3
List list = new ArrayList(array.length);
Collections.addAll(list, array);
注意:Arrays.asList(array) 不能作為一個 List 對象的復制操作
因為:方式 1 中 只是復制了引用,list 修改會改變 array 內容,因為返回的 list 是 Arrays 里面的一個靜態內部類,該類并未實現add、remove方法,因此在使用時存在局限性
所以,一般使用第 2 中和第 3 種方式轉換
String[] arr = {"1","2","3","4","5"};
List list1 = new ArrayList(Arrays.asList(arr));
List list2 = Arrays.asList(arr);
List list3 = new ArrayList(arr.length);
Collections.addAll(list3, arr);
list1.set(0, "a");
System.out.println("Array:"+Arrays.toString(arr));
System.out.println("List :"+list1.toString());
System.out.println();
list2.set(0, "b");
System.out.println("Array:"+Arrays.toString(arr));
System.out.println("List :"+list2.toString());
System.out.println();
list3.set(0, "c");
System.out.println("Array:"+Arrays.toString(arr));
System.out.println("List :"+list3.toString());
執行結果:
Array:[1, 2, 3, 4, 5]
List :[a, 2, 3, 4, 5]
Array:[b, 2, 3, 4, 5]
List :[b, 2, 3, 4, 5]
Array:[b, 2, 3, 4, 5]
List :[c, 2, 3, 4, 5]
ArrayList 去重
雙重for循環去重
通過set集合判斷去重,不打亂順序
遍歷后以 contains() 判斷賦給另一個list集合
set和list轉換去重
//雙重for循環去重
for (int i = 0; i < list.size() - 1; i++) {
for (int j = list.size() - 1; j > i; j--) {
if (list.get(j).equals(list.get(i))) {
list.remove(j);
}
}
}
//set集合判斷去重,不打亂順序
List listNew=new ArrayList<>();
Set set=new HashSet();
for (String str:list) {
if(set.add(str)){ //HashSet 內部維持map,其鍵唯一
listNew.add(str);
}
}
ArrayList 排序
可以使用如下方式
對象實現 Comparable 接口,
使用 Collections.sort(List list, Comparator super T> c),
使用 ArrayList#sort(Comparator super E> c)
前一種:重寫 compareTo(T o) 方法 ,后兩種:通過匿名內部類的方式實現比較器 Comparator 接口,重寫compare(T o1, T o2)
public class Student implements Comparable {
private int age;
@Override
public int compareTo(Student o) {
return this.age - o.age; // 升序
}
}
//自定義排序1
Collections.sort(list, new Comparator() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge() - s2.getAge();
}
});
//自定義排序2
new ArrayList().sort(new Comparator() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge() - s2.getAge();
}
});
Vector
同樣基于動態數組實現,但它是線程安全的,它支持線程的同步,但實現同步需要很高的花費,
因此,訪問它比訪問 ArrayList 慢。
LinkedList
基于雙向鏈表實現,只能順序訪問
因此,很適合數據的動態插入和刪除,隨機訪問和遍歷速度比較慢。
另外,他還提供了 List 接口中沒有定義的方法,專門用于操作表頭和表尾元素,可以當作堆棧、隊列和雙向隊列使用。
List 之間的區別
ArrayList 和 Vector 的
Vector 基本與ArrayList類似,數組存儲,排列有序,可重復,允許多個NULL1、
訪問效率比ArrayList稍慢,因為**Vector是同步的,線程安全**,它有對外方法都由 synchronized 修飾。
為什么要用Arraylist取代Vector呢
Vector類的所有方法都是同步的??梢杂蓛蓚€線程安全地訪問一個Vector對象、但是一個線程訪問Vector的話代碼要在同步操作上耗費大量的時間。
Arraylist不是同步的,所以在不需要保證線程安全時時建議使用Arraylist。
Arraylist 與 LinkedList 區別
線程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保證線程安全;
底層數據結構:Arraylist 底層使用的是數組;LinkedList 底層使用的是雙向鏈表數據結構
插入和刪除是否受元素位置的影響
ArrayList 采用數組存儲,所以插入和刪除元素的時間復雜度受元素位置的影響,時間復雜度就為 O(n-i)
LinkedList 采用鏈表存儲,所以插入和刪除元素時間復雜度不受元素位置的影響,都是近似 O(1)而數組為近似 O(n)
是否支持快速隨機訪問: LinkedList 不支持高效的隨機元素訪問,而 ArrayList 支持??焖匐S機訪問就是通過元素的序號快速獲取元素對象(對應于get(int index)方法)。
總結
以上是生活随笔為你收集整理的java j集合_JNotes/Java-集合篇(2)集合之List.md at master · harryjudy2240/JNotes · GitHub...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java上传文件到ftp_java实现文
- 下一篇: 类属性的特征java_java定义类、属