一、顺序表的优缺点
顺序表是用数组来实现,其优缺点如下:
(一)优点:
get和set所花费的时间短,时间复杂度为O(1)。
(二)缺点:
add和remove所花费的时间长,最坏的情况是在0位置进行,时间复杂度为O(N);最好的情况是在末端进行,时间复杂度为O(1)。
二、线性结构的基本操作
一般地有add、remove、get、set、size、isEmpty、indexOf、clear、trimToSize、ensureCapaticy。
(一)成员变量
先来看看成员变量先。
/**
* 数组默认初始化大小
*/
private final static int DEFAULT_CAPACITY = 10;
/**
* 用于存放元素的数组
*/
private T[] mArray;
/**
* 当前有效元素的个数
*/
private int mCurrentSize;
/**
* 记录改动过的次数
*/
private int mModCount;
(二)add
要往List中添加一个元素,后面的元素依次向后移动一个位置,腾出一个位置出来,再把元素插入到这个腾出来的位置中,如果把元素添加在List末端则不需要移动元素,直接插入到末端,如果添加的时候空间不够得动态申请更大的空间才能存放;下图是把S元素添加到List中第三个位置的示意图(用画图工具画的,粗糙了点,随便吐槽),未添加之前List的有效元素有5个,第一步是把最后一个元素(索引为4的T)往后移动一个位置(索引为5的位置),第二步是把索引3的元素(R)往后移动到索引为4的位置,第三步是把索引为2的元素(E)往后移动到索引为3的位置,最后把S元素添加到索引为2的位置,添加后List中就有6个元素;举个栗子:可以想象成排队,小明非要插队进来,后面的人就要往后退一步,从最后面的人开始退。
/**
* 添加元素到表末尾
* @param t 要添加的元素
* @return 添加是否成功
*/
public boolean add(T t) {
add(size(), t);
return true;
}
/**
* 添加元素到指定位置
* @param pIndex 指定的位置
* @param t 要添加的元素
*/
public void add(int pIndex, T t) {
if (pIndex < 0 || pIndex > size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
//检查空间是否足够
if (size() == mArray.length) {
//加1是保证当size为0的情况也能扩容
ensureCapaccity(size() * 2 + 1);
}
for (int i = size(); i > pIndex; i--) {
mArray[i] = mArray[i-1];
}
mArray[pIndex] = t;
mCurrentSize++;
mModCount++;
}
(三)remove
删除List中的一个元素,一般要把要删除的元素取出来存放到一个位置,这个被删除元素后面的元素依次先前移动,如果是删除List中最后一个元素则不需要移动元素,最后把被删除的元素返回,下图是把索引为2的M元素从List中删除,第一步是把要删除的元素先存到另一个地方,第二步是把索引为3的元素(O)往前移动到索引为2的位置,第三步是把索引为4的元素(V)向前移动到索引为3的位置,第四步是把索引为5的元素(E)向前移动到索引为4的位置,然后把索引为5的位置置空,最后把M元素返回;举个栗子:可以想象成排队,老师看不惯小明插队行为,来叫小明去办公室一趟,先把小明揪出来,然后后面的人往前移动,最后小明被拖到办公室。
/**
* 删除在表中指定位置的元素
* @param pIndex 元素的位置
* @return 被删除的元素
*/
public T remove(int pIndex) {
if (pIndex < 0 || pIndex >= size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
T _RmElement = mArray[pIndex];
for(int i = pIndex; i < size() - 1; i++) {
mArray[i] = mArray[i + 1];
}
mArray[size() - 1] = null;
mCurrentSize--;
mModCount++;
return _RmElement;
}
(四)get
获取在表中指定位置的元素,下图是获取索引为3的元素;举个栗子:可以想想比如老师想看看队伍中排在第四个位置的是不是小明。
/**
* 得到在表中指定位置的元素
* @param pIndex 指定的位置
* @return 指定位置的元素
*/
public T get(int pIndex) {
if (pIndex < 0 || pIndex >= size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
return mArray[pIndex];
}
(五)set
设置在表中指定位置的元素,下图是把索引为3的e设置为E;举个栗子:可以想象成在排好队后,这时小刚才过来,老师看小明不顺眼把小明揪了出来,让小刚排在原来小明排的位置,小明不用排队。
/**
* 设置在表中指定位置的元素
* @param pIndex 指定的位置
* @param t 要设置的元素
*/
public void set(int pIndex, T t) {
if (pIndex < 0 || pIndex >= size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
mArray[pIndex] = t;
mModCount++;
}
(六)size
获取表的长度,下图表的长度就为4,;举个栗子:想象下在排队,老师想知道有多少个人在排。
/**
* 得到当前表的大小
* @return 返回当前表的大小
*/
public int size() {
return mCurrentSize;
}
(七)isEmpty
判断当前表是不是空的;举个栗子:想象下比如老师召集班里的同学去排队,老师想知道是不是没有人去排队。
/**
* 表当前是否是空表
* @return 是否是空表
*/
public boolean isEmpty() {
return size() == 0;
}
(八)indexOf
查找某个元素在表中的索引位置,下图是查找D在表中的索引位置;举个栗子:比如在排好的队里老师想知道小明站在哪个位置,以方便联系到他。
/**
* 查找指定元素在表中的位置
* @param o 要找的元素
* @return 元素在表中的位置
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size(); i++)
if (mArray[i]==null)
return i;
} else {
for (int i = 0; i < size(); i++)
if (o.equals(mArray[i]))
return i;
}
return -1;
}
(九)clear
清空表中所有元素,下图就是清空的示意图;举个栗子:又是排队,老师说解散。
/**
* 清空所有元素 并把数组恢复到初始大小
*/
public void clear() {
mCurrentSize = 0;
ensureCapaccity(DEFAULT_CAPACITY);
mModCount++;
}
(十)trimToSize
保留有效元素长度,下图有效元素到索引为3(包含3)的位置,4到7的就不要了;举个栗子:开会搬来凳子,本来这一列有8张凳子的,但只坐了4个人,后面的4张凳子就被收走了。
/**
* 只保留有效元素长度
*/
public void trimToSize() {
ensureCapaccity(size());
mModCount++;
}
(十一)ensureCapaticy
动态扩容数组到指定长度,如果指定的长度小于当前长度就不处理,下图就是把一个长度为4的数组扩容到长度为7;举个栗子:开会搬凳子排队,目前只有4张凳子,但有8个人要排,所以至少要有8张凳子。
/**
* 用于扩容数组保障数组容量足够使用
* @param pNewCapacity 新的数组容量大小
*/
private void ensureCapaccity(int pNewCapacity) {
if (pNewCapacity < mCurrentSize) {
return;
}
T[] _OldArr = mArray;
mArray = (T[]) new Object[pNewCapacity];
for(int i = 0; i < size(); i++) {
mArray[i] = _OldArr[i];
}
}
三、完整的代码
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyArrayList<T> implements Iterable<T> {
/**
* 数组默认初始化大小
*/
private final static int DEFAULT_CAPACITY = 10;
/**
* 用于存放元素的数组
*/
private T[] mArray;
/**
* 当前有效元素的个数
*/
private int mCurrentSize;
/**
* 记录改动过的次数
*/
private int mModCount;
public MyArrayList() {
clear();
}
/**
* 用于扩容数组保障数组容量足够使用
* @param pNewCapacity 新的数组容量大小
*/
private void ensureCapaccity(int pNewCapacity) {
if (pNewCapacity < mCurrentSize) {
return;
}
T[] _OldArr = mArray;
//泛型数组不能new只能强制转化
mArray = (T[]) new Object[pNewCapacity];
for(int i = 0; i < size(); i++) {
mArray[i] = _OldArr[i];
}
}
/**
* 得到当前表的大小
* @return 返回当前表的大小
*/
public int size() {
return mCurrentSize;
}
/**
* 清空所有元素 并把数组恢复到初始大小
*/
public void clear() {
mCurrentSize = 0;
ensureCapaccity(DEFAULT_CAPACITY);
mModCount++;
}
/**
* 表当前是否是空表
* @return 是否是空表
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* 查找指定元素在表中的位置
* @param o 要找的元素
* @return 元素在表中的位置
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size(); i++)
if (mArray[i]==null)
return i;
} else {
for (int i = 0; i < size(); i++)
if (o.equals(mArray[i]))
return i;
}
return -1;
}
/**
* 只保留有效元素
*/
public void trimToSize() {
ensureCapaccity(size());
mModCount++;
}
/**
* 设置在表中指定位置的元素
* @param pIndex 指定的位置
* @param t 要设置的元素
*/
public void set(int pIndex, T t) {
if (pIndex < 0 || pIndex >= size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
mArray[pIndex] = t;
mModCount++;
}
/**
* 得到在表中指定位置的元素
* @param pIndex 指定的位置
* @return 指定位置的元素
*/
public T get(int pIndex) {
if (pIndex < 0 || pIndex >= size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
return mArray[pIndex];
}
/**
* 添加元素到表末尾
* @param t 要添加的元素
* @return 添加是否成功
*/
public boolean add(T t) {
add(size(), t);
return true;
}
/**
* 添加元素到指定位置
* @param pIndex 指定的位置
* @param t 要添加的元素
*/
public void add(int pIndex, T t) {
if (pIndex < 0 || pIndex > size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
//检查空间是否足够
if (size() == mArray.length) {
//加1是保证当size为0的情况也能扩容
ensureCapaccity(size() * 2 + 1);
}
for (int i = size(); i > pIndex; i--) {
mArray[i] = mArray[i-1];
}
mArray[pIndex] = t;
mCurrentSize++;
mModCount++;
}
/**
* 删除在表中指定位置的元素
* @param pIndex 元素的位置
* @return 被删除的元素
*/
public T remove(int pIndex) {
if (pIndex < 0 || pIndex >= size()) {
throw new ArrayIndexOutOfBoundsException(pIndex);
}
T _RmElement = mArray[pIndex];
for(int i = pIndex; i < size() - 1; i++) {
mArray[i] = mArray[i + 1];
}
mArray[size() - 1] = null;
mCurrentSize--;
mModCount++;
return _RmElement;
}
/**
* 获取迭代器
*/
@Override
public Iterator<T> iterator() {
return new ArrayListIterator();
}
/**
* 迭代器类
*
*/
private class ArrayListIterator implements Iterator<T> {
/**
* 当前索引位置
*/
private int mIndex;
/**
* 用于判断表是否被修改过,如果不等于mModCount说明被修改过
*/
private int mExpectedModCount = mModCount;
/**
* 用于判断元素能否被删除
*/
private boolean mCanRemove = false;
/**
* 是否有下一个元素
*/
@Override
public boolean hasNext() {
return mIndex < size();
}
/**
* 下一个元素
*/
@Override
public T next() {
if (checkIsModify()) {
throw new ConcurrentModificationException();
}
if (!hasNext()) {
throw new NoSuchElementException();
}
T t = get(mIndex++);
mCanRemove = true;
return t;
}
/**
* 删除元素,必须在next()方法执行后操作
*/
@Override
public void remove() {
if (checkIsModify()) {
throw new ConcurrentModificationException();
}
if (!mCanRemove) {
throw new IllegalStateException();
}
MyArrayList.this.remove(--mIndex);
mExpectedModCount++;
mCanRemove = false;
}
/**
* 检查List是否被修改过
* @return 被修改过返回true 否则返回false
*/
private boolean checkIsModify() {
return mExpectedModCount != mModCount;
}
}
}