先上完整圖
數據結構:計算機中存儲、組織數據的方式。
分類:主要是三類,所有的容器數據結構都是這3個基本接口的擴展實現。
其中(Legacy)標註的代表線程安全,
可以用順口溜:喂(Vector-對標ArrayList),S(Stack,StringBuffer-對標StringBuilder)H(HashTable-對標HashMap,Properties-繼承自HashTable)E(Enum枚舉,有EnumSet和EnumMap兩個子類)記憶。
- (interface)Iterator(可迭代,線性)
- (interface)Map (k,v結構,非線性)
- (interface)Comparable(可比較)
Iterator:迭代器
這個接口的名稱就很能說明一切了,繼承或實現該接口就擁有了可迭代的能力,但這個更多是作爲標識作用,並不算數據結構,數據結構是從Collection開始的,類似的還有Comparable和Cloneable
接口的方法:
boolean hasNext();//是否有下一個數據
E next();//下一個數據
//移除迭代器返回的數據,並可以選擇是否取出,1.8以後使用,這個操作在<算法>一書中受到了批判,屬於用力過猛的方法,
default void remove() {
throw new UnsupportedOperationException("remove");
}
//hasNext()和next()方法的封裝
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
Collection:單列數據
List:有序(先後),主要有兩大分支,LinkedList和ArrayList
LinkedList:底層是鏈表,並不複雜抓住鏈表本質看源碼
核心代碼:以初始化方法一覽
1.初始化,本質是調用addAll()方法,其中index爲0,也就是說沒有擴容的說法,則也符合鏈表的特性
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
ArrayList:本質是數組
核心代碼:
1.初始化
//默認容量是10
private static final int DEFAULT_CAPACITY = 10;
直接添加數據
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
這裏可以看到它其實是將c變成了數組,而且我發現toArray()方法是屬於Collection的,
則意味這在LinkedList也有,但LinkedList並沒有實現它,而是使用父類的,
而且測試下來確實可以使用,這讓我有點好奇,LinkedList的數據是如何變成數組的,原理還有待探究.
先創建數組,這裏可以看到是可以指定容量的。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
2.添加數據以及被動擴容。可以看到如果數據存滿了,就會觸發擴容
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//這裏可以看到擴容的規則是擴容爲原來的容量的3/2,即增加1/2數據的容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
Vector:線程安全的ArrayList,所以對應的特性都有
核心代碼:
1.初始化,與ArrayList完全一致
2.線程安全Synchronize的體現在對數的操作(不包括初始化)
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
Set:數學概念集合的抽象,並不是二圖所說的有序,因爲我沒找到更好的圖了,抱歉。Set子類有實現了有序的,但是在實現繼承或實現Sorted的基礎上。
HashSet:底層以HashMap實現,這裏就沒有進行排序
核心代碼;
1.初始化
<1> public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
<2> public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
可以很清楚的看到底層是HashMap,可以直接放數據集合或什麼都不放(這裏沒放源碼),也可以先指定容量
2.添加
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
這裏可以看到HashSet的值其實是存放在了HashMap<K,V>結構的K裏面,也就是索引,而不是值V裏面,
值是固定的PRESENT對象,因爲HashMap的K是不能重複的,這樣就直接保證了HashSet數據的不重複。
3.遍歷
public Iterator<E> iterator() {
return map.keySet().iterator();
}
這裏可以直接看到是直接使用HashMap的方法進行遍歷