List接口
介紹
java.util.List
接口繼承自 Collection
接口 ,是單列集合中的一個重要分支。
- 允許出現重復元素
- 線性存儲,有索引
- 有序,存入和取出的順序是一致的
特有方法
方法 | 描述 |
---|---|
void add(int index, E element) | 將指定的元素插入此列表中的指定位置(可選操作)。 |
E get(int index) | 返回此列表中指定位置的元素。 |
E remove(int index) | 刪除該列表中指定位置的元素(可選操作)。 |
E set(int index, E element) | 用指定的元素(可選操作)替換此列表中指定位置的元素。 |
測試代碼
package collection;
import java.util.ArrayList;
import java.util.List;
/**
* 測試list接口的幾個特有方法
* 有序,與index相關的常用方法
*/
public class TestList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
System.out.println(list);
list.add("A");
list.add("B");
list.add("C");
System.out.println(list);
list.add(1, "a");
System.out.println(list);
System.out.println("刪除元素"+list.remove(0));
System.out.println(list);
System.out.println("被替換的元素" + list.set(0, "A"));
System.out.println(list);
}
}
結果
[]
[A, B, C]
[A, a, B, C]
刪除元素A
[a, B, C]
被替換的元素a
[A, B, C]
注意下標越界跟數組一樣
List子類
ArrayList集合
java.util.ArrayList 是可調整大小的數組的實現List接口
元素增刪慢,查找快,滿足日常開發中實現查詢數據、遍歷數據等功能
但不意味着隨意使用ArrayList完成任何需求,當頻繁增刪數據時,效率極低。
底層原理
數據存放在Object類型的數組中:
transient Object[] elementData; // non-private to simplify nested class access
既然是數組,那它是怎樣實現自動擴容的呢?
以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));
}
// 如果列表為空,返回默認容量 DEFAULT_CAPACITY = 10,否則返回最小容量
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++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 擴容過程,舊容量 = 原數組長度,新容量 = 擴容1.5倍
// 如果新容量小於最小容量,新容量=最小容量
// 如果新容量 大於 最大數組大小 Integer.MAX_VALUE - 8
//總之用數組存儲是不可能無限增長,超過21億多就拋出內存溢出異常
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
// 最后調用工具類Arrays的復制方法,將原數組的數據復制到一個新的數組
Arrays.java
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
return copy;
}
// 底層還是調用了系統級的復制數組方法
// 本地方法
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
注意
ArrayList的實現不是同步的,存在線程安全問題。
LinkedList集合
特點
- 底層用鏈表結構:查詢慢,增刪快
- 包含大量操作首尾元素的方法
- 使用特有方法時不要用多態
底層
用雙向鏈表存儲數據
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
繼承 AbstractSequentialList 類
實現了很多接口
- List 列表的相關操作
- Queue
- Deque 隊列
- Cloneable 克隆
- Serializable 序列化,用於網絡傳輸
public class LinkedList<E>extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
public interface Deque<E> extends Queue<E> {}
Java LinkedList(鏈表) 類似於 ArrayList,是一種常用的數據容器。
與 ArrayList 相比,LinkedList 的增加和刪除對操作效率更高,而查找和修改的操作效率較低。
以下情況使用 ArrayList :
- 頻繁訪問列表中的某一個元素。
- 只需要在列表末尾進行添加和刪除元素操作。
以下情況使用 LinkedList :
- 你需要通過循環迭代來訪問列表中的某些元素。
- 需要頻繁的在列表開頭、中間、末尾等位置進行添加和刪除元素操作。
特有方法
方法 | 描述 |
---|---|
void addFirst(E e) | 在該列表開頭插入指定的元素。 |
void addLast(E e) | 將指定的元素追加到此列表的末尾。 |
void push(E e) | 將元素推送到由此列表表示的堆棧上。 |
E getFirst() | 返回此列表中的第一個元素。 |
E getLast() | 返回此列表中的最后一個元素。 |
E peek() | 檢索但不刪除此列表的頭(第一個元素)。 |
E peekFirst() | 檢索但不刪除此列表的第一個元素,如果此列表為空,則返回 null 。 |
E peekLast() | 檢索但不刪除此列表的最后一個元素,如果此列表為空,則返回 null 。 |
E poll() | 檢索並刪除此列表的頭(第一個元素)。 |
E pollFirst() | 檢索並刪除此列表的第一個元素,如果此列表為空,則返回 null 。 |
E pollLast() | 檢索並刪除此列表的最后一個元素,如果此列表為空,則返回 null 。 |
E removeFirst() | 從此列表中刪除並返回第一個元素。 |
E removeLast() | 從此列表中刪除並返回最后一個元素。 |
E pop() | 從此列表表示的堆棧中彈出一個元素。 |
Vector
所有單列集合的祖先,Java 1.0版本就已經發布!
Java1.2版本之后,ArrayList取代了Vector。
Vector 類實現了一個動態數組。和 ArrayList 很相似,但是兩者是不同的:
- Vector 是同步訪問的。
- Vector 包含了許多傳統的方法,這些方法不屬於集合框架。
Set接口
java.util.Set
接口繼承自 Collection
接口 ,也是單列集合中的一個重要分支。
- 不允許存儲重復的元素
- 沒有索引!沒有帶索引的方法,方法和Collection一樣
- 不能用普通for循環遍歷,增強for可以
Set子類
HashSet集合
java.util.HashSet
集合實現 Set
接口
特點
- 不重復
- 無序
- 無索引
底層
HashTable 查詢速度非常快
public class HashSet<E>extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable{
static final long serialVersionUID = -5024744406713321676L;
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
}
測試代碼
import java.util.HashSet;
import java.util.Iterator;
public class TestHashSet {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
System.out.println(set.add("a"));
System.out.println(set.add("a"));
System.out.println(set.add("b"));
System.out.println(set.add("c"));
System.out.println("========迭代器=======");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.print(it.next() + "\t");
}
System.out.println("\n========增強for=======");
for (String s : set) {
System.out.print(s + "\t");
}
}
}
結果
true
false
true
true
========迭代器=======
a b c
========增強for=======
a b c
Hash值
一個十進制的整數,由系統隨機給出。
可以視為模擬對象存儲的邏輯地址,而不是數據實際存儲的物理地址。
Object祖宗類中就提供獲取hash值的方法hashCode() ,hashCode調用了
public native int hashCode();
對具體實現感興趣的請轉至傳送門
哈希碼的通用約定如下:
- 在java程序執行過程中,在一個對象沒有被改變的前提下,無論這個對象被調用多少次,hashCode方法都會返回相同的整數值。對象的哈希碼沒有必要在不同的程序中保持相同的值。
- 如果2個對象使用equals方法進行比較並且相同的話,那么這2個對象的hashCode方法的值也必須相等。
- 如果根據equals方法,得到兩個對象不相等,那么這2個對象的hashCode值不需要必須不相同。但是,不相等的對象的hashCode值不同的話可以提高哈希表的性能。
Hash表
查詢快!
JDK 1.8 之前,哈希表 = 數組 + 鏈表
JDK 1.8 之后
- 哈希表 = 數組 + 鏈表
- 哈希表 = 數組 + 紅黑樹
- 紅黑樹在數量大時提高查詢速度
Hash表的底層
數組結構:把元素分組,相同哈希值的放到一起,用鏈表或紅黑樹保存
當鏈表長度超過8位,將鏈表轉為紅黑樹
初始容量為16
存儲數據到集合的過程
HashSet的底層
就是HashMap !!
https://blog.csdn.net/HD243608836/article/details/80214413
HashMap明天再補