線性表
線性表:零個或多個具有相同類型的數據元素的有限序列。數據元素的個數稱爲線性表的長度。
A=(a1,a2,……ai-1,ai,ai+1,……,an);
● A代表一個線性表
● ai(1<=i<=n)成爲線性表的元素,i爲元素的下標,表示該元素在線性表中的位置
● 線性表中n爲表長,其中n>=0
● 長度等於零時稱爲空表,通常記爲:L=( )
● 將元素ai-1成爲元素ai的直前驅,將元素ai+1成爲元素ai的直接後繼。
理解線性表的定義有以下要點
⑴ 序列——順序性:元素具有線性順序,第一個元素無前驅,最後一個元素無後繼,其他每個元素有且僅有一個前驅和一個後繼。
⑵ 有限——有限性:元素個數有限,在計算機中處理的對象都是有限的。
⑶ 相同類型——相同性:元素取自於同一個數據對象,這意味着每個元素佔用相同數量的存儲單元。
⑷ 元素類型不確定——抽象性:數據元素的類型是抽象的、不具體的,需要根據具體問題確定。
線性表的操作
建表(初始化)、求表長、查找、插入、刪除
線性表的存儲結構
包括順序存儲結構和鏈式存儲結構兩種,因此可以將線性表分爲順序表和鏈表兩大類
本篇博文我們來一起學習下順序存儲結構。
順序存儲結構
定義:線性表在順序存儲形式下構成的表。
線性表的順序存儲結構具有兩個基本特點
① 線性表中所有元素所佔的存儲空間是連續的;
② 線性表中各數據元素在存儲空間中是按邏輯順序依次存放的。
假設線性表中的第一個數據元素的存儲地址(即首地址)爲 ADR(a1),每一個數據元素佔k個字節,則線性表中第i個元素ai在計算機存儲空間中的存儲地址爲:ADR(ai)=ADR(a1)+(i-1)k
例如:長度爲n的線性表在計算機中的順序存儲結構
在程序設計語言中,通常定義一個一維數組來表示線性表的順序存儲空間。數組需要根據情況預設足夠的大小,同時還需要一個變量指出線性表在數組中的當前狀況,如元素個數或最後一個元素在數組中的位置等。這兩方面的信息共同描述一個順序表,可將它們封裝在一起。
順序存儲結構的優缺點
優點:查詢很快
缺點:插入和刪除效率慢
在Java中,我們常見具有代表性的順序存儲結構有很多,這裏我們以ArrayList爲例,進行分析,看看它內部是如何實現順序存儲結構的,由於源碼過長,這裏我們重點分析增刪改查和迭代器方法。
構造方法
ArrayList提供了三個構造方法,可以構造一個默認初始容量爲12的空列表和構造一個指定初始容量的空列表以及構造一個包含指定collection的元素的列表,這些元素按照該集合的迭代器返回它們的順序排列。
public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess {
/**
* 最小容量值,Java中爲10,android中爲12
*/
private static final int MIN_CAPACITY_INCREMENT = 12;
/**
* 數組元素的長度
*/
int size;
/**
* ArrayList是基於數組的方式實現的
*/
transient Object[] array;
/**
* 創建一個指定帶容量大小的ArrayList
*/
public ArrayList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}
/**
* 創建一個無參構造的ArrayList
*/
public ArrayList() {
array = EmptyArray.OBJECT;
}
/**
* 創建一個包含collection的ArrayList
*/
public ArrayList(Collection<? extends E> collection) {// java的多態性
if (collection == null) {
throw new NullPointerException("collection == null");
}
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);
a = newArray;
}
array = a;
size = a.length;
}
添加方法
/**
* 添加方法,添加到列表的尾部
*/
@Override public boolean add(E object) {
Object[] a = array;// 將array賦值給一個局部數組
int s = size;// 用局部的s獲取長度
if (s == a.length) {// 如果現在的長度等於數組array的長度,那麼空間滿了,需要聲明一個新數組
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];// s<6?12:6
System.arraycopy(a, 0, newArray, 0, s);// 把原來的數組拷貝到新的數組中來
array = a = newArray;
}
a[s] = object;// 把元素添加進來
size = s + 1;// 長度+1
modCount++;// 計量器,只要數組中元素動一下,它就+1
return true;
}
/**
* 添加方法,添加到指定位置
*
* @param index the index at which to insert the object.
* @param object the object to add.
* @throws IndexOutOfBoundsException
* when {@code location < 0 || location > size()}
*/
@Override public void add(int index, E object) {
Object[] a = array;
int s = size;
if (index > s || index < 0) {
throwIndexOutOfBoundsException(index, s);
}
// 當數組長度容量足夠時,執行System.arraycopy方法實現數組的複製
if (s < a.length) {
System.arraycopy(a, index, a, index + 1, s - index);
} else {// 當數組容量不足時,進行擴容
// assert s == a.length;
// 創建新數組
Object[] newArray = new Object[newCapacity(s)];
// 將數據拷貝到新數組中,並移動位置
System.arraycopy(a, 0, newArray, 0, index);
System.arraycopy(a, index, newArray, index + 1, s - index);
array = a = newArray;
}
a[index] = object;
size = s + 1;
modCount++;
}
/**
* 添加方法,將容器中所有元素添加到此列表的尾部
* Adds the objects in the specified collection to this {@code ArrayList}.
* @param collection the collection of objects.
* @return {@code true} if this {@code ArrayList} is modified, {@code false}
* otherwise.
*/
@Override public boolean addAll(Collection<? extends E> collection) {
Object[] newPart = collection.toArray();
int newPartSize = newPart.length;
if (newPartSize == 0) {
return false;
}
Object[] a = array;
int s = size;
int newSize = s + newPartSize; // If add overflows, arraycopy will fail
if (newSize > a.length) {
int newCapacity = newCapacity(newSize - 1); // ~33% growth room
Object[] newArray = new Object[newCapacity];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
System.arraycopy(newPart, 0, a, s, newPartSize);
size = newSize;
modCount++;
return true;
}
刪除方法
/**
* 刪除列表中指定位置上的元素
* @param index the index of the object to remove.
* @return the removed object.
* @throws IndexOutOfBoundsException
* when {@code location < 0 || location >= size()}
*/
@Override public E remove(int index) {
Object[] a = array;
int s = size;
if (index >= s) {
throwIndexOutOfBoundsException(index, s);
}
@SuppressWarnings("unchecked") E result = (E) a[index];
// 將刪除位置之後的元素向前挪動一個位置
System.arraycopy(a, index + 1, a, index, --s - index);
// 將數組末尾置空
a[s] = null;
size = s;
modCount++;
return result;
}
// 刪除列表中首次出現的指定元素(如果存在)
@Override public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
修改方法
/**
* 修改方法
*
* @param index the index at which to put the specified object.
* @param object the object to add.
* @return the previous element at the index.
* @throws IndexOutOfBoundsException
* when {@code location < 0 || location >= size()}
*/
@Override public E set(int index, E object) {
Object[] a = array;// 給一個下標index,用object修改
if (index >= size) {
throwIndexOutOfBoundsException(index, size);
}
@SuppressWarnings("unchecked") E result = (E) a[index];
a[index] = object;
return result;
}
查找方法
/**
* 查找是否包含某個元素
*
* @param object the object to search for.
* @return {@code true} if {@code object} is an element of this
* {@code ArrayList}, {@code false} otherwise
*/
@Override public boolean contains(Object object) {
Object[] a = array;// 聲明一個Object數組
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {// 全部遍歷一遍,找到元素返回true
if (object.equals(a[i])) {
return true;
}
}
} else {// 沒找到返回false
for (int i = 0; i < s; i++) {
if (a[i] == null) {
return true;
}
}
}
return false;
}
迭代器
/**
* 每個List都會有一個迭代器,裏面包含hasNext方法,remove方法,
*/
private class ArrayListIterator implements Iterator<E> {
/** Number of elements remaining in this iteration */
private int remaining = size;// 剩下來的數量,用全局變量數組的長度賦值
/** Index of element that remove() would remove, or -1 if no such elt */
private int removalIndex = -1;
/** The expected modCount value */
private int expectedModCount = modCount;
public boolean hasNext() {// 下面是否還有元素
return remaining != 0;
}
@SuppressWarnings("unchecked") public E next() {
ArrayList<E> ourList = ArrayList.this;
int rem = remaining;
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0) {// 沒有元素可以遍歷了
throw new NoSuchElementException();
}
remaining = rem - 1;
return (E) ourList.array[removalIndex = ourList.size - rem];
}
public void remove() {// 用迭代器進行刪除,實際上創建一個新數組,刪除然後進行copy
Object[] a = array;
int removalIdx = removalIndex;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (removalIdx < 0) {
throw new IllegalStateException();
}
System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
a[--size] = null; // Prevent memory leak
removalIndex = -1;
expectedModCount = ++modCount;
}
}
@Override public int hashCode() {
Object[] a = array;
int hashCode = 1;
for (int i = 0, s = size; i < s; i++) {
Object e = a[i];
hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
}
return hashCode;
}
// 判斷兩個對象是否相等
@Override public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof List)) {
return false;
}
List<?> that = (List<?>) o;
int s = size;
if (that.size() != s) {
return false;
}
Object[] a = array;
if (that instanceof RandomAccess) {
for (int i = 0; i < s; i++) {
Object eThis = a[i];
Object ethat = that.get(i);
if (eThis == null ? ethat != null : !eThis.equals(ethat)) {
return false;
}
}
} else { // Argument list is not random access; use its iterator
Iterator<?> it = that.iterator();
for (int i = 0; i < s; i++) {
Object eThis = a[i];
Object eThat = it.next();
if (eThis == null ? eThat != null : !eThis.equals(eThat)) {
return false;
}
}
}
return true;
}
其他方法
/**
* 清空ArrayList集合中所有元素,使用Arrays.fill方法,將其填充爲null
* @see #isEmpty
* @see #size
*/
@Override public void clear() {
if (size != 0) {
Arrays.fill(array, 0, size, null);
size = 0;
modCount++;
}
}
/**
* 克隆方法,由於ArrayList實現了Cloneable接口,所以能被克隆
*
* @return a shallow copy of this {@code ArrayList}
* @see java.lang.Cloneable
*/
@Override public Object clone() {
try {
ArrayList<?> result = (ArrayList<?>) super.clone();
result.array = array.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
/**
* 創建一個新的Object數組,把array中的元素拷貝到數組中,然後返回
* @return an array of the elements from this {@code ArrayList}
*/
@Override public Object[] toArray() {
int s = size;
Object[] result = new Object[s];
System.arraycopy(array, 0, result, 0, s);
return result;
}
通過源碼分析,我們可以看出ArrayList是基於數組實現的,是一個動態數組,其容量能自動增長;我們可以通過下標索引直接查找到指定位置的元素,因此查找效率高,但每次插入或刪除元素,就要大量地移動元素,因此插入刪除元素的效率低。