数组

作者: 珍惜丶现在的 | 来源:发表于2019-01-24 18:53 被阅读0次

专业定义:数组是一种线性的数据结构,用一组连续的内存空间来存储相同类型的数据。

线性指的是数据排列成像线一样的结构,只有向前或向后两个方向,同是线性的数据结构还有链表,栈,队列等。相对来说非线性的数据结构就比如树,图,堆等。

我们知道,数组是支持随机访问的。比如说我们想得到 a[5] 的数据,直接就能够定位到这个位置,而不用从a[0] 开始一个一个的向后找。能够支持这个特性,正是因为数组是在连续内存空间且数据类型相同。

不过数组也有个弊端,创建时必须指定大小且不支持扩容。在Java中为我们提供了ArrayList来解决这个问题。

不管学习什么,最好的方式就是站在设计者的角度来思考为什么要这么设计。那么,为了检验我们是不是真的掌握了这种数据结构,我们来自己实现一个支持动态扩展的工具类。

public class Array<E> {

    private Object[] data; // 用于存储数据的数组
    private int size; // data中元素的个数
    // 无参构造方法,默认容量为10
    public Array() {
        this(10);
    }

    public Array(int initCapacity) {
        this.data = new Object[initCapacity];
        this.size = 0;
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }
    // 获取当前数组的容量
    public int getCapacity() {
        return data.length;
    }
}

首先列出的是类的结构和几个基本的方法。那么接下来我们还需要哪些方法呢?比如,add、get、set、remove、contains、find、resize。

我们先看扩容方法resize()

private void resize(int newCapacity) {
    Object[] newData = new Object[newCapacity]; // 创建一个新的数组,容量为方法传入的参数
    for (int i = 0; i < size; i++) {
        newData[i] = data[i];
    }
    data = newData; // data 指向新的数组
}

添加方法 add()

// 在数组的最后位置添加数据 e
public void add(E e) {
    add(e, size);
}
// 在指定的位置 index 添加数据 e
public void add(E e, int index) {
    // 判断 index 是否合法
    if (index < 0 || index > size)
        throw new IllegalArgumentException("illegal index: " + index);
    // 考虑扩容,在添加此数据前,当前数组已经满了,则需要扩容,当前容量 * 2
    if (size == data.length - 1)
        resize(data.length * 2);
    for (int i = size; i >= index; i--) {
        data[i + 1] = data[i];
    }
    data[index] = e;
    size++; // 记得维护size
}

get()、set()、find()、contains()

// 获取下标为 index 的值
public E get(int index) {
    if (index < 0 || index >= size)
        throw new IllegalArgumentException("illegal index: " + index);
    return (E) data[index];
}
// 修改下标为 index 的值
public void set(E e, int index) {
    if (index < 0 || index >= size)
        throw new IllegalArgumentException("illegal index: " + index);
    data[index] = e;
}
// 找到元素 e 所在的位置
public int find(E e) {
    for (int i = 0; i < size; i++) {
        if (e.equals(data[i]))
            return i;
    }
    return -1;
}
// 判断元素 e 是否存在
public boolean contains(E e) {
    return !(find(e) == -1);
}

删除方法 remove()

// 删除指定元素 e
public void remove(E e) {
    int index = find(e); // 首先判断元素 e 是否存在
    if (index != -1)
        remove(index);
}
// 删除下标为 index 的元素
public E remove(int index) {
    if (index < 0 || index >= size)
        throw new IllegalArgumentException("illegal index: " + index);
    E delData = (E) data[index]; // 记录删除的元素
    for (int i = index; i < size - 1; i++) {
        data[i] = data[i + 1];
    }
    size--;
    data[size] == null;
    if (size <= data.length / 4 && data.length / 2 != 0) // ?
        resize(data.length / 2);
    return delData;
}

我们来重点关注一下 ? 处的代码。当我们一直删除元素的时候,到了某一个临界值的话,我们会“缩容”来避免内存空间的浪费,那么临界值是什么呢?我们看图



那么,图中的情况,我们要不要“缩容”呢?显然是不可以的。因为如果“缩容之后”,那么一旦有添加操作,又会触发扩容,这样是很消耗性能的。那么,临界值是多少呢?如下图所示


以上就是我们自己实现的支持动态扩容的数组。

动态数组的扩容是很消耗性能的,在某种场景下还会触发OOM,比如我们的内存空间是20M,有一个动态数组的大小是10M,此时向动态数组中再添加一个元素,就会触发扩容。此时会创建一个新的数组,大小为20M,但这时内存空间只剩了10M,所以就会触发OOM。

在Java中,不只是ArrayList,还有很多支持动态扩容的数据结构。所以为了避免频繁的扩容操作,在能估算出数据个数的情况下,在构造对象的时候,尽量指定初始化大小。

其实,对于数组的删除操作,在某些情况下,我们还可以换一种实现的思路。比如,当我们要删除其中的一个数据时,只是给这个数据添加一个特殊的标记,等到某一个特殊的时刻(比如数组空间满)触发一次删除操作,这样的话,效率会比单个删除要高,因为每次删除都要遍历并移动数组中的元素。想一想,这种思路是不是有点熟悉?在JVM的GC算法中,标记清除或者标记整理是不是跟这个思路很像。

相关文章

  • 数组

    数组数组数组数组数组数组数组数组数组

  • JavaScript - 5.数组<增删改查>

    数组 Array 数组 - 增 数组 - 删 / 改 数组 - 查 数组 - 自动 toString() 数组 -...

  • PHP数组使用

    数组定义 数组增、删、改 数组查询 数组排序 数组合并、分割 数组比较、去重复 数组长度 数组遍历 数组转换 其他...

  • 》》》PHP初入---(三)

    数组定义 1.索引数组:数组下标是整型的 声明数组: 访问数组: count(数组)--获取数组长度 查看数组所有...

  • JavaScript中数组的常用操作

    数组的遍历 数组的映射 数组的简化 数组的连接 获取数组的片段 数组的拷贝 查找数组 数组去重

  • JavaSE之数组

    六、数组 目录:数组概述、数组声明创建、数组使用、多维数组、Array类、稀疏数组 1.什么是数组 数组的定义:数...

  • Shell数组、关联数组

    数组 定义数组 获取数组 关联数组 定义关联数组 获取关联数组

  • 学习Java第五天

    数组是多个数据的集合 数组的语法 数组元素类型【】 数组名; 多维数组: 数组元素类型【】【】 数组名; 多维数组...

  • php基础精粹

    PHP php数组 php数组之索引数组初始化 PHP数组之索引数组赋值 PHP数组之访问索引数组内容 PHP数组...

  • C语言的惯用集

    数组部分 数组部分 清空数组a 把数据读进数组a 对数组a求和

网友评论

      本文标题:数组

      本文链接:https://www.haomeiwen.com/subject/omuujqtx.html