数组基础

数组作为数据结构最大的优点在于可以快速查询,arr[2]。但由于数组的大小在开始创建时就被定义,因此如果存储的内容超出了数组容量,就无法在原先的数组中保存数据。这里就需要使用一个可以动态改变数组容量的动态数组来存放数组。而在建立动态数组类前,我们首先要创建一个数组类。

这里我们假设要创建一个int类型的数组类。在开头先定义好我们这个类中有一个数组data[],以及数组中实际存放的元素数量size。初始化数组类时需要输入数组的容量capacity的值,如果没有输入则默认为10。

public class Array {
    private int[] data;
    private int size;

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

    public Array() {
        this(10);
    }
}

然后我们需要给该数组类添加增删改查四个基础功能。

首先是加的功能,参数index是要添加的位置,即数组的索引,参数element是要添加进去的元素。类似于插入的过程使得整个数组在index往后的索引值都需要往后移一位。

//添加指定索引index位置的元素data
public void add(int index, int element) {
    if (size == data.length) {
        throw new IllegalArgumentException("Add failed, Array is full");
    }
    if (index > size || index < 0) {
        throw new IllegalArgumentException("Add failed, index is not under[0 - size]");
    }
    for (int i = size; i > index; i--) {
        data[i] = data[i - 1];
    }
    data[index] = element;
    size++;
}

我们可以通过上述的方法封装好在数组首部和尾部添加元素的方法。

public void addFirst(int element) {
    add(0, element);
}

public void addLast(int element) {
    add(size, element);
}

删除的方法与增加的方法类似,只不过是后续的元素要往前移一位。

//删除index位置的元素并返回该元素的值
    public int delete(int index) {
        if (index >= size || index < 0) {
            throw new IllegalArgumentException("delete failed, index is illegal");
        }
        int ret = data[index];
        for (int i = index; i < size; i++) {
            data[i] = data[i + 1];
        }
        size--;
        return ret;
    }

    public int deleteFirst() {
        return delete(0);
    }

    public int deleteLast() {
        return delete(size - 1);
    }

查找和改变的方式比较简单。

//寻找数组是否包含元素e,如果包含返回索引值,否则返回-1
public int findx(int e) {
    for (int i = 0; i < size; i++) {
        if (data[i] == e) {
            return i;
        }
    }
    return -1;
}

//将索引上的值改变为e
public void set(int index, int e) {
    if (index >= size || index < 0) {
        throw new IllegalArgumentException("set failed, index is illegal");
    }
    data[index] = e;
}

该数组类应该要有个输出,重写Object的toString方法

@Override
public String toString() {
    StringBuilder res = new StringBuilder();
    res.append("Array: size = %d capacity = %d\n", size, data.length);
    res.append("[");
    for (int i = 0; i < size; i++) {
        res.append(data[i]);
        if (i != size - 1) {
            res.append(" ,");
        }
    }
    res.append("]");
    return res.toString();
}

泛型

由于数组不可能永远都是int类型,因此写一个泛型的数组类是必要的,我们可以通过它来创建各个类型的数组。

但是需要注意的是,使用泛型创造的数据结构不可以放置基本数据类型(int, char, boolean, double…),只能放置它们的包装类(Int, Char, Boolean, Double…)。

下面的代码是使用泛型创造的数组类:
需要注意,泛型定义数组时不可以直接new一个泛型数组,而是要new Object[]后再整形成泛型

public class Array<E> {
    private E[] data;
    private int size;

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

    public Array() {
        this(10);
    }

    //添加指定索引index位置的元素data
    public void add(int index, E element) {
        if (size == data.length) {
            throw new IllegalArgumentException("Add failed, Array is full");
        }
        if (index > size || index < 0) {
            throw new IllegalArgumentException("Add failed, index is not under[0 - size]");
        }
        for (int i = size; i > index; i--) {
            data[i] = data[i - 1];
        }
        data[index] = element;
        size++;
    }

    public void addFirst(E element) {
        add(0, element);
    }

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

    //删除index位置的元素并返回该元素的值
    public E delete(int index) {
        if (index >= size || index < 0) {
            throw new IllegalArgumentException("delete failed, index is illegal");
        }
        E ret = data[index];
        for (int i = index; i < size; i++) {
            data[i] = data[i + 1];
        }
        size--;
        return ret;
    }

    public E deleteFirst() {
        return delete(0);
    }

    public E deleteLast() {
        return delete(size - 1);
    }

    //寻找数组是否包含元素e,如果包含返回索引值,否则返回-1
    public int findx(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i] == e) {
                return i;
            }
        }
        return -1;
    }

    //将索引上的值改变为e
    public void set(int index, E e) {
        if (index >= size || index < 0) {
            throw new IllegalArgumentException("set failed, index is illegal");
        }
        data[index] = e;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("Array: size = %d capacity = %d\n", size, data.length);
        res.append("[");
        for (int i = 0; i < size; i++) {
            res.append(data[i]);
            if (i != size - 1) {
                res.append(" ,");
            }
        }
        res.append("]");
        return res.toString();
    }
}

动态数组

动态数组要实现的功能是在数组突破容量时扩大数组的容量,当数组实际存储的元素数远小于容量时缩小数组的容量。方法可以是:当数组到极限时,创建一个两倍于原数组容量的新数组,将原数组的内容复制到新数组中,并返回新数组;当数组的size小于等于1/4容量时,创建一个1/2容量的新数组,赋值进去并返回新数组。具体的代码操作只需要改变add和delete两个函数方法。

public void add(int index, E element) {
    if (size == data.length) {
        renew(data.length * 2);
    }
    if (index > size || index < 0) {
        throw new IllegalArgumentException("Add failed, index is not under[0 - size]");
    }
    for (int i = size; i > index; i--) {
        data[i] = data[i - 1];
    }
    data[index] = element;
    size++;
}
public E delete(int index) {
    if (index >= size || index < 0) {
        throw new IllegalArgumentException("delete failed, index is illegal");
    }
    E ret = data[index];
    for (int i = index; i < size; i++) {
        data[i] = data[i + 1];
    }
    size--;
    if (size == data.length / 4 && data.length / 2 != 0) {
        renew(data.length/2);
    }
    return ret;
}
private void renew(int capacity) {
    E[] dataNew = (E[]) new Object[capacity];
    for (int i = 0; i < size; i++) {
        dataNew[i] = data[i];
    }
    data = dataNew;
}

代码测试

增加元素观察是否能扩展数组容量,减少很多元素观察是否能缩小容量

public class Main {
    public static void main(String[] args) {
        Array<Integer> arr = new Array<>();
        for(int i = 0 ; i < 10 ; i ++)
            arr.addLast(i);
        System.out.println(arr);

        arr.add(1, 100);
        System.out.println(arr);

        arr.addFirst(-1);
        System.out.println(arr);

        arr.delete(2);
        System.out.println(arr);

        arr.delete(4);
        System.out.println(arr);

        arr.deleteFirst();
        System.out.println(arr);

        arr.deleteFirst();arr.deleteFirst();arr.deleteFirst();arr.deleteFirst();arr.deleteFirst();
        System.out.println(arr);
    }
}