一、明确内置数组及它的缺点
1、Java内置数组的特点
(1)数组的长度一旦确定则不可更改
(2)数组只能存储同一类型的数据
(3)数组中每个存储空间大小一致且地址连续
(4)数组提供角标的方式访问元素
2、Java内置数组的潜在问题
(1)容量不够用时,怎么办?
(2)指定位置插入或删除元素,怎么做?
(3)数组对象只有length属性,够用不?
既然Java内置数组有如此大的缺陷,我们能不能根据它的缺点,重新构造一个数组,想着完善它的功能呢?答案肯定是可以的,动态数组就是这样一个改进版的数组。
二、如何封装动态数组
1、属性方面:
int size 数组的有效元素个数
int capacity 数组的最大容量data.length
E[] data 数据的存储容器
2、行为方面:
(1)增()
(2)删()
(3)改()
(4)查()
(5)其他()
我们又为什么要强调动态数组呢?
because of 动态数组是顺序存储结构的具体实现!
三、线性表的实现
1、线性表接口List的定义
为什么我们不直接定义一个线性表而是要先定义一个list呢?
答:这是为后面为写栈做准备,这些方法都是共有的,这样会大大减少代码重复!
2、线性表的顺序存储结构ArrayList的定义
注意:里面所有方法中,需要传参数的方法,一定要判断传进来的参数有没有特殊情况的,有的话需要throw错误!!
其中的添加元素和删除元素、清空表需要特别注意:
(1)增加元素要考虑什么时候扩容,扩多少?考虑size
(2)删除元素需要考虑什么时候缩容,缩多少?考虑size
(3)清空表直接缩容到最小,size为0
3、代码如下:
list.java:
public interface list <E> extends Iterable{ //使实现list的类可迭代
int getSize();
boolean isEmpty();
void add(int index,E e);
void addFirst(E e);
void addLast(E e);
E get(int index);
E getFirst();
E getLast();
void set(int index,E e);
boolean isContains(E e);
int find(E e);
E remove(int index);
E removeFirst();
E removeLast();
void removeElement(E e);
void clear();
}
ArrayList.java :
import java.util.Iterator;
public class ArrayList<E> implements list<E> { //支持范型
private E[]data; //因为是动态数组,所以其本质还是由数组实现的,也即是这个data实现的
private int size; //定义data数组的有效长度
private static int default_Capacity = 10; //定义data的默认最大容量。
------------------------------------------------------------------------
//三个构造函数的重载,对data数组进行针对性初始化。
public ArrayList(){
this(default_Capacity);
size=0;
}
public ArrayList(int Capacity){
if(Capacity<0) {
throw new IllegalArgumentException("容量不能为负数!");
}
data=(E[])(new Object[Capacity]);
size=0;
}
public ArrayList(E[]data){
/*
this(data.length);
这样写,如果data.length为null,就会出现空指针异常。
因为this语句调用必须写在第一句,这样的话,你还不能对这个null进行处理。
所以采用下面这个方法!
*/
data=(E[])(new Object[data.length]);
for(int i=0;i<data.length;i++) {
this.data[i]=data[i];
}
size=data.length;
}
----------------------------------------------------------------------
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
if(size==0) {
return true;
}else {
return false;
}
}
@Override
public void add(int index, E e) {
if(data.length==size) { //先要看看数组是否满了,是否需要扩容
resize(2*data.length);
}
if(index<0||index>=data.length) { //在可以插入元素的情况下讨论传进来的角标是否合理才是有意义的。
throw new IllegalArgumentException("数组越界!");
}
//当然我并不是一开始就知道这样写的,我一开始也是把上面这句放在第一句的,后面才慢慢修正的。
for(int i=size;i>=index;i--) {
data[i]=data[i-1];//i-1即是最后一个有效元素,把值给后面的size位置的无效元素,每个元素分别这样往后移。
}
data[index]=e;
size++;
}
private void resize(int newlength) {
E[]newdata=(E[])(new Object[newlength]);
for(int i=0;i<size;i++) { //你可以最开始写data.length,但是当缩容写完你这里就应该改成size了!增加代码的复用能力。
newdata[i]=data[i];
}
data=newdata; //偷天换日!
}
@Override
public void addFirst(E e) {
add(0,e);
}
@Override
public void addLast(E e) {
add(size,e);
}
@Override
public E get(int index) {
if(index<0||index>=data.length) {
throw new IllegalArgumentException("数组越界!");
}
return data[index];
}
@Override
public E getFirst() {
return get(0);
}
@Override
public E getLast() {
return get(size-1);
}
@Override
public void set(int index, E e) {
if(index<0||index>=data.length) {
throw new IllegalArgumentException("数组越界!");
}
data[index]=e;
}
@Override
public boolean isContains(E e) {
if(find(e)!=-1) {
return true;
}else {
return false;
}
//也可以直接:return find(e)!=-1
}
@Override
public int find(E e) {
for(int i=0;i<size;i++) {
if(data[i]==e) {
return i;
}
}
return -1;
}
@Override
public E remove(int index) {
if(index<0||index>=size) {
throw new IllegalArgumentException("数组越界!");
}
E e=data[index];//因为要返回删除的数,所以得先记录删除的数,最后要return
。
for(int i=index;i<size;i++) {
data[i]=data[i+1];
}
size--;
if(size<=data.length/4&&data.length/2>=10) {
resize(data.length/2);
}
return e;
}
@Override
public E removeFirst() {
return remove(0);
}
@Override
public E removeLast() {
return remove(size-1);
}
@Override
public void removeElement(E e) {
remove(find(e));
}
@Override
public void clear() {
size=0;
while(data.length!=10) {
resize(data.length/2);
}
}
//重点
public String toString() {
StringBuilder sb=new StringBuilder();
sb.append(String.format("ArrayList:%d/%d\n", size,data.length));
sb.append("[");
for(int i=0;i<size;i++) {
if(i==size-1) {
sb.append(data[i]);
}else {
sb.append(data[i]+",");
}
}
sb.append("]");
return sb.toString();
}
@Override
public Iterator<E> iterator() {//前面我们的list接口继承了Iterable,所以这里我们需要实现它里面的方法iterator。
return new ArrayListIterator();//专属于ArrayList的迭代器对象
}
private class ArrayListIterator implements Iterator{//这是一个内部类,为了后面得到一个专属于ArrayList的迭代器对象
private int index=-1;
@Override
public boolean hasNext() {//是否有下一个元素
if(index==size-1) {
return false;
}else {
return true;
}
}
@Override
public Object next() { //下一个元素是什么,返回它
index++;
return data[index];
}
}
}
测试类:
public class testArrayList {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
for(int i=1;i<=5;i++){
list.addFirst(i);
}
for(int i=6;i<=10;i++){
list.addLast(i);
}
System.out.println(list.toString());
list.add(5,5);
System.out.println(list.toString());
list.clear();
System.out.println(list.toString());
}
}
运行结果: