Java自定义一个变长数组的思路与代码

 更新时间:2022年12月29日 12:55:39   作者:余数kl  
有时我们希望将把数据保存在单个连续的数组中,以便快速、便捷地访问数据,但这需要调整数组大小或者对其扩展,下面这篇文章主要给大家介绍了关于Java自定义一个变长数组的思路与代码,需要的朋友可以参考下

前言

首先需要声明的是,Java本身是提供了变长数组的,即ArrayList。那么自定义一个变长数组有啥用?其实没啥用或者说用处不大,主要就是为了了解下变长数组的设计理念而已。实际工作中直接使用ArrayList

思路分析

主要功能点:

  1. 新建时可以指定容量大小,不指定时使用默认容量大小。
  2. 向数组中追加新元素,当超过容量时应该自动扩容。
  3. 向数组中指定位置添加新元素,需要考虑指定的下标是否越界,同样也需要考虑扩容操作。
  4. 删除末尾的元素,需要考虑缩小容量。
  5. 删除指定位置元素,需要考虑指定的下标是否越界,同样也需要考虑缩小容量。
  6. 修改特定位置的元素,需要考虑指定的下标是否越界。
  7. 以时间复杂度为O ( 1 ) O(1)O(1)获取任意位置的元素,需要考虑指定的下标是否越界。

主要注意点:

  1. 扩容: 这里扩容2倍(ArrayList 是扩容 1.5 倍),扩容时新建一个2倍容量的新数组,然后将旧数组中的元素按顺序拷贝到新数组。
  2. 缩容: 当数组中的元素个数 <= 0.25 * 容量时,自动缩小容量为原来的一半,新建一个容量是原来容量一半的数组,然后将旧数组中的元素按顺序拷贝到新数组。(ArrayList缩小为和当前元素个数一样大)。
  3. 指定位置添加: 需要先将指定位置及后面所有的元素都向后移动一位,将指定位置空出来然后再插入。
  4. 指定位置删除: 先将制定位置删除,然后将后面的所有元素都向前移动一位。
  5. 容量大小: 需要指定容量的最大值,避免OOM的发生。最小值可以指定也可以不指定。

实现代码

/**
 * 变长数组
 * */
public class ResizeableArray<E> {

    private static final int MIN_CAPACITY = 10;

    private static final int MAX_CAPACITY = Integer.MAX_VALUE - 8;

    // 当前实际数据大小
    private int size = 0;

    // 实际存放元素的数组,容量为 elements.length
    private Object[] elements;

    public ResizeableArray(){
        this(MIN_CAPACITY);
    }

    public ResizeableArray(int initCapacity){
        if(initCapacity < MIN_CAPACITY){
            initCapacity = MIN_CAPACITY;
        }else if(initCapacity > MAX_CAPACITY){
            initCapacity = MAX_CAPACITY;
        }
        this.elements = new Object[initCapacity];
    }

    // 数组的特性,根据下标获取元素
    public E get(int index){
        this.checkIndex(index);
        return (E)elements[index];
    }

    // 添加一个元素到最后
    public void add(E element){
        if(size == elements.length){
            // 需要扩容
            this.expandCapacity();
        }
        elements[size++] = element;
    }

    // 添加一个元素到指定位置
    public void add(int index, E element){

        if(index == size){
            this.add(element);
            return;
        }
        this.checkIndex(index);
        if(size == elements.length){
            // 需要扩容
            this.expandCapacity();
        }
        // 需要先将index和之后的所有元素向后移动一位
        for(int i = size - 1; i >= index; i--){
            elements[i + 1] = elements[i];
        }
        elements[index] = element;
        size++;

    }

    // 设置下标为index的元素
    private void set(int index, E element){
        this.checkIndex(index);
        elements[index] = element;
    }

    /**
     * 删除下标为index的元素
     * 当 size <= 0.25 * capacity 的时候缩小容量
     */

    private void delete(int index){
        this.checkIndex(index);
        elements[index] = null;
        // 如果删除的不是最后一个元素,需要将后续元素往前移动一位
        if(index < size - 1){
            for(int i = index + 1; i < size; i++){
                elements[i - 1] = elements[i];
            }
        }
        size--;
        if(size <= 0.25 * elements.length){
            this.reduceCapacity();
        }
    }

    private void deleteLast(){
        this.delete(size - 1);
    }

    private void checkIndex(int index){
        if(index < 0 || index >= size){
            throw new IndexOutOfBoundsException(String.format("Out of bounds at: %s, size is: %d", index, size));
        }
    }

    private void expandCapacity(){
        if(MAX_CAPACITY == elements.length){
            // 容量达到最大限制,无法扩容。
            throw new RuntimeException("The capacity has reached its maximum limit and cannot be expanded.");
        }
        int newCapacity = Math.min(elements.length << 1, MAX_CAPACITY);
        Object[] newElements = new Object[newCapacity];
        System.arraycopy(elements, 0, newElements, 0, size);
        elements = newElements;
    }

    private void reduceCapacity(){
        if(elements.length == MIN_CAPACITY){
            return;
        }
        int newCapacity = Math.max(elements.length >> 1, MIN_CAPACITY);
        Object[] newElements = new Object[newCapacity];
        System.arraycopy(elements, 0, newElements, 0, size);
        elements = newElements;
    }



    public static void main(String[] args){
        ResizeableArray<Integer> resizeableArray = new ResizeableArray<>();
        System.out.printf("初始化后,size为: %d \n", resizeableArray.size);
        System.out.printf("初始化后,capacity为: %d \n", resizeableArray.elements.length);
        System.out.println();

        for(int i = 0; i < 20; i++){
            resizeableArray.add(i);
        }

        System.out.printf("添加20个元素后,size为: %d \n", resizeableArray.size);
        System.out.printf("添加20个元素后,capacity为: %d \n", resizeableArray.elements.length);
        System.out.printf("添加20个元素后,第5个元素是: %d \n", resizeableArray.get(4));
        System.out.println();

        resizeableArray.delete(4);

        System.out.printf("删除第五个元素后,size为: %d \n", resizeableArray.size);
        System.out.printf("删除第五个元素后,capacity为: %d \n", resizeableArray.elements.length);
        System.out.printf("删除第五个元素后,第5个元素是: %d\n", resizeableArray.get(4));
        System.out.println();

        resizeableArray.add(4, 100);
        System.out.printf("在第五个位置插入元素后,size为: %d \n", resizeableArray.size);
        System.out.printf("在第五个位置插入元素后,capacity为: %d \n", resizeableArray.elements.length);
        System.out.printf("在第五个位置插入元素后,第5个元素是: %d\n", resizeableArray.get(4));
        System.out.println();

        for(int i = 0; i < 15; i++){
            resizeableArray.deleteLast();
        }
        System.out.printf("删除后面15个元素后,size为: %d \n", resizeableArray.size);
        System.out.printf("删除后面15个元素后,capacity为: %d \n", resizeableArray.elements.length);
        System.out.printf("删除后面15个元素后,第5个元素是: %d\n", resizeableArray.get(4));
        System.out.println();

        resizeableArray.set(4, 200);
        System.out.printf("修改第五个元素后,当前size为: %d \n", resizeableArray.size);
        System.out.printf("修改第五个元素后,capacity为: %d \n", resizeableArray.elements.length);
        System.out.printf("修改第五个元素后,第5个元素是: %d\n", resizeableArray.get(4));

    }
}

测试结果

由执行结果可知:

  • 初始化后默认容量为10
  • 添加元素超过10个后会自动扩容。
  • 删除一个元素后,size会减1,后面元素会自动向前移动一位。
  • 插入一个新元素后,size会加1,后续元素后移一位。
  • 删除到只有0.25 * 容量个元素后,会自动缩小容量。

总结

到此这篇关于Java自定义一个变长数组的思路与代码的文章就介绍到这了,更多相关Java自定义变长数组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • spring boot配置多个请求服务代理的完整步骤

    spring boot配置多个请求服务代理的完整步骤

    这篇文章主要给大家介绍了关于spring boot配置多个请求服务代理的完整步骤,文中通过示例代码介绍的非常详细,对大家学习或者使用spring boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-11-11
  • springcloud-feign调用报错问题

    springcloud-feign调用报错问题

    这篇文章主要介绍了springcloud-feign调用报错问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Spring Data Jpa 复合主键的实现

    Spring Data Jpa 复合主键的实现

    这篇文章主要介绍了Spring Data Jpa 复合主键的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • SpringBoot实现文件上传下载实时进度条功能(附源码)

    SpringBoot实现文件上传下载实时进度条功能(附源码)

    这篇文章主要为大家详细介绍了SpringBoot如何实现文件上传下载实时进度条功能,文中的示例代码讲解详细,感兴趣的小伙伴可以学习一下
    2022-10-10
  • 详解关于Windows10 Java环境变量配置问题的解决办法

    详解关于Windows10 Java环境变量配置问题的解决办法

    这篇文章主要介绍了关于Windows10 Java环境变量配置问题的解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • java迷宫算法的理解(递归分割,递归回溯,深搜,广搜)

    java迷宫算法的理解(递归分割,递归回溯,深搜,广搜)

    本文主要使用的算法(自动生成地图:递归分割法、递归回溯法;寻找路径:深度优先、广度优先算法),非常具有实用价值,需要的朋友可以参考下
    2021-06-06
  • MyBatis实现多表联查的详细代码

    MyBatis实现多表联查的详细代码

    这篇文章主要介绍了MyBatis如何实现多表联查,通过实例代码给大家介绍使用映射配置文件实现多表联查,使用注解的方式实现多表联查,需要的朋友可以参考下
    2022-08-08
  • Java实现Dijkstra算法的示例代码

    Java实现Dijkstra算法的示例代码

    Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。本文主要介绍了实现这一算法的Java代码,需要的可以参考一下
    2022-07-07
  • Spring Bean创建和循环依赖

    Spring Bean创建和循环依赖

    这篇文章主要介绍了Spring Bean创建和循环依赖,讲述了Spring容器中 Bean 的创建过程已经主要的方法,另外也着重分析了循环依赖的问题,需要的小伙伴可以参考一下
    2022-05-05
  • java中建立0-10m的消息(字符串)实现方法

    java中建立0-10m的消息(字符串)实现方法

    下面小编就为大家带来一篇java中建立0-10m的消息(字符串)实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05

最新评论