当前位置 博文首页 > 欢迎光临:java数据结构——2.动态泛型数组的二次封装

    欢迎光临:java数据结构——2.动态泛型数组的二次封装

    作者:[db:作者] 时间:2021-09-20 13:54

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

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

    下面先大致看一下我封装的一个数组类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并没有封装太多的方法进去,当我们想要对数组进行增删改查时,可能我们需要这样(以删除、排序为例):

    删除数组中某一个元素:

    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这些触发索引的方法),所以使用起来十分高效。


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


    cs