02-动态数组ArrayList实现

1.数据结构

1.什么是数据结构?

  • 数据结构式计算机存储数据,组织数据的方式。

2.常见的数据结构

  • 线性结构:线性表(数组、链表、栈、队列、哈希表)
  • 树形结构:二叉树,AVL树,红黑树,B树,堆,Trie,哈弗曼,并查集
  • 图形结构:邻接矩阵,邻接表。

3.在实际应用中,根据使用场景来选择最合适的数据结构。

2.线性表概述

线性表是具有n个相同类型元素的有限序列(n >= 0),在逻辑上具有索引和数据部分:是否能通过索引能直接访问到数据部分,要看具体的线性表类型(典型代表:数组和链表)。

在这里插入图片描述

常见的概念:

  • a1是首节点(首元素),an是尾节点(尾元素)。
  • a1是a2的前驱,a2是a1的后继。

常见的线性表有:

  • 数组
  • 链表
  • 队列
  • 哈希表(散列表)

3.线性表:数组Array

3.1.概述

1.数组是一种书信存储的线性表,所有元素的内存地址是连续的。

2.数组内存分配

我们看一下这句Java代码中,new的int类型数组的内存分配情况:

	int[] array = new int[]{
    
    11, 22, 33};

局部变量array放在栈空间中,堆空间中new开辟出来3个int大小的连续内存空间:
在这里插入图片描述
3.Java数组的缺点

在很多编程语言中,数组都有一个明显的缺陷。比如上面的语句:

	int[] array = new int[]{
    
    11, 22, 33};

数组array初始化后,其容量大小就是固定好了,以后无法动态修改数组容量。数组元素存满后,就无法再存放数据了,因为不能扩大数组的大小了。

4.实际开发中,我们更希望数组的容量是可以动态改变的,所以我们会自己实现一个可以动态扩容的数组。

那么我们需要考虑一个问题:我们自己实现的可扩容的数组需要提供哪些API接口给别人调用?

3.2.动态数组接口设计

要满足一个动态数组的功能,那么最起码要提供这些公共的接口:

在这里插入图片描述

public class ArrayList {
    
    
	
	/**
	 * 清除所有元素
	 */
	public void clear() {
    
    

	}

	/**
	 * 元素的数量
	 * @return
	 */
	public int size() {
    
    
		return 0;
	}

	/**
	 * 是否为空
	 * @return
	 */
	public boolean isEmpty() {
    
    
		return false;
	}

	/**
	 * 是否包含某个元素
	 * @param element
	 * @return
	 */
	public boolean contains(int element) {
    
    
		return false;
	}

	/**
	 * 添加元素到尾部
	 * @param element
	 */
	public void add(int element) {
    
    
		
	}

	/**
	 * 获取index位置的元素
	 * @param index
	 * @return
	 */
	public int get(int index) {
    
    
		return 0;
	}

	/**
	 * 设置index位置的元素
	 * @param index
	 * @param element
	 * @return 原来的元素ֵ
	 */
	public int set(int index, int element) {
    
    
		return 0;
	}

	/**
	 * 在index位置插入一个元素
	 * @param index
	 * @param element
	 */
	public void add(int index, int element) {
    
    

	}

	/**
	 * 删除index位置的元素
	 * @param index
	 * @return
	 */
	public int remove(int index) {
    
    
		return 0;
	}

	/**
	 * 查看元素的索引
	 * @param element
	 * @return
	 */
	public int indexOf(int element) {
    
    
		return 0;
	}
}

3.3.动态数组的设计实现

1.成员变量

	/**
	 * 元素的数量个数:注意不是数组的容量
	 */
	private int size;
	
	/**
	 * 引用:指向一个int数组
	 */
	private int[] elements;
	
	//默认的数组容量大小
	private static final int DEFAULT_CAPACITY = 10;
	
	//-1下标:代表没有这个元素
	private static final int ELEMENT_NOT_FOUND = -1;

2.构造方法:

	/**
	 * 构造1:不指定数组容量:默认容量是10
	 */
	public ArrayList() {
    
    
		//调用有参构造
		this(DEFAULT_CAPACITY);
	}
	
	/**
	 * 构造2:我的设计
	 * 		指定初始容量,如果参数小于默认容量时用默认容量
	 * @param capacity
	 */
	public ArrayList(int capacity) {
    
    
		capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;
		elements = new int[capacity];
	}

3.方法实现

  1. size()
	/**
	 * 元素的数量
	 * @return 返回size成员变量
	 */
	public int size() {
    
    
		return size;
	}

2.isEmpty()

	/**
	 * 是否为空
	 * @return
	 */
	public boolean isEmpty() {
    
    
		return size == 0;
	}

3.get(int index)

	/**
	 * 获取index位置的元素
	 * @param index
	 * @return
	 */
	public int get(int index) {
    
    
		if(index < 0 || index > size) {
    
    
			throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
		}
		return elements[index];
	}

4.set(int index, int element)

	/**
	 * 设置index位置的元素
	 * @param index
	 * @param element
	 * @return 原来的元素ֵ
	 */
	public int set(int index, int element) {
    
    
		if(index < 0 || index > size) {
    
    
			throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
		}
		int old = elements[index];
		elements[index] = element;
		return old;
	}

5.indexOf(int element)

	/**
	 * 查看元素的索引
	 * @param element
	 * @return
	 */
	public int indexOf(int element) {
    
    
		for (int i=0; i < size; i++) {
    
    
			if (elements[i] == element) return i;
		}
		return ELEMENT_NOT_FOUND;
	}

6.contains(int element)

	/**
	 * 是否包含某个元素
	 * @param element
	 * @return
	 */
	public boolean contains(int element) {
    
    
		return indexOf(element) != ELEMENT_NOT_FOUND;
	}
  1. clear()
    直接将size设为0,不做具体的“清空”操作,这样反而可能更加高效。因为原数组可能还需要add元素,那么直接覆盖原来废弃的值即可。这样就不用有清空操作,重新申请空间等操作。
    框架设计者,自己来设计功能背后的逻辑。只要对外“语义”是通顺的,背后的设计逻辑,由我们自己来决定(考虑到性能方面)。
	/**
	 * 清除所有元素
	 */
	public void clear() {
    
    
		size = 0;
	}

8.add(int element):暂时不考虑扩容的问题

	/**
	 * 添加元素到尾部
	 * @param element
	 */
	public void add(int element) {
    
    
		elements[size++] = element;
	}

9.重写toString():用来打印

	@Override
	public String toString() {
    
    
		StringBuilder string = new StringBuilder();
		string.append("[");
		for (int i = 0; i < size; i++) {
    
    
			if(i != 0)
				string.append(", ");
				
			string.append(elements[i]);
		}
		string.append("]");
		return string.toString();
	}

10.remove(int index):

删除index位置的元素:具体操作是index后面的位置前移,覆盖掉要删除的元素。所以数组的删除和插入操作很麻烦。

	/**
	 * 删除index位置的元素:
	 * 	具体操作是index后面的位置前移,覆盖掉要删除的元素。
	 * 	所以数组的删除操作很麻烦。
	 * @param index
	 * @return 被删除的元素
	 */
	public int remove(int index) {
    
    
		if(index < 0 || index > size) {
    
    
			throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
		}
		int old = elements[index];
		//index后的元素前移
		while (index < size - 1) {
    
    
			elements[index] = elements[index+1];
			index++;
		}
		size--;
		return old;
	}

11.复用出来3个方法

	/**
	 * 检查索引是否越界
	 * @param index
	 */
	private void rangeCheck(int index) {
    
    
		if(index < 0 || index >= size) {
    
    
			outOfBounds(index);
		}
	} 
	/**
	 * 插入时:检查索引是否越界
	 * @param index
	 */
	private void rangeCheckForAdd(int index) {
    
    
		if(index < 0 || index > size) {
    
    
			outOfBounds(index);
		}
	} 
	/**
	 * 打印索引越界异常信息
	 * @param index
	 */
	private void outOfBounds(int index) {
    
    
		throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
	}

12.add(int index, int element):在指定位置插入元素

	/**
	 * 在index位置插入一个元素:原来该位置的元素及后面的元素后移
	 * @param index
	 * @param element
	 */
	public void add(int index, int element) {
    
    
		rangeCheckForAdd(index);
		
		for(int i = size; i > index; i--) {
    
    
			elements[i] = elements[i-1]; 
		}
		elements[index] = element;
		size++;
	}

3.4.动态扩容

1.实现分析

当数组Array的容量已经填满了,一般数组就不再允许向其中添加,插入元素。如果想要实现,即使数组满了也能向其中插入元素,那么此时就需要对数组进行扩容了。

扩容的实现一般是申请一个更大的内存,将原来的元素赋值到这个新的内存中,原来的引用指向新申请的内存地址。

在这里插入图片描述

2.哪些方法需要考虑扩容

重构一下add(int element)和add(int index, int element):

  • 既然add(int element)是向尾部插入元素,那么直接调用add(size, element即可,这样只要更新,扩展add(int index, int element)方法,add(int element)机会随之改变。
  • 重构代码:
	/**
	 * 添加元素到尾部
	 * @param element
	 */
	public void add(int element) {
    
    
		add(size, element);
	}
	/**
	 * 在index位置插入一个元素:原来该位置的元素及后面的元素后移
	 * @param index
	 * @param element
	 */
	public void add(int index, int element) {
    
    
		rangeCheckForAdd(index);
		
		for(int i = size; i > index; i--) {
    
    
			elements[i] = elements[i-1]; 
		}
		elements[index] = element;
		size++;
	}
  • 加上扩容机制:
	/**
	 * 确保数组容量能满足插入,新增操作:
	 * 	如果满足:不做任何操作
	 * 	否则:扩容为原来的1.5倍
	 * @param capacity:插入,添加元素时,需要的最小容量
	 */
	private void ensureCapacity(int capacity) {
    
    
		int oldCapacity = elements.length;
		//如果旧容量>=需要的最小容量,说明容量足够,可以插入
		if (oldCapacity >= capacity) return;
		//否则就需要扩容:位运算效率比算数运算高很多
		int newCapacity = oldCapacity + (oldCapacity >> 1);
		//复制元素到新扩容出来的数组中
		int[] newElememts = new int[newCapacity];
		for (int i = 0; i < size; i++) {
    
    
			newElememts[i] = elements[i];
		}
		elements = newElememts;
		System.out.println("扩容了: "+"旧容量,"+oldCapacity+"新容量,"+newCapacity);
	}
	
	/**
	 * 在index位置插入一个元素:原来该位置的元素及后面的元素后移
	 * @param index
	 * @param element
	 */
	public void add(int index, int element) {
    
    
		rangeCheckForAdd(index);
		//确保容量足够:查看容量是否足够,不够就扩容
		ensureCapacity(size + 1);
		for(int i = size; i > index; i--) {
    
    
			elements[i] = elements[i-1]; 
		}
		elements[index] = element;
		size++;
	}

4.动态数组实现的完善

4.1.泛型

泛型技术可以让动态数组更加通用,可以存放任何数据类型。

类定义时,指明元素是泛型,其它用到元素类型的地方都改成泛型:

public class ArrayList<E> {
    
    
	/**
	 * 构造2:指定初始容量,如果参数小于默认容量时用默认容量
	 * @param capacity
	 */
	public ArrayList(int capacity) {
    
    
		capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;
		elements = new E[capacity];//Cannot create a generic array of E
	}

	/**
	 * 确保数组容量能满足插入,新增操作:
	 * @param capacity:插入,添加元素时,需要的最小容量
	 */
	private void ensureCapacity(int capacity) {
    
    
		int oldCapacity = elements.length;
		if (oldCapacity >= capacity) return;

		int newCapacity = oldCapacity + (oldCapacity >> 1);
		E[] newElememts = new E[newCapacity]; //Cannot create a generic array of E
		for (int i = 0; i < size; i++) {
    
    
			newElememts[i] = elements[i];
		}
		elements = newElememts;
	}
}

注意上面有两个报错://Cannot create a generic array of E,即不能创建泛型数组,那么我们怎么解决呢?

  • 首先我们不能写死类型:因为每次创建的元素类型都是不固定的。
elements = new Person[capacity]; 
elements = new Car[capacity]; 
  • 那么我们要找一个能代表所有类型的类:Object,然后再强转:
elements = (E[])new Object[capacity];
E[] newElememts = (E[])new Object[newCapacity];

4.2.对象数组的内存管理

1.看一下这两个创建数组的语句在内存分配上的区别:

int[] array1 = new int[3];
Object[] array2 = new Object[7]
  • 语句1:因为int是固定的4个字节,所以会直接在堆内存中分配4 * 3个字节大小的内存空间。
    在这里插入图片描述
  • 语句2:如果创建的是一个对象(Object对象)数组,那么堆空间中申请的连续7块内存,存放的是地址值。即存放的是7个引用,这样更方便以后指向其他的对象(用到多态时),所以可以存放任何对象元素。
    在这里插入图片描述

2.clear()方法的含义和改进

clear()方法的含义:是清除元素,但是保留数组的“架子”,这样以后还能重复利用,直接添加元素覆盖。

	/**
	 * 清除所有元素
	 */
	public void clear() {
    
    
		size = 0;
	}

1.因为以前是new int[n]; 那么堆空间开辟的空间中直接存放的就是int值。设置size=0后,其他的方法用array试,语义上也是符合的。而且如果后面会用到这个空间,直接重复利用就好,不用再重新开辟了。
在这里插入图片描述

2.那么现在是泛型了,存储的是对象了,还能这样写吗?

要分析一下:对象数组对空间中存放的是地址值,而且地址值指向了对象元素。那么和以前的int[]数组相比,多出来了的是地址元素指向对象这个部分,其他还是一样的。

那么当我们不想要这些对象,想要清空时,如果还是直接size=0的话,虽然对使用者在使用时来说语义上没有影响,但是这些对象还是存在的,直到地址元素指向一个新的对象,此时原来的对象才算销毁。

考虑到内存利用的情况:我们的clear()方法要主动销毁这些对象。

在这里插入图片描述
3.所以还是和new int[n];的情况一样,我们保留数组内存,清除对象内存。

存放地址值的内存可以重复利用的,还可以被覆盖为其他的对象地址,所以不能直接将数组引用置为null:elements = null; 这样就会导致整个数组内存都销毁了,即存放地址值的堆内存空间没有了指向,也会被销毁。下次再用的话,只能重新开辟。

能循环利用的留下,不能循环利用的滚蛋。
在这里插入图片描述
4.新的clear()

	/**
	 * 清除所有元素
	 */
	public void clear() {
    
    
		for (int i = 0; i < size; i++) {
    
    
			elements[i] = null;
		}
		size = 0;
	}
  1. 测试
public class Person {
    
    
	/**
	 * 对象被销毁时:调用(写下遗言)
	 */
	@Override
	protected void finalize() throws Throwable {
    
    
		super.finalize();
		System.out.println("我得遗言:"+"我没了...");
	}
}	

	ArrayList<Person> list = new ArrayList<>();
	list.add(new Person(12, "aaaa", new Date()));
	list.add(new Person(13, "bbbb", new Date()));
	list.add(new Person(14, "cccc", new Date()));
	list.clear();
	//主动提示GC回收
	System.gc();

	//结果:
	我得遗言:我没了...
	我得遗言:我没了...
	我得遗言:我没了...

3.remove()方法改进

  1. 以前的remove(int index):删除位置后的元素前移后,最后一个元素没有处理

在这里插入图片描述

	public E remove(int index) {
    
    
		rangeCheck(index);
		
		E old = elements[index];
		//index后的元素前移
		while (index < size-1) {
    
    
			elements[index] = elements[index+1];
			index++;
		}
		size--;
		return old;
	}
  1. 改进:
	public E remove(int index) {
    
    
		rangeCheck(index);
		
		E old = elements[index];
		//index后的元素前移
		while (index < size-1) {
    
    
			elements[index] = elements[index+1];
			index++;
		}
		
		elements[--size] = null;
		return old;
	}
  1. remove(E element):删除指定元素
	/**
	 * 删除指定元素
	 * @param element
	 * @return
	 */
	public int remove(E element) {
    
    
		int index = indexOf(element);
		remove(indexOf(element));
		return index;
	}

4.3.equals

1.indexOf(E element)方法中涉及到对象的比较,那么就不能直接用“==” 。

“==”比较对象时比较的是两个对象的内存地址,我们在此处自己定义比较规则:两个对象的各个属性一样时,认为相等(即使不是同一个对象)。

假设一个类中重写了自己的比较规则equals,那么对象数组中比较这个对象元素时,也要调用equals方法。

如果Person类没有重写equals方法,ArrayList类indexO()方法仍然调用equals方法,那么会走Object中的equals方法,Object中的equals方法会调用"=="比较。

Integer等包装类,里面已经重写了equals方法,equals方法比较的是数值。

   //Person类中重写equals方法
   @Override
   public boolean equals(Object obj) {
    
    
   	Person person = (Person)obj;
   	return person.age == this.age && person.name == this.name;
   }

   /**
    * 查找元素的索引
    * @param element
    * @return
    */
   public int indexOf(E element) {
    
    
   	for (int i=0; i < size; i++) {
    
    
   		if (elements[i].equals(element)) return i;
   	}
   	return ELEMENT_NOT_FOUND;
   }

2.equals的进一步改进

当element[i]可能是Person对象,也可能是Integer对象时,那么每次调用Person的equals接收的obj实际类型可能根本就不是Person。

那么每次直接强转成Person,显然后报错: ClassCastException
在这里插入图片描述
改进代码:

	@Override
	public boolean equals(Object obj) {
    
    
		if(obj == null) return false;
		if(obj instanceof Person) {
    
    
			Person person = (Person)obj;
			return person.age == this.age && person.name == this.name;
		}
		//否则:接收的obj不是Person类型
		return false;
	}

4.4.null值处理,是否可以存储空数据

  1. 一个内部设计方面的问题:是否可以存储null数据。
	arrayList.add(null);
  1. 看一下add()方法,其实我们的实现默认是支持存null的
	public void add(int index, E element) {
    
    
		rangeCheckForAdd(index);
		//确保容量足够:查看容量是否足够,不够就扩容
		ensureCapacity(size + 1);
		for(int i = size; i > index; i--) {
    
    
			elements[i] = elements[i-1]; 
		}
		elements[index] = element;
		size++;
	}

在这里插入图片描述
3.那么此时就又会出现问题:indexOf(E element)

indexOf(E element)方法中的:elements[i].equals(element)判断语句,因为可能存在null元素,那么null.equals()就会报错。

	public int indexOf(E element) {
    
    
		for (int i=0; i < size; i++) {
    
    
			if (elements[i].equals(element)) return i;
		}
		return ELEMENT_NOT_FOUND;
	}

那么分成两种情况考虑:

	/**
	 * 查找元素的索引
	 * @param element
	 * @return
	 */
	public int indexOf(E element) {
    
    
		if(element == null) {
    
    
			for (int i=0; i < size; i++) {
    
    
				if (elements[i] == null) return i;
			}
		}else {
    
    
			//那么element一定不为null,放在前面调用equals绝对没问题
			for (int i=0; i < size; i++) {
    
    
				if (element.equals(elements[i])) return i;
			}
		}	
		return ELEMENT_NOT_FOUND;
	}

5.整个代码

最后贴一下我整个的代码:

5.1 Person

package com.mj;

import java.util.Date;

public class Person {
    
    
	private int age;
	private String name;
	private Date birthday;
	public Person(int age, String name, Date birthday) {
    
    
		this.age = age;
		this.name = name;
		this.birthday = birthday;
	}
	@Override
	public String toString() {
    
    
		return "Person [age=" + age + ", name=" + name + ", birthday=" + birthday + "]";
	}
	public int getAge() {
    
    
		return age;
	}
	public void setAge(int age) {
    
    
		this.age = age;
	}
	public String getName() {
    
    
		return name;
	}
	public void setName(String name) {
    
    
		this.name = name;
	}
	public Date getBirthday() {
    
    
		return birthday;
	}
	public void setBirthday(Date birthday) {
    
    
		this.birthday = birthday;
	}
	public Person() {
    
    
		super();
	}
	
	@Override
	public boolean equals(Object obj) {
    
    
		if(obj == null) return false;
		if(obj instanceof Person) {
    
    
			Person person = (Person)obj;
			return person.age == this.age && person.name == this.name;
		}
		//否则:接收的obj不是Person类型
		return false;
	}
	
	/**
	 * 对象被GC回收时:调用(写下遗言)
	 */
	@Override
	protected void finalize() throws Throwable {
    
    
		super.finalize();
		System.out.println("我的遗言:"+"我没了...");
	}
}

5.2.com.mj.ArrayList

package com.mj;

@SuppressWarnings("unchecked")
public class ArrayList<E> {
    
    
	
	/**
	 * 元素的数量
	 */
	private int size;
	/**
	 * 所有的元素
	 */
	private E[] elements;
	
	//默认的数组容量大小
	private static final int DEFAULT_CAPACITY = 10;
	
	//-1下标:代表没有这个元素
	private static final int ELEMENT_NOT_FOUND = -1;
	
	/**
	 * 检查索引是否越界
	 * @param index
	 */
	private void rangeCheck(int index) {
    
    
		if(index < 0 || index >= size) {
    
    
			outOfBounds(index);
		}
	} 
	
	/**
	 * 检查索引是否越界
	 * @param index
	 */
	private void rangeCheckForAdd(int index) {
    
    
		if(index < 0 || index > size) {
    
    
			outOfBounds(index);
		}
	} 
	
	/**
	 * 打印索引越界异常信息
	 * @param index
	 */
	private void outOfBounds(int index) {
    
    
		throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
	}
	
	/**
	 * 确保数组容量能满足插入,新增操作:
	 * 	如果满足:不做任何操作
	 * 	否则:扩容为原来的1.5倍
	 * @param capacity:插入,添加元素时,需要的最小容量
	 */
	private void ensureCapacity(int capacity) {
    
    
		int oldCapacity = elements.length;
		//如果旧容量>=需要的最小容量,说明容量足够,可以插入
		if (oldCapacity >= capacity) return;
		//否则就需要扩容:位运算效率比算数运算高很多
		int newCapacity = oldCapacity + (oldCapacity >> 1);
		//复制元素到新扩容出来的数组中
		E[] newElememts = (E[])new Object[newCapacity];
		for (int i = 0; i < size; i++) {
    
    
			newElememts[i] = elements[i];
		}
		elements = newElememts;
		System.out.println("扩容了: "+"旧容量,"+oldCapacity+"新容量,"+newCapacity);
	}
	
	/**
	 * 构造1:不指定数组容量:默认容量是10
	 */
	public ArrayList() {
    
    
		//调用有参构造
		this(DEFAULT_CAPACITY);
	}
	
	/**
	 * 构造2:指定初始容量,如果参数小于默认容量时用默认容量
	 * @param capacity
	 */
	public ArrayList(int capacity) {
    
    
		capacity = (capacity < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capacity;
		elements = (E[])new Object[capacity];
	}
	
	/**
	 * 清除所有元素
	 */
	public void clear() {
    
    
		for (int i = 0; i < size; i++) {
    
    
			elements[i] = null;
		}
		size = 0;
	}

	/**
	 * 元素的数量
	 * @return
	 */
	public int size() {
    
    
		return size;
	}

	/**
	 * 是否为空
	 * @return
	 */
	public boolean isEmpty() {
    
    
		return size == 0;
	}

	/**
	 * 是否包含某个元素
	 * @param element
	 * @return
	 */
	public boolean contains(E element) {
    
    
		return indexOf(element) != ELEMENT_NOT_FOUND;
	}

	/**
	 * 添加元素到尾部
	 * @param element
	 */
	public void add(E element) {
    
    
		add(size, element);
	}

	/**
	 * 获取index位置的元素
	 * @param index
	 * @return
	 */
	public E get(int index) {
    
    
		rangeCheck(index);
		
		return elements[index];
	}

	/**
	 * 设置index位置的元素
	 * @param index
	 * @param element
	 * @return 原来的元素ֵ
	 */
	public E set(int index, E element) {
    
    
		rangeCheck(index);
		
		E old = elements[index];
		elements[index] = element;
		return old;
	}

	/**
	 * 在index位置插入一个元素:原来该位置的元素及后面的元素后移
	 * @param index
	 * @param element
	 */
	public void add(int index, E element) {
    
    
		rangeCheckForAdd(index);
		//确保容量足够:查看容量是否足够,不够就扩容
		ensureCapacity(size + 1);
		for(int i = size; i > index; i--) {
    
    
			elements[i] = elements[i-1]; 
		}
		elements[index] = element;
		size++;
	}

	/**
	 * 删除index位置的元素:
	 * 	具体操作是index后面的位置前移,覆盖掉要删除的元素。
	 * 	所有数组的删除操作很麻烦。
	 * @param index
	 * @return 被删除的元素
	 */
	public E remove(int index) {
    
    
		rangeCheck(index);
		
		E old = elements[index];
		//index后的元素前移
		while (index < size-1) {
    
    
			elements[index] = elements[index+1];
			index++;
		}
		
		elements[--size] = null;
		return old;
	}
	
	/**
	 * 删除指定元素
	 * @param element
	 * @return
	 */
	public int remove(E element) {
    
    
		int index = indexOf(element);
		remove(indexOf(element));
		return index;
	}

	/**
	 * 查找元素的索引
	 * @param element
	 * @return
	 */
	public int indexOf(E element) {
    
    
		if(element == null) {
    
    
			for (int i=0; i < size; i++) {
    
    
				if (elements[i] == null) return i;
			}
		}else {
    
    
			//那么element一定不为null,放在前面调用equals绝对没问题
			for (int i=0; i < size; i++) {
    
    
				if (element.equals(elements[i])) return i;
			}
		}	
		return ELEMENT_NOT_FOUND;
	}
	
	@Override
	public String toString() {
    
    
		StringBuilder string = new StringBuilder();
		string.append("[");
		for (int i = 0; i < size; i++) {
    
    
			if(i != 0)
				string.append(", ");
			string.append(elements[i]);

		}
		string.append("]");
		return string.toString();
	}
	

}

猜你喜欢

转载自blog.csdn.net/tttxxl/article/details/115128062
02-