01-数组

nobility 发布于 2020-04-04 2477 次阅读


数组

  • 数组在声明时大小固定,未存满的情况下,会留有某个位置是没有元素的
  • 数组有索引的概念,从零开始
  • 查询快(支持随机访问),增删慢

自己封装的数组(仅支持int类型)

/**
 * @ClassName: Array
 * @Description: 自定义动态数组
 */
public class Array {
  private int[] data; //真正的数组,和数组的容量使用data.length表示
  private int size; //数组中有元素的个数,同时也指向了末端后的一个没有元素的位置

  /**
   * @param capacity: 用户指定的容量
   * @MethodName: Array
   * @Description: 根据用户需求创建指定大小容量的数组
   */
  public Array(int capacity) {
    this.data = new int[capacity];
    this.size = 0;
  }

  /**
   * @MethodName: Array
   * @Description: 为用户设置默认的容量大小,为10
   */
  public Array() {
    this(10);
  }

  /**
   * @MethodName: getSize
   * @Description: 获取已存入元素个数
   * @Return int
   */
  public int getSize() {
    return this.size;
  }

  /**
   * @MethodName: getCapacity
   * @Description: 获取数组容量
   * @Return int
   */
  public int getCapacity() {
    return this.data.length;
  }

  /**
   * @MethodName: isEmpty
   * @Description: 判断数组是否为空
   * @Return boolean
   */
  public boolean isEmpty() {
    return this.size == 0;
  }

  /**
   * @MethodName: add
   * @Description: 向指定的位置插入指定的元素
   * @Param index:   指定的位置
   * @Param element: 指定的元素
   * @Return void
   */
  public void add(int index, int element) {
    if (index < 0 || index > this.size) {  //若传入的index不在数组范围内,将无法插入
      throw new IllegalArgumentException("Add failed, Index is illegal");
    }
    if (this.size == this.data.length) { //若元素个数与容量大小相等时,说明数组已经满了
      throw new IllegalArgumentException("Add failed, Array is full");
    }
    for (int i = size - 1; i >= index; i--) { //size-1将i指针指向了末端
      //将数组中从插入元素开始整个向后移动,腾出要插入位置位置
      //此时i指向要移动的块(末端),一直向左逼近,直到到插入的位置,将插入位置的元素向后移动后即可循环结束
      this.data[i + 1] = this.data[i];
      //将前一个元素赋给后一个元素,即将第i个元素赋给第i+1个元素
    }
    this.data[index] = element;  //插入元素
    this.size++; //添加完毕元素后需要将元素个数加一
  }

  /**
   * @MethodName: get
   * @Description: 用户根据指定索引获取元素
   * @Param index: 指定的索引
   * @Return int
   */
  public int get(int index) {
    if (index < 0 || index > this.size) {
      throw new IllegalArgumentException("Get failed, Index is illegal");
    }
    return this.data[index];
  }

  /**
   * @MethodName: set
   * @Description: 用户根据索引修改元素内容
   * @Param index: 指定的索引
   * @Param element: 要修改成的内容
   * @Return void
   */
  public void set(int index, int element) {
    if (index < 0 || index > this.size) {
      throw new IllegalArgumentException("Set failed, Index is illegal");
    }
    this.data[index] = element;
  }

  /**
   * @MethodName: contains
   * @Description: 判断数组是否包含该元素
   * @Param element: 要判断的元素
   * @Return boolean
   */
  public boolean contains(int element) {
    for (int i = 0; i < size - 1; i++) {
      //遍历整个数组,但是仅限于用户存入的元素,size是元素个数,所以要减1
      if (this.data[i] == element) {
        return true;  //若找到相同的元素就返回true
      }
    }
    return false; //遍历完整个数组都未找到相同的元素,说明就不包含该元素,返回false
  }

  /**
   * @MethodName: find
   * @Description: 根据元素查找第一个符合的索引,找不到返回-1
   * @Param element: 要查找的元素
   * @Return int
   */
  public int find(int element) {
    for (int i = 0; i < this.size; i++) {
      if (element == this.data[i]) {
        return i;
      }
    }
    return -1;
  }

  /**
   * @MethodName: findAll
   * @Description: 根据元素查找所有符合条件的索引并以数组形式返回,找不到返回就返回一个空数组
   * @Param element: 要查找的元素
   * @Return int[]
   */
  public int[] findAll(int element) {
    int number = 0; //要返回的数组容量
    for (int i = 0; i < this.size; i++) { //遍历整个数组,找到有几个与要查找的元素相同的元素
      if (element == this.data[i]) {  //有与要查找的元素相同的元素
        number++; //容量就加1
      }
    }
    if (number > 0) { //若有符合条件的元素
      int[] indexes = new int[number];  //就开辟能刚好盛满所有索引的数组
      for (int i = 0, j = 0; i < this.size; i++) {  //遍历整个数组
        if (element == this.data[i]) {  //有与要查找的元素相同的元素
          indexes[j] = i; //就放入索引数组中
          j++;  //索引数组下标加1
        }
      }
      return indexes; //返回索引数组
    } else {  //若没有符合条件的元素
      return new int[]{}; //就返回一个空数组
    }
  }

  /**
   * @MethodName: remove
   * @Description: 根据索引删除指定元素,并返回删除的元素
   * @Param index: 指定的索引
   * @Return void
   */
  public int remove(int index) {
    if (index < 0 || index >= size) { //若传入的index不在数组范围内,将删除失败
      throw new IllegalArgumentException("Remove failed, Index is illegal");
    }
    int result = data[index]; //存储要删除的元素
    for (int i = index; i < this.size - 1; i++) {	//size-1是末端,当i指向末端时结束移动
      //将数组中从删除元素开始整个向前移动,挤掉要删除的元素
      //此时i指向要移动的块(始端),一直向右逼近,直到末端,将删除元素覆盖掉即可循环结束
      data[i] = data[i + 1];
      //将后一个元素赋给前一个元素,即将第i+1个元素赋给第i个元素
    }
    this.size--;  //删除完毕元素后需要将元素个数减1
    return result;  //返回删除元素
  }

  /**
   * @MethodName: removeElement
   * @Description: 根据元素内容删除匹配的第一个元素,删除成功返回true否则返回false
   * @Param element: 要匹配的元素
   * @Return boolean
   */
  public boolean removeElement(int element) {
    int index = this.find(element); //先根据元素查找所在索引
    if (index != -1) { //若元素存在
      this.remove(index); //就删除
      return true;  //返回true表示删除成功
    }
    return false; //返回false表示元素不存在,删除失败
  }

  /**
   * @MethodName: removeAllElement
   * @Description: 根据元素内容删除所有匹配的元素
   * @Param element: 要匹配的元素
   * @Return void
   */
  public void removeAllElement(int element) {
    while (this.removeElement(element)) { } //由于removeElement删除成功会返回true,就可以循环调用达到删除全部的目的
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(); //使用StringBuilder进行拼接字符串,方便查看结果
    for (int i = 0; i < this.size; i++) {
      sb.append(data[i]);
      if (i != this.size - 1) {  //若不是最后一个元素会加一个逗号进行分隔
        sb.append(",");
      }
    }
    return "Array{" +
        "capacity=" + this.data.length +
        ", size=" + this.size +
        '}' +
        ": [" +
        sb.toString()
        + "]";
  }
}

使用泛型增强自定义数组

  • Java语言不支持直接new一个泛型数组,需要先new一个Object数组,再使用向下强制转型成泛型数组(记着加[]
  • Java语言对于对象的比较需要使用equals()方法进行比较
  • Java语言对于对象有垃圾回收机制,在弃用对象时,最好将该对象的引用置为null(比如remove()方法)
  • 将所有用到传入数据参数的类型中变为泛型(比如set()方法),所有返回数据参数的类型中变为泛型(比如get()方法)
  • 在使用泛型类时,需要指定泛型,并且只能使用引用数据类型(声明和创建都需要<>但是创建中可省略指定类型)
import java.util.Objects;

/**
 * @ClassName: Array
 * @Description: 自定义动态数组
 */
public class Array<E> {
  private E[] data; //真正的数组,和数组的容量使用data.length表示
  private int size; //数组中有元素的个数,同时也指向了末端后的一个没有元素的位置

  /**
   * @param capacity: 用户指定的容量
   * @MethodName: Array
   * @Description: 根据用户需求创建指定大小容量的数组
   */
  public Array(int capacity) {
    this.data = (E[]) new Object[capacity];
    this.size = 0;
  }

  /**
   * @MethodName: Array
   * @Description: 为用户设置默认的容量大小,为10
   */
  public Array() {
    this(10);
  }

  /**
   * @MethodName: getSize
   * @Description: 获取已存入元素个数
   * @Return int
   */
  public int getSize() {
    return this.size;
  }

  /**
   * @MethodName: getCapacity
   * @Description: 获取数组容量
   * @Return int
   */
  public int getCapacity() {
    return this.data.length;
  }

  /**
   * @MethodName: isEmpty
   * @Description: 判断数组是否为空
   * @Return boolean
   */
  public boolean isEmpty() {
    return this.size == 0;
  }

  /**
   * @MethodName: add
   * @Description: 向指定的位置插入指定的元素
   * @Param index:   指定的位置
   * @Param element: 指定的元素
   * @Return void
   */
  public void add(int index, E element) {
    if (index < 0 || index > this.size) {  //若传入的index不在数组范围内,将无法插入
      throw new IllegalArgumentException("Add failed, Index is illegal");
    }
    if (this.size == this.data.length) { //若元素个数与容量大小相等时,说明数组已经满了
      throw new IllegalArgumentException("Add failed, Array is full");
    }
    for (int i = size - 1; i >= index; i--) { //size-1将i指针指向了末端
      //将数组中从插入元素开始整个向后移动,腾出要插入位置位置
      //此时i指向要移动的块(末端),一直向左逼近,直到到插入的位置,将插入位置的元素向后移动后即可循环结束
      this.data[i + 1] = this.data[i];
      //将前一个元素赋给后一个元素,即将第i个元素赋给第i+1个元素
    }
    this.data[index] = element;  //插入元素
    this.size++; //添加完毕元素后需要将元素个数加一
  }
  
  /**
   * @MethodName: get
   * @Description: 用户根据指定索引获取元素
   * @Param index: 指定的索引
   * @Return int
   */
  public E get(int index) {
    if (index < 0 || index > this.size) {
      throw new IllegalArgumentException("Get failed, Index is illegal");
    }
    return this.data[index];
  }

  /**
   * @MethodName: set
   * @Description: 用户根据索引修改元素内容
   * @Param index: 指定的索引
   * @Param element: 要修改成的内容
   * @Return void
   */
  public void set(int index, E element) {
    if (index < 0 || index > this.size) {
      throw new IllegalArgumentException("Set failed, Index is illegal");
    }
    this.data[index] = element;
  }

  /**
   * @MethodName: contains
   * @Description: 判断数组是否包含该元素
   * @Param element: 要判断的元素
   * @Return boolean
   */
  public boolean contains(E element) {
    for (int i = 0; i < size - 1; i++) {
      //遍历整个数组,但是仅限于用户存入的元素,size是元素个数,所以要减1
      if (this.data[i].equals(element)) {
        return true;  //若找到相同的元素就返回true
      }
    }
    return false; //遍历完整个数组都未找到相同的元素,说明就不包含该元素,返回false
  }

  /**
   * @MethodName: find
   * @Description: 根据元素查找第一个符合的索引,找不到返回-1
   * @Param element: 要查找的元素
   * @Return int
   */
  public int find(E element) {
    for (int i = 0; i < this.size; i++) {
      if (this.data[i].equals(element)) {
        return i;
      }
    }
    return -1;
  }

  /**
   * @MethodName: findAll
   * @Description: 根据元素查找所有符合条件的索引并以数组形式返回,找不到返回就返回一个空数组
   * @Param element: 要查找的元素
   * @Return int[]
   */
  public int[] findAll(E element) {
    int number = 0; //要返回的数组容量
    for (int i = 0; i < this.size; i++) { //遍历整个数组,找到有几个与要查找的元素相同的元素
      if (this.data[i].equals(element)) {  //有与要查找的元素相同的元素
        number++; //容量就加1
      }
    }
    if (number > 0) { //若有符合条件的元素
      int[] indexes = new int[number];  //就开辟能刚好盛满所有索引的数组
      for (int i = 0, j = 0; i < this.size; i++) {  //遍历整个数组
        if (this.data[i].equals(element)) {  //有与要查找的元素相同的元素
          indexes[j] = i; //就放入索引数组中
          j++;  //索引数组下标加1
        }
      }
      return indexes; //返回索引数组
    } else {  //若没有符合条件的元素
      return new int[]{}; //就返回一个空数组
    }
  }

  /**
   * @MethodName: remove
   * @Description: 根据索引删除指定元素,并返回删除的元素
   * @Param index: 指定的索引
   * @Return void
   */
  public E remove(int index) {
    if (index < 0 || index >= size) { //若传入的index不在数组范围内,将删除失败
      throw new IllegalArgumentException("Remove failed, Index is illegal");
    }
    E result = data[index]; //存储要删除的元素
    for (int i = index; i < this.size - 1; i++) {	//size-1是末端,当i指向末端时结束移动
      //将数组中从删除元素开始整个向前移动,挤掉要删除的元素
      //此时i指向要移动的块(始端),一直向右逼近,直到末端,将删除元素覆盖掉即可循环结束
      data[i] = data[i + 1];
      //将后一个元素赋给前一个元素,即将第i+1个元素赋给第i个元素
    }
    this.size--;  //删除完毕元素后需要将元素个数减1
    data[size] = null;
    //由于最后一个元素还与data数组有引用关系,所以最好手动置为空,方便垃圾回收机制快速回收
    return result;  //返回删除元素
  }
  
  /**
   * @MethodName: removeElement
   * @Description: 根据元素内容删除匹配的第一个元素,删除成功返回true否则返回false
   * @Param element: 要匹配的元素
   * @Return boolean
   */
  public boolean removeElement(E element) {
    int index = this.find(element); //先根据元素查找所在索引
    if (index != -1) { //若元素存在
      this.remove(index); //就删除
      return true;  //返回true表示删除成功
    }
    return false; //返回false表示元素不存在,删除失败
  }

  /**
   * @MethodName: removeAllElement
   * @Description: 根据元素内容删除所有匹配的元素
   * @Param element: 要匹配的元素
   * @Return void
   */
  public void removeAllElement(E element) {
    while (this.removeElement(element)) {
    } //由于removeElement删除成功会返回true,就可以循环调用达到删除全部的目的
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder(); //使用StringBuilder进行拼接字符串,方便查看结果
    for (int i = 0; i < this.size; i++) {
      sb.append(data[i]);
      if (i != this.size - 1) {  //若不是最后一个元素会加一个逗号进行分隔
        sb.append(",");
      }
    }
    return "Array{" +
        "capacity=" + this.data.length +
        ", size=" + this.size +
        '}' +
        ": [" +
        sb.toString()
        + "]";
  }
}

改为动态数组增强自定义数组

  • 动态数组是指容量动态:只是在插入和删除数据时才会对容量进行扩充或收缩
  /**
   * @MethodName: resize
   * @Description: 将data扩容指定大小
   * @Param newCapacity: 指定的大小
   * @Return void
   */
  private void resize(int newCapacity) {
    E[] newData = (E[]) new Object[newCapacity];
    for (int i = 0; i < this.size; i++) {
      newData[i] = this.data[i];
    }
    this.data = newData;
  }
  • 在容量不足时自动扩大容量,也就是add()方法中,在抛出容量不足的异常时,将该数组拷贝到一个新的且容量较大的数组中
    • 注意添加元素之前扩容,扩容应该是动态的,应该与当前容量有关
 /**
   * @MethodName: add
   * @Description: 向指定的位置插入指定的元素
   * @Param index:   指定的位置
   * @Param element: 指定的元素
   * @Return void
   */
  public void add(int index, E element) {
    if (index < 0 || index > this.size) {  //若传入的index不在数组范围内,将无法插入
      throw new IllegalArgumentException("Add failed, Index is illegal");
    }
    if (this.size == this.data.length) { //若元素个数与容量大小相等时,说明数组已经满了
      this.resize(this.getCapacity() * 2);	//此时不再抛出异常,而是扩容操作
    }
    for (int i = size - 1; i >= index; i--) { //size-1将i指针指向了末端
      //将数组中从插入元素开始整个向后移动,腾出要插入位置位置
      //此时i指向要移动的块(末端),一直向左逼近,直到到插入的位置,将插入位置的元素向后移动后即可循环结束
      this.data[i + 1] = this.data[i];
      //将前一个元素赋给后一个元素,即将第i个元素赋给第i+1个元素
    }
    this.data[index] = element;  //插入元素
    this.size++; //添加完毕元素后需要将元素个数加一
  }
  • 在容量过多时自动缩小容量,也就是remove()方法中,删除元素之后判断在达到一定容量时,就将该数组拷贝到一个新的且较小的数组中
    • 缩容应该也是动态的,应该与当前容量有关
    • 还要注意判断是否缩容使用的是除法,所以除出来的结果有可能等于0,要加一层判断
    • 而且不要缩容的刚好将容器填满,否则可能会出现用户在此界限上反复增加删除,导致一直扩容和缩容,性能大大降低)
  /**
   * @MethodName: remove
   * @Description: 根据索引删除指定元素,并返回删除的元素
   * @Param index: 指定的索引
   * @Return void
   */
  public E remove(int index) {
    if (index < 0 || index >= size) { //若传入的index不在数组范围内,将删除失败
      throw new IllegalArgumentException("Remove failed, Index is illegal");
    }
    E result = data[index]; //存储要删除的元素
    for (int i = index; i < this.size - 1; i++) {  //size-1是末端,当i指向末端时结束移动
      //将数组中从删除元素开始整个向前移动,挤掉要删除的元素
      //此时i指向要移动的块(始端),一直向右逼近,直到末端,将删除元素覆盖掉即可循环结束
      data[i] = data[i + 1];
      //将后一个元素赋给前一个元素,即将第i+1个元素赋给第i个元素
    }
    this.size--;  //删除完毕元素后需要将元素个数减1
    data[size] = null;
    //由于最后一个元素还与data数组有引用关系,所以最好手动置为空,方便垃圾回收机制快速回收
    
    if (this.getCapacity() / 4 == this.getSize() && this.getCapacity() / 2 != 0) {
      //在删除元素后判断是否容量过大,并且容量不能是0
      //缩容也不是刚刚将容器填满
      this.resize(this.getCapacity() / 2);
    }
    
    return result;  //返回删除元素
  }
此作者没有提供个人介绍
最后更新于 2020-04-04