java实现动态数组(三)

概述

实现我上一篇博客中提到的
扩展

实现

根据索引删除元素

	/**
     *  根据索引删除元素
     *      思路:目标索引处往后的元素都往前移动一个位置 就把目标索引处的值给覆盖了 即删除
     *
     *      已有数组
     *         元素  1 2 3 4 5
     *
     *         索引  0 1 2 3 4
     *
     *      现在要删除索引为2的元素 则索引为3、4的元素(要移动2个元素 2 = size - index-1)往前移动一个位置
     *      即 System.arraycopy()方法中 srcPos 为 index+1
     *      就把索引为 2 的元素覆盖掉了
     *      索引为 3、4 的元素移动后 索引就变为了 2、3(开始索引为2 即destPos为2 2即index) 所以System.arraycopy()方法中destPos为index
     * @param index 目标索引
     * @return 返回被删掉的元素
     */
    public Integer removeByIndex(int index) {
        if (index < 0 || index > size) {
            log.error("索引:{} 不正确", index);
            return null; // 返回null表示 没有任何一个元素被删掉
        }
        int removedEle=arr[index]; // 待删除的元素
        System.arraycopy(arr, index + 1, arr, index, size - index - 1);
        size--; // 删除一个元素后 size自然就要-1
        return removedEle;
    }

根据元素值删除元素

	/**
     *  根据值删除元素
     *      思路:遍历数组 直到找到目标值 再调用removeByIndex()删掉即可
     *
     * @param value 欲删除的元素的值
     * @return
     */
    public int removeByValue(int value) {
        int removedIndex = -1;
        for (int i = 0; i < size; i++) {
            if (arr[i] == value) {
                removedIndex = i;
                break;
            }
        }
        if (removedIndex == -1) { // 说明待删除元素在数组中不存在 提示
            log.info("待删除元素:{}在数组中不存在", value);
            return -1;
        }
        // 调用根据索引删除元素的方法即可
        return removeByIndex(removedIndex);
    }

测试用例

package com.lovehena.datastructure.test;

import com.lovehena.datastructure.MyArrayList;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class TestMyArrayList {

    public static void main(String[] args) {
        MyArrayList list = new MyArrayList();
        list.add(0, 1);
        list.add(1, 2);
        list.add(2, 3);
        list.add(3, 4);
        list.add(4, 5);
        list.add(5, 6);
        list.add(6, 7);
        list.add(7, 8);
        list.add(8, 9);
        list.add(9, 10);
        list.addLast(11);
        list.print();

        list.add(0, 100); // 把100放在第一个位置
        /*Consumer<Integer> c1 = new Consumer<Integer>() { // 匿名内部类的写法创建Consumer的实例
            @Override
            public void accept(Integer element) {
                log.info("数组中的元素为:{}", element);
            }
        };
        list.print(c1);*/
        list.print();

        list.add(8,88);
        list.add(9,99);
        list.add(10,1000);
        list.add(11,111);
        list.add(12,122);
        list.print();
        // 测试删除 removeByIndex
        Integer removed = list.removeByIndex(0);
        log.info("删除第一个元素:{} 后",removed);
        list.print();

        Integer removed2 = list.removeByIndex(15);
        log.info("再删掉最后一个元素:{} 后",removed2);
        list.print();

        Integer removed3 = list.removeByIndex(4);
        log.info("删掉第五个元素:{} 后",removed3);
        list.print();

        // 测试删除 removeByValue
        int removed4 = list.removeByValue(10);
        log.info("删除元素:{} 后",10);
        list.print();
    }

}

测试用例输出

测试用例输出

完整实现

package com.lovehena.datastructure;

import lombok.extern.slf4j.Slf4j;

import java.util.function.Consumer;

/*
*   java实现一个动态数组
*       实际上,java.util.ArrayList这个类就是一个动态数组的类
*       和静态数组最大的不同就是,java.util.ArrayList中的元素个数是可以变化的。
*
*   一个动态数组中至少要有3个属性:
*       1.size 动态数组中已有元素的个数
*       2.capacity 动态数组中最大的元素个数
*       3.arr 虽然我们要实现的是一个动态数组,但还是需要一个静态数组用于存储元素,当静态数组无法容纳更多元素时动态扩容,于是,动态数组就实现了
*
*   先实现以下3个方法:
*       1.add(int index,int element) 往数组中指定索引处添加元素
*       2.addLast(int element) 将欲添加的元素添加到数组最后面
*       3.print() 数组的遍历
* */
@Slf4j
public class MyArrayList {
    private int size = 0; // 动态数组中已手动添加的元素的个数 初始时一个手动添加的元素也没有
    private int capacity = 10; // 动态数组中最大的元素个数 默认为10 java.util.ArrayList的默认初始容量也是10
    // 初始化一个静态数组 用于存储元素
    // 数组中的元素类型默认都是整数 如果需要存储其他类型 可以利用泛型机制实现
    // 实际上 int[] arr=new int[capacity] 这行代码执行完时 arr中已经有10个元素了 并且每个元素的值都是0
    private int[] arr = new int[capacity];

    /**
     * 往数组中指定索引处添加元素
     *  思路:
     *      已有数组
     *    元素  1 2 3 4 5
     *    索引  0 1 2 3 4 5
     *
     *      现在要往索引为2的位置添加一个元素100,则 3 4 5这3( 3 = size - index )个元素都要往后移动1位,将索引为2的位置空出来,再将目标元素插入在索引2处即可
     *      保证占用的内存空间是连续的
     * @param index 目标索引
     * @param element 目标元素
     */
    public void add(int index, int element) {
        if (size == capacity) { // 原容量已满还要添加新元素 则需要扩容
            grow();
        }

        if (index < 0 || index > size) { // index最大值为size-1 最小值为0
            log.error("索引:{} 不正确", index);
            return;
        }
        if (index == size) { // 往数组最后一个位置添加一个元素 不需要移动任何元素
            arr[index] = element;
            size++;
            return;
        }
        // 利用System.arraycopy完成数组中元素的移动 api不熟悉的话可以点进源码阅读下注释
        // destPos的最大值为index(而不是index+1 java api设计中大多都是含头不含尾)
        System.arraycopy(arr, index, arr, index + 1, size - index);
        arr[index] = element;
        size++; // 已手动添加的元素的个数+1 这个size其实类似于实现链表时的头节点(头指针)
    }

    /**
     *  扩容:
     *      创建一个新数组,数组长度为原数组的1.5倍(java.util.ArrayList中每次扩容就是1.5倍),将原数组中的元素拷贝到新数组中即可
     */
    private void grow() {
        int oldCapacity = capacity; // 记录旧容量
        int newCapacity = capacity + capacity / 2;// 1.5倍的capacity
        int[] newArr = new int[newCapacity];
        System.arraycopy(arr, 0, newArr, 0, size); // 把原数组中手动添加的元素全都拷贝到新数组中
        arr = newArr;
        capacity = newCapacity;
        log.info("发生了扩容 扩容前静态数组长度为:{},扩容后的长度为:{}", oldCapacity, newCapacity);
    }

    /**
     *  将欲添加的元素添加到数组最后面
     *      思路:
     *        已有数组
     *      元素  1 2 3 4 5
     *      索引  0 1 2 3 4
     *
     *      现在要将6插入到数组的最后面,即插入到索引为5( size = 5)的位置,所以调用add传入index为size即可
     *
     *      实际上 很多代码的书写都是先总结规律 再写出来的
     * @param element 欲添加的元素
     */
    public void addLast(int element){
        // 直接调用add方法即可
        add(size,element);
    }

    /**
     *  根据索引删除元素
     *      思路:目标索引处往后的元素都往前移动一个位置 就把目标索引处的值给覆盖了 即删除
     *
     *      已有数组
     *         元素  1 2 3 4 5
     *
     *         索引  0 1 2 3 4
     *
     *      现在要删除索引为2的元素 则索引为3、4的元素(要移动2个元素 2 = size - index-1)往前移动一个位置
     *      即 System.arraycopy()方法中 srcPos 为 index+1
     *      就把索引为 2 的元素覆盖掉了
     *      索引为 3、4 的元素移动后 索引就变为了 2、3(开始索引为2 即destPos为2 2即index) 所以System.arraycopy()方法中destPos为index
     * @param index 目标索引
     * @return 返回被删掉的元素
     */
    public Integer removeByIndex(int index) {
        if (index < 0 || index > size) {
            log.error("索引:{} 不正确", index);
            return null; // 返回null表示 没有任何一个元素被删掉
        }
        int removedEle=arr[index]; // 待删除的元素
        System.arraycopy(arr, index + 1, arr, index, size - index - 1);
        size--; // 删除一个元素后 size自然就要-1
        return removedEle;
    }

    /**
     *  根据值删除元素
     *      思路:遍历数组 直到找到目标值 再调用removeByIndex()删掉即可
     *
     * @param value 欲删除的元素的值
     * @return
     */
    public int removeByValue(int value) {
        int removedIndex = -1;
        for (int i = 0; i < size; i++) {
            if (arr[i] == value) {
                removedIndex = i;
                break;
            }
        }
        if (removedIndex == -1) { // 说明待删除元素在数组中不存在 提示
            log.info("待删除元素:{}在数组中不存在", value);
            return -1;
        }
        // 调用根据索引删除元素的方法即可
        return removeByIndex(removedIndex);
    }

    /**
     *  Consumer的泛型表示调用者拿到的操作数的类型 本例中调用者需要拿到每个数组的元素(int类型)
     *  类似于js中的回调函数
     * @param cb 调用者手动指定遍历数组的逻辑
     */
    public void print(Consumer<Integer> cb){ //
        /*
            为什么不是这么写?
            for (int i = 0; i < arr.length; i++) {

            }
            因为 数组中真正添加了几个元素是由size表示的 我们遍历时需要的是我们手动添加的元素
            而不是数组初始化时默认初始化的为0的元素
         */
        for (int i = 0; i < size; i++) {
            cb.accept(arr[i]);
        }
    }

    /**
     *  数组遍历
     */
    public void print(){
        /*
            为什么不是这么写?
            for (int i = 0; i < arr.length; i++) {

            }
            因为 数组中真正添加了几个元素是由size表示的 我们遍历时需要的是我们手动添加的元素
            而不是数组初始化时默认初始化的为0的元素
         */
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(arr[i]).append(" ");
        }
        log.info("遍历数组 数组为:{}\n",sb.toString());
    }

    
    // todo 扩展:根据元素值删除元素时,如果数组中存在多个目标元素该怎么处理?

}

扩展

根据元素值删除元素时,如果数组中存在多个目标元素该怎么处理?

最后

好了,如果对你有帮助的话,欢迎点个免费的赞哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值