1.向量介绍
计算机程序主要运行在内存中,而内存在逻辑上可以被看做是连续的地址。为了充分利用这一特性,在主流的编程语言中都存在一种底层的被称为数组(Array)的数据结构与之对应。在使用数组时需要事先声明固定的大小以便程序在运行时为其开辟内存空间;数组通过下标值计算出地址偏移量来对内部元素进行访问。
可以看到,原始的数组很基础,所以运行效率非常的高。但同时也存在着严重的问题:
1.由于数组的大小需要在创建时被固定下来,但大多数程序在编写时无法很好的预测到可能的数据量大小,因而也就无法在创建时设置合适的数组大小,过大则浪费内存空间;过小则会出现上溢,需要编程人员进行特别的处理。
2.访问数组时,很容易出现数组下标越界的情况。由于数组的访问是非常频繁的,因而在追求性能的语言中(如C语言),编译器都没有对数组下标越界进行额外的检查,当程序出现了意外的数组下标越界时,依然允许程序访问和修改数组外部的内存地址,这很容易造成古怪的,难以复现的bug。(Java,python等较为高级的语言为了安全起见,即使舍弃掉一定的性能也要对数组下标越界进行检查)。
针对上述问题,我们需要对原始的数组进行一定程度的封装,在不改变基本使用方式的前提下,使其在运行过程中能够针对所存储的数据量大小自适应的扩容;对数组下标的越界访问进行检查,同时提供一系列的常用接口供用户使用。
而这个基于数组封装之后的数据结构,我们一般称之为"向量(vector)"或者"顺序表(sequence list)"。
2.向量主要ADT接口介绍
由于是使用java作为实现的语言,因此在设计上参考了jdk自带的向量数据结构:java.util.ArrayList类。
1.size()
接口定义:int size();
接口描述:返回当前列表中元素的个数。
2.isEmpty()
接口定义:boolean isEmpty();
接口描述:如果当前列表中元素个数为0,返回true;否则,返回false。
3.indexOf()
接口定义:int indexOf(E e);
接口描述:判断元素"e"是否存在于列表中
4.contains()
接口定义:boolean contains(E e);
接口描述: 判断元素"e"是否存在于列表中
5.add()
接口定义:boolean add(E e);
接口描述:在列表的最后插入元素"e"。
接口定义:void add(int index,E e);
接口描述:在列表的下标为"index"位置处插入元素"e"。
6.remove()
接口定义:boolean remove(E e);
接口描述:从列表中找到并且移除"e"对象,找到并且成功移除返回true;否则返回false。
接口定义:E remove(int index);
接口描述:移除列表中下标为"index"位置处的元素,返回被移除的元素。
7.set()
接口定义:E set(int index,E e);
接口描述:将列表中下标为"index"位置处的元素替代为"e",返回被替代的元素。
8.get()
接口定义:E get(int index);
接口描述:返回列表中下标为"index"位置处的元素。
3.向量实现细节
3.1 向量属性
向量作为数组的进一步封装,内部持有着一个数组,首先我们有以下属性:
public class ArrayList implements List {
/**
* 内部封装的数组 */
private Object[] elements; /**
* 线性表默认的容量大小
* */
private static final int DEFAULT_CAPACITY = 16; /**
* 扩容翻倍的基数
* */
private static final double EXPAND_BASE = 1.5; /**
* 内部数组的实际大小
* */
private int capacity; /**
* 当前线性表的实际大小
* */
private int size; //=================================================构造方法======================================================
/**
* 默认的无参构造方法
* */
public ArrayList() {
size = 0; //:::设置数组大小为默认
elements = new Object[DEFAULT_CAPACITY];
} /**
* 设