数据结构之线性表(又称动态数组)

一、明确内置数组及它的缺点

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];//因为要返回删除的数,所以得先记录删除的数,最后要returnfor(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());
	}
}

运行结果:
在这里插入图片描述

发布了162 篇原创文章 · 获赞 142 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44571270/article/details/103497174
今日推荐