1.自定义数组

一. 简单的数组

举个数组的简单应用:

public class Main {

    public static void main(String[] args) {
        int[] array = new int[10];
        for(int i = 0; i < array.length; i++) {
            array[i] = i;
        }

        int[] scores = new int[]{100, 99, 66};
        for(int i = 0; i< scores.length; i++) {
            System.out.println(scores[i]);
        }

        for(int score: scores) {
            System.out.println(score);
        }
    }
}

运行结果:

100
99
66
100
99
66


二.二次封装属于我们的数组

- 数组最大的优点: 快速查询
- 数组最好应用于‘索引有语义’的情况
- 但并非所有的有语义的索引都适于数组: 
身份证号11010319851266666,多位数需要更多的内存空间, 不适合当索引

我们这篇博文,主要处理‘索引没有语义’的情况

先简单实现自己的数组:

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

    /**
     *有参数的构造函数
     * @param capacity 传入数组的容量
     */
    public Array(int capacity){
        data = new int[capacity];
        size = 0;
    }

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

    /**
     * 获取数组中的元素个数
     * @return size 元素个数
     */
    public int getSize(){
        return size;
    }


    /**
     * 获取数组的容量
     * @return
     */
    public int getcapacity(){
        return data.length;
    }
    
    /**
     * 判断数组是否为空
     * @return
     */
    public boolean isEmpty(){
        return size == 0;
    }
}


三. 在数组中添加元素

1. 在数组末尾添加元素

在Array类中添加方法:

    /**
     * 向数组末尾添加元素
     * @param e
     */
    public void addLast(int e){
        if(size == data.length){
            throw new IllegalArgumentException("AddList failed. Array is full");
        }
        data[size] = e;
        size++;

    }

2. 在指定位置插入元素

    /**
     * 向数组末尾添加元素
     *
     * @param e
     */
    public void addLast(int e) {
        add(size, e);
    }

    /**
     * 在数组开头添加元素
     * @param e
     */
    public void addFirst(int e){
        add(0, e);
    }

    /**
     * index位置,插入元素e
     *
     * @param index位置, e要插入的元素
     */
    public void add(int index, int e) {
        if (size == data.length) {
            throw new IllegalArgumentException("AddList failed. Array is full");
        }

        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Require index >=0 and < size");
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = e;
        size++;
    }

四. 数组中查询元素和修改元素

 /**
     * 获取index位置的元素
     * @param index
     * @return
     */
    int get(int index){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed.Index is illegal.");
        }
        return data[index];
    }

    /**
     * index位置赋值
     * @param index
     * @return
     */
    void set(int index, int e){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Set failed.Index is illegal.");
        }

        data[index] = e;
    }

     /**
     * 打印Array类的时候自动调用(改写了默认toString方法)
     * @return
     */
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("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();
    }

五. 包含、搜索和删除

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

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

    /**
     * 删除index位置的元素, 并返回该元素
     *
     * @param index
     * @return
     */
    public int remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed.Index is illegal.");
        }

        int ret = data[index]
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];

        }
        size--;
        return ret;
    }
    /**
     * 删除开头位置的元素, 并返回该元素
     *
     * @return
     */
    public int removeFirst() {
       return remove(0);
    }
    /**
     * 删除末尾位置的元素, 并返回该元素
     *
     * @return
     */
    public int removeLast() {
        return remove(size - 1);
    }

    /**
     * 删除元素e
     * @param e
     */
    public void removeElement(int e){
        int index = find(e);
        if(index!=-1){
            remove(index);
        }
    }


六. 使用范型

- 使用范型,让我们的数据结构可以放置‘任意’的数据类型
- ‘任意’的数据类型, 不能是基本数据类型, 只能是类对象
- 基本类型: boolean byte char short int long float double

- java的每个基本数据类型都有对应的包装类
- 包装类: Boolean Byte Char Short Int Long Float Double

java实现范型很简单, 在类名后面加<自定义名字>, 并将原来的部分int改为:

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

    /**
     * 有参数的构造函数
     *
     * @param capacity 传入数组的容量
     */
    public Array(int capacity) {
        data = (E[]) new Object[capacity];  // 不能直接new E 需要绕个弯
        size = 0;
    }

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

    /**
     * 获取数组中的元素个数
     *
     * @return size 元素个数
     */
    public int getSize() {
        return size;
    }


    /**
     * 获取数组的容量
     *
     * @return
     */
    public int getcapacity() {
        return data.length;
    }

    /**
     * 判断数组是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 向数组末尾添加元素
     *
     * @param e
     */
    public void addLast(E e) {
        add(size, e);
    }

    /**
     * 在数组开头添加元素
     *
     * @param e
     */
    public void addFirst(E e) {
        add(0, e);
    }

    /**
     * index位置,插入元素e
     *
     * @param index位置, e要插入的元素
     */
    public void add(int index, E e) {
        if (size == data.length) {
            throw new IllegalArgumentException("AddList failed. Array is full");
        }

        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Require index >=0 and < size");
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = e;
        size++;
    }

    /**
     * 获取index位置的元素
     *
     * @param index
     * @return
     */
    E get(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Get failed.Index is illegal.");
        }
        return data[index];
    }

    /**
     * index位置赋值
     *
     * @param index
     * @return
     */
    void set(int index, E e) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Set failed.Index is illegal.");
        }

        data[index] = e;
    }


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

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

    /**
     * 删除index位置的元素, 并返回该元素
     *
     * @param index
     * @return
     */
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed.Index is illegal.");
        }

        E ret = data[index];
        for (int i = index + 1; i < size; i++) {
            data[i - 1] = data[i];

        }
        size--;
        data[size] = null; // 改为存放类之后, 需要把引用清空, java才会做垃圾回收
        return ret;
    }

    /**
     * 删除开头位置的元素, 并返回该元素
     *
     * @return
     */
    public E removeFirst() {
        return remove(0);
    }

    /**
     * 删除末尾位置的元素, 并返回该元素
     *
     * @return
     */
    public E removeLast() {
        return remove(size - 1);
    }

    /**
     * 删除元素e
     *
     * @param e
     */
    public void removeElement(E e) {
        int index = find(e);
        if (index != -1) {
            remove(index);
        }
    }

    /**
     * 打印Array类的时候自动调用(改写了默认toString方法)
     *
     * @return
     */
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("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();
    }
}

Main也需要做些许轻微修改:

public class Main {

    public static void main(String[] args) {
        Array<Integer> arr = new Array<>(20);  //使用范型,需告诉是什么数据类型。 这里使用int的包装类Integer
        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.remove(2);
        System.out.println(arr);

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

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


七. 动态数组

增加自定义空间方法:

 private void resize(int newCapacity){
        E[] newData = (E[])new Object[newCapacity];
        for(int i=0; i<size; i++){
            newData[i] = data[i];
        }
        data = newData;
    }

修改add方法:

     /**
     * index位置,插入元素e
     *
     * @param index位置, e要插入的元素
     */
    public void add(int index, E e) {


        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed. Require index >=0 and < size");
        }

        if (size == data.length) {   //满了就扩容
            resize(2 * data.length);  // 扩容的量和当前元素个数是一个数量级的
        }

        for (int i = size - 1; i >= index; i--) {
            data[i + 1] = data[i];
        }

        data[index] = e;
        size++;
    }

remove删除到一定程度的时候,缩小容量:

 /**
     * 删除index位置的元素, 并返回该元素
     *
     * @param index
     * @return
     */
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed.Index is illegal.");
        }

        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 / 2){     //减少到一半就动态的减少
            resize(data.length / 2);
        }
        return ret;
    }

防止复杂度震荡

addLast和removeLast的均摊复杂度都是:[n+(n+1)]/(n+1)=2
当我们的自定义数组数据满了的情况下,addLast紧接removeLast会出现复杂度一直处于最大的情况。 
我们采用lazy的策略来避免: 
修改remove方法的代码:

     /**
     * 删除index位置的元素, 并返回该元素
     *
     * @param index
     * @return
     */
    public E remove(int index) {
        if (index < 0 || index >= size) {
            throw new IllegalArgumentException("Remove failed.Index is illegal.");
        }

        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){     //减少到1/4就动态的减少
            resize(data.length / 2);
        }
        return ret;
    }

由原来减少到1/2就动态减少, 改为1/4. 这样removeLast就不会和addLast发生复杂度震荡了。

猜你喜欢

转载自blog.csdn.net/weixin_41207499/article/details/80823232
今日推荐