java 队列与泛型——动态数组的实现原理
学习本文需要用到的知识:java数组
1、思路推理
众所周知,在很多编程语言中,数组的大小一旦确定,在定义后是不可以改变的。如果数组储存满了之后还想往数组里面储存元素,肯定不能接着用同一个数组。所以需要重新定义一个比原数组要大的数组,把原数组里的数据重新储存到新数组,再把新数据储存进去。代码如下:
public class MyArray {
//声明初始数组
private Object[] array;
//用于储存数组的大小
private int size;
//在对象被创建时创建数组,初始大小为0
public MyArray() {
array = new Object[0];
}
public boolean add(Object newPart) {
// 创建一个新数组
Object[] newArray = new Object[size + 1];
// 将原有的数组的内容赋给新数组
for (int i = 0; i < array.length; i++) {
newArray[i] = array[i];
}
//添加的数据写入新数组
newArray[size++] = newPart;
//将数组array指向newArray
array = newArray;
return true;
}
}
上述代码实现的MyArray对象可以实现储存任意个元素,但有两个明显的缺陷:
缺陷一:**可以在同一个MyArray对象里面储存任意的对象。**这貌似很不错,但在实际使用中,很多地方都只需要储存同一类的对象,用以上的方法无法判断所储存的元素是否是同一个类的对象。这就需要用到泛型。
泛型允许我们使用E、K、V等字母()来代替不确定的数据类型(注意:E、K、V等字母不属于数据类型,只是一个符号,不能用在数据类型需要确定的地方;这里的数据类型不包括基本数据类型,如果需要用到基本数据类型,则用基本数据类型对应的类来替代)。具体方法如下:
首先在类名后加“<>”,尖括号里用E代替用户需要使用的数据类型:
public class MyArray<E> {……………}
然后将类的成员里面,不确定的数据类型都用E替代,替代后完整代码如下:
public class MyArray<E> {
//声明初始数组
private Object[] array;
//用于储存数组的大小
private int size;
//在对象被创建时创建数组,初始大小为0
public MyArray() {
array= new Object[0];
}
public boolean add(E newPart) {
//创建一个新数组
Object[] newArray = new Object[size + 1];
//将原有的数组的内容赋给新数组
for(int i = 0; i < array.length; i++) {
newArray[i]= array[i];
}
//添加的数据写入新数组
newArray[size++]= newPart;
//将数组array指向newArray
array= newArray;
return true;
}
}
缺陷二:每储存一个数据就需要新定义一个数组,效率低下。我们可以给MyArray添加一个储存比例,让其初始化的时候有一定的大小(增长比例),等数据储存过量时再创建新数组,而且这个数组的大小要比原数组增加一定的大小(增长比例),而不是只增加一。另外,在存入数据时,需要先判断原数组里面是否有空值,有的话就将数据直接存入,没有再创建新数组。完整代码如下:
public class MyArray<E> {
private Object[] array;
private int size;
private int addScale = 5; // 设置增长比例,这里暂时设置为5
public MyArray() {
//初始大小由增长比例来确定
array = new Object[addScale];
}
public boolean add(E newPart) {
//检查之前数组是否有空的元素,如果有,则将添加内容补充到空元素
for (int i = 0; i < size; i++) {
if (array[i] == null) {
array[i] = newPart;
return true;
}
}
//创建一个新数组
// 按增长比例定义新数组,以提高效率
Object[] newArray = new Object[size + addScale];
//将原有的数组的内容赋给新数组
for (int i = 0; i < array.length; i++) {
newArray[i] = array[i];
}
//添加的数据写入新数组
newArray[size]= newPart;
size = size + addScale;
array= newArray;
return true;
}
2、其它方法的实现
(1)、在指定的位置添加新元素:
// 在索引为index的地方插入newPart
public boolean add(int index, E newPart) {
if(index < 0 || index > size)
return false;
if(index == size) {
add(newPart);
return true;
}
add((E) null);
for (int i = size - 1; i > index; i--) {
array[i]= array[i - 1];
}
array[index] = newPart;
return true;
}
(2)、往数组里添加数组:
// 往数组里添加数组
public void add(MyArray<E> e) {
for(int i = 0; i < e.size(); i++) {
if(e.get(i) != null) {
//利用已经定义的add函数
add((E)e.get(i));
}
}
}
(3)、在指定位置插入某个元素
// 在索引为index的地方插入newPart
public boolean add(int index, E newPart) {
if(index < 0 || index > size)
return false;
if(index == size) {
add(newPart);
return true;
}
add((E)null);
for (int i = size - 1; i > index; i--) {
array[i]= array[i - 1];
}
array[index] = newPart;
return true;
}
(4)、在指定位置插入数组:
// 将数组插入索引为index的元素前
public int add(int index, MyArray<E> e) {
int count = 0;
if(index < 0 || index > size)
return count;
for(int i = 0; i < e.size(); i++) {
//利用(3)里的函数来实现插入单个元素
add(index+i,(E)e.get(i));
count++;
}
return count;
}
(5)、将指定位置的元素替换成新元素:
// 将索引为index的数据替换为newPart
public boolean update(int index, E newPart) {
if(index < 0 || index >= size)
return false;
else{
array[index]= newPart;
return true;
}
}
(6)、将指定的第一个元素替换成新元素:
// 将第一个内容为e的元素替换为newPart
public boolean update(E e, E newPart) {
for (int i = 0; i < size; i++) {
if(array[i] == e) {
array[i] = newPart;
return true;
}
}
return false;
}
(7)、将指定的所有元素替换成新元素:
// 将所有内容为e的元素替换为newPart,返回值为替换的元素个数
public int updateAll(E e, E newPart) {
int count = 0;
for(int i = 0; i < size; i++) {
if(array[i] == e) {
array[i] = newPart;
count++;
}
}
return count;
}
(8)、移除第一个指定的元素:
// 如果元素deletePart存在,则删除第一个deletePart元素
public boolean remove(E deletePart) {
for(int i = 0; i < size; i++) {
if(get(i) == deletePart) {
array[i] = null;
complement(i);
return true;
}
}
return false;
}
// 将所有靠后的数据向前面为空的索引替换补全
public void complement(int index) {
for(int j = index; j < size - 1; j++) {
array[j] = array[j + 1];
}
size--;
}
(9)、执行指定索引的元素:
// 删除索引为index的元素,返回值为删除的元素
public E remove(int index) {
if(index < 0 || index > size) {
return null;
}
E removePart = (E) array[index];
array[index] = null;
complement(index);
return removePart;
}
// 将所有靠后的数据向前面为空的索引替换补全
public void complement(int index) {
for(int j = index; j < size - 1; j++) {
array[j] = array[j + 1];
}
size--;
}
(10)、移除所有指定的元素:
// 删除所有元素和deletePart相同的内容,返回值为更改的元素数量
public int removaAll(E deletePart) {
int count = 0;
for(int i = 0; i < size; i++) {
if(array[i] == deletePart) {
array[i] = null;
complement(i);
i--;
count++;
}
}
return count;
}
// 将所有靠后的数据向前面为空的索引替换补全
public void complement(int index) {
for(int j = index; j < size - 1; j++) {
array[j] = array[j + 1];
}
size--;
}
(11)、获取指定索引的元素:
// 获取索引为index的元素
public Object get(int index) {
return array[index];
}
(12)、获取数组的大小
// 获取数组的大小
public int size() {
return size;
}