什么是数组,数组这种结构就是把数据码成一排进行存放.

数组是一种线性的数据结构,那么如何二次封装一个数组呢,二次封装数组的意义是什么呢?

下面先大致看一下我封装的一个数组类Array。

package com.Leo;

public class Array<E> {

    private E[] data;

    private int size;

    //构造函数,传入数组的容量capacity构造Array
    public Array(int capacity) {
        data = (E[]) new Object[capacity];
        size = 0;
    }

    //无参的构造函数,默认为10
    public Array() {
        this(10);
    }

    public int getSize() {
        return size;
    }

    public int getCapacity() {
        return data.length;
    }

    //返回是否是空
    public boolean isEmpty() {
        return size == 0;
    }

    //在所有元素后加入一个新元素
    public void addLast(E e) {
        add(size, e);
    }

    //在所有元素前加一个新元素
    public void addFirst(E e) {
        add(0, e);
    }

    //在INDEX处插入新元素e
    public void add(int index, E e) {
        if (size == data.length) {
            resize(2 * data.length);
        }
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("插入失败");
        }
        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }
        data[index] = e;
        size++;
    }

    //获取index索引位置的元素
    public E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("获取失败,索引错误");
        }
        return data[index];
    }

    //修改数组中索引元素
    public void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("修改失败,索引错误");
        }
        data[index] = e;
    }

    //查找数组中是否有元素e
    public boolean contains(int e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return true;
            }
        }
        return false;
    }

    //查找数组中元素e所在的索引,如果不存在返回-1
    public int find(E e) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(e)) {
                return i;
            }
        }
        return -1;
    }

    //从数组中删除index位置的元素,返回删除的元素
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("修改失败,索引错误");
        }
        E ret = data[index];
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];
        }
        size--;
        data[size] = null;

        if (size == data.length / 4 && data.length / 2 != 0) {
            resize(data.length / 2);
        }
        return ret;
    }

    //删除第一个元素
    public E removeFirst() {
        return remove(0);
    }

    //删除最后一个元素
    public E removeLast() {
        return remove(size - 1);
    }

    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("Array:size = %d , capacity = %d\n", size, data.length));
        sb.append("[");
        for (int i = 0; i < size; i++) {
            sb.append(data[i]);
            if (i != size - 1) {
                sb.append(",");
            }
        }
        sb.append("]");
        return sb.toString();
    }
    //扩容缩容
    private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }
}

我们在IDEA编译器中New一个数组,并且查看它封装的方法:

java二次封装azkaban java二次封装_数据结构

因为数组是一个最基础的数据结构,所以JAVA并没有封装太多的方法进去,当我们想要对数组进行增删改查时,可能我们需要这样(以删除、排序为例):

删除数组中某一个元素:

String[] str = {a,b,c,d,e,f};
        for (int i=0;i<str.length;i++){
            if("d".equals(str[i])){
                str[i] = null;
            }
        }

删除某一个元素后再把后面的元素向前移动,保证数组中没有空元素:

String[] str = {a, b, c, d, e, f};
        int flag = 0;
        for (int i = str.length - 1; i >= 0; i--) {
            if ("d".equals(str[i])) {
                flag = i;
            }
        }
        for (int j = flag; j < str.length - 1; j++) {
            str[j] = str[j + 1];
        }

当我们在程序中多次操作数组,向其中进行增删改查的操作,我们为什么不再次封装一些方法,来减少重复代码,让程序更加整洁,也使得对数组的操作更加方便,便于维护。

比如,在我的Array数组类中,有如下几个特点

1.使用泛型,Array[E],把数组设计成泛型的,可以使得我们不只创建一个String类型的数组,我们可以使用所有类型来创建数组,让整个项目都可以使用这个类来创造数组。

2.数组是动态扩容,动态缩容的:当我们调用数组类中的add方法时,当要加入元素的索引等于数组大小时,我们动态的创造一个新的数组,设置新的数组的大小是旧的2倍,然后把新的数组赋给旧的数组,然而旧的数据没有地方使用,就交给垃圾回收来清理掉它,此时我们就把数组的大小扩大了一倍,缩容同理,代码如下:

//对数组扩容,缩容
private void resize(int newCapacity) {
        E[] newData = (E[]) new Object[newCapacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }

3.对数组封装的增删改查方法的算法复杂度都是O(n)级别的,甚至是O(1)级别的(比如addFirst,addLast这些触发索引的方法),所以使用起来十分高效。


总结:针对于以上三点,我认为,当我们在项目中大量选用了数组这种数据结构,并对其进行了一些增删改查的操作,不如我们去封装一个操作数组的方法的类,这样会使我们的代码更统一,优雅,高效,便于维护。