数据结构--动态数组ArrayList (一)

以下是学习恋上数据结构与算法的记录,本篇主要内容是Java实现动态数组

数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合
在这里插入图片描述
◼精心选择的数据结构可以带来更高的运行或者存储效率,所以在实际应用中,往往会根据使用场景来选择最合适的数据结构。


◼线性表是最基本、最简单、也是最常用的一种数据结构(数组,链表,栈,队列,哈希表等)。

线性表是具有n个相同类型元素的有限序列(n>=0)

在这里插入图片描述
a1是首节点(首元素),an是尾结点(尾元素)
a1是a2的前驱,而a2是a1的后继


◼数组是一种顺序存储的线性表,所有元素的内存地址是连续的。

在这里插入图片描述


在很多编程语言中,数组都有个致命的缺点,那就是无法动态修改容量
实际开发中,我们更希望数组的容量是可以动态改变的。而JDK也内置了动态数组java.util.ArrayList

动态数组 设计:

size:元素个数;
elements:所有的元素
在这里插入图片描述

  // 元素的数量
	private int size;
	// 所有的元素
	private E[] elements;//使用泛型来表示数组类型,从而能保证复用性
	//默认容量
	private static final int DEFAULT_CAPACITY = 10;
	//索引错误值
	private static final int ELEMENT_NOT_FOUND = -1;
	//有参构造方法 如果赋值小于默认容量值 则选取默认容量来构造数组
	public ArrayList(int capaticy) {
		capaticy = (capaticy < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : capaticy;
		elements = (E[]) new Object[capaticy];
	}
	//无参构造 就是构造默认容量动态数组
	public ArrayList() {
		this(DEFAULT_CAPACITY);
	}

◼void add(E element);// 添加元素到最后面
思路:将新元素添加到数组的size位置上, size++
在这里插入图片描述


	public void add(E element) {
		add(size, element);
	}

◼void add(intindex, E element);// 往index位置添加元素
思路:将index之后的元素往后移动,则会空出index位置空间
移动时先要从后向前移动元素,如果从前向后移动元素, 那么会进行元素覆盖
在这里插入图片描述

	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++;
	}

◼E remove(intindex);// 删除index位置对应的元素
思路:将index后面元素前移顺序覆盖,size–
在这里插入图片描述

	public E remove(int index) {
		rangeCheck(index);
		E old = elements[index];
		for (int i = index + 1; i < size; i++) {
			elements[i - 1] = elements[i];
		}
		elements[--size] = null;
		return old;
	}

◼E get(intindex);// 返回index指定索引位置对应的元素

	public E get(int index) {
		rangeCheck(index);
		return elements[index];
	}

◼E set(intindex, E element);// 设置index位置的元素

	public E set(int index, E element) {
		rangeCheck(index);
		E old = elements[index];
		elements[index] = element;
		return old;
	}

◼int indexOf(E element);// 查看元素的位置
可以通过循环, 查找元素在数组中的位置

	public int indexOf(E element) {
	    //设计的动态数组允许null值
		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;
	}

◼void clear();// 清除所有元素,将所有的元素置为null, 然后size置为0

	public void clear() {
		for (int i = 0; i < size; i++) {
			elements[i] = null;
		}
		size = 0;
	}

◼void ensureCapacity(int capacity);//扩容 保证容量
思路:复制:新建一个新的动态数组,将旧数组数据插入新数组即可

采用位运算是为了提高运算效率,因为取模,乘除等运算比较浪费性能

须注意的是在扩容和缩容中要注意复杂度震荡
如果他们的相乘是1(扩容的新容量是旧容量的2倍,缩容的容量也压缩2倍,那就是2*1/2=1),就会频繁触发复杂度震荡,所以这里采用的是扩容1.5倍,缩容一半就是缩两倍,以此避免复杂度震荡。
在这里插入图片描述

	private void ensureCapacity(int capacity) {
		int oldCapacity = elements.length;
		if (oldCapacity >= capacity) return;
		// 新容量为旧容量的1.5倍 ,位运算则每左移一位,相当于该数乘以2
		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);
	}
	

◼void trim();//缩容为了减小浪费内存,其思路与扩容相同

		private void trim() {
		int oldCapacity = elements.length;
		int newCapacity = oldCapacity >> 1;
		if (size > (newCapacity) || oldCapacity <= 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);
	}

◼检查索引及处理(抛异常)索引检查是为了防止索引不能越界, 即不能小于0, 也不能大于等于size。

private void outOfBounds(int index) {
        //当用户传索引值不对时,我们对它的处理是抛异常
		throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
	}
	//检查索引正确方法
	private void rangeCheck(int index) {
		if (index < 0 || index >= size) {
			outOfBounds(index);
		}
	}
	//这是对添加方法设计的检查索引方法
	private void rangeCheckForAdd(int index) {
		if (index < 0 || index > size) {
			outOfBounds(index);
		}
	}

◼toString等其他方法

   // 元素的数量  
    public int size() {
		return size;
	}
   // 是否为空 ,size==0即无任务元素
	public boolean isEmpty() {
		 return size == 0;
	}
   // 是否包含某个元素
   //思路:用indexOf()查询索引 ,若索引为-1,则不存在。
	public boolean contains(E element) {
		return indexOf(element) != ELEMENT_NOT_FOUND;
	}
	//重写toString方法将元素拼接成字符串打印输出
    @Override
	public String toString() {
		StringBuilder string = new StringBuilder();//字符串拼接建议使用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();
	}
	

猜你喜欢

转载自blog.csdn.net/qq_44961149/article/details/104499209