一、数组是一种常见的数据结构,用来存储同一类型值的集合java
二、数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致git
三、数组是一种顺序存储的线性表,全部元素的内存地址是连续的github
四、例如:new 一个int基本类型的数组array数组
int[] array = new int[]{11,22,33};
五、数组的优点与劣势数据结构
/** * 元素的数量 */ protected int size; /** * 数组全部元素及内存地址指向 */ private E[] elements;
图示结构:app
将动态数组与链表共同的属性与方法抽取出,做为抽象类,提升复用性ide
抽象父类接口——List
函数
public interface List<E> { //查无元素的返回标志 int ELEMENT_NOT_FOUND = -1; /** * 元素的数量 * @return */ int size(); /** * 是否为空 * @return */ boolean isEmpty(); /** * 设置index位置的元素 * @param index * @param element * @return 原来的元素ֵ */ E set(int index, E element); /** * 获取index位置的元素 * @param index * @return */ E get(int index); /** * 是否包含某个元素 * @param element * @return */ boolean contains(E element); /** * 查看元素的索引 * @param element * @return */ int indexOf(E element); /** * 添加元素到尾部 * @param element */ void add(E element); /** * 在index位置插入一个元素 * @param index * @param element */ void add(int index, E element); /** * 删除index位置的元素 * @param index * @return */ E remove(int index); /** * 删除指定元素 * @param element * @return */ public E remove(E element); /** * 清除全部元素 */ void clear();
抽象父类AbstractList
是对接口List
的实现ui
public abstract class AbstractList<E> implements List<E> { /** * 元素的数量 */ protected int size; /** * 元素的数量 * @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); } /** * 非法索引访问数组异常 * @param index */ protected void outOfBounds(int index) { throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size); } /** * 索引检查函数 * @param index */ protected void rangeCheck(int index) { if (index < 0 || index >= size) { outOfBounds(index); } } /** * 数组添加元素的索引检查函数 * @param index */ protected void rangeCheckForAdd(int index) { //index > size,元素能够添加在数组size位置,即数组尾部下一存储单元 if (index < 0 || index > size) { outOfBounds(index); } } }
public class DynamicArray<E> extends AbstractList<E> { /** * 数组全部元素及内存地址指向 */ private E[] elements; //数组的默认初始化值 private static final int DEFAULT_CAPACITY = 10; /** * 带参构造函数,参数是数组初始化值 * @param capacity */ public DynamicArray(int capacity) { //若是传入的capacity>默认初始值,取capacity,不然取默认值 capacity = Math.max(capacity, DEFAULT_CAPACITY); //经过new Object[],动态数组能够实现多对象化 elements = (E[]) new Object[capacity]; } /** * 构造函数,将数组初始化 */ public DynamicArray() { this(DEFAULT_CAPACITY); } /** * 设置index位置的元素值 * @param index * @param element * @return old */ public E set(int index,E element){ //检查索引是否合法 rangeCheck(index); // E old = elements[index]; elements[index] = element; return old; } /** * 获取数组index位置的元素 * @param index * @return elements[index]; */ public E get(int index){ rangeCheck(index); return elements[index]; } /** * 查看元素的索引 * @param element * @return */ public int indexOf(E element){ //若是元素为空,单独判断,防止NPE if (element == null){ for (int i = 0;i < size;i++){ if (elements[i] == null) return i; } }else { //元素不为空 for (int i = 0;i < size;i++){ if (element.equals(elements[i])) return i; } } //查无此元素 return ELEMENT_NOT_FOUND; } /** * 添加元素到数组指定位置 * @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++; } /** * 删除指定元素 * @param element * @return */ public E remove(E element){ //调用indexOf获取索引,经过索引删除指定元素 return remove(indexOf(element)); } /** * 删除指定index位置的元素 * @param index * @return */ public E remove(int index){ //检查索引是否合法 rangeCheck(index); E old = elements[index]; for (int i = index + 1;i < size;i++){ elements[i - 1] = elements[i]; } //将数组原来尾部最后的元素所在的位置置为null,释放原来地址引用对应的对象内存 elements[--size] = null; //检测是否须要缩容 trim(); return old; } /** * 清除全部元素 */ public void clear() { for (int i = 0; i < size; i++) { elements[i] = null; } size = 0; } /** * 保证要有capacity的容量 * @param capacity */ private void ensureCapacity(int capacity){ int oldCapacity = elements.length; //若是数组容量足够,return if (oldCapacity >= capacity) return; //不然的话,数组扩容,数组扩容1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); E[] newElements = (E[]) new Object[newCapacity]; //将原有数组元素复制到新数组中 for (int i = 0;i < size;i++){ newElements[i] = elements[i]; } //指向新数组 elements = newElements; System.out.println(oldCapacity + "扩容为" + newCapacity); } /** * 重写toString函数,打印数组 * @return */ @Override public String toString() { // size=3, [99, 88, 77] StringBuilder string = new StringBuilder(); string.append("size=").append(size).append(", ["); for (int i = 0; i < size; i++) { if (i != 0) { string.append(", "); } string.append(elements[i]); } string.append("]"); return string.toString(); } }
在每次删除数组元素时及清空数组时,进行缩容检查this
/** * 若是内存使用比较紧张,动态数组有比较多的剩余空间,能够考虑进行缩容操做 * 例如:通过扩容后的数组很大,在通过删除操做后,数组元素数量很少,而数组所占空间够大 * 好比剩余空间占总容量的一半时,就进行缩容 (规则能够自定义) */ private void trim(){ int oldCapacity = elements.length; int newCapacity = oldCapacity >> 1; //若是元素的数量大于缩容后数组长度,或者小于初始量,不进行缩容操做 if (size >= (newCapacity) || size <= DEFAULT_CAPACITY) return;; // 剩余空间还不少 E[] newElements = (E[]) new Object[newCapacity]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; System.out.println(oldCapacity + "缩容为" + newCapacity); }
/** * 清除全部元素 */ public void clear() { // 数组清空,应该根据状况缩容,避免内存浪费 if (elements != null && elements.length > DEFAULT_CAPACITY) { elements = (E[]) new Object[DEFAULT_CAPACITY]; }else { for (int i = 0; i < size; i++) { elements[i] = null; } } size = 0; }
我的能力有限,有不正确的地方,还请指正
文章为原创,欢迎转载,注明出处便可
本文的代码已上传github
,欢迎star