Java 数据结构篇-模拟实现动态数组

news2024/12/24 8:55:03

🔥博客主页: 小扳_-CSDN博客
❤感谢大家点赞👍收藏⭐评论✍
   

 

 

本篇目录

        1.0 动态数组说明

        2.0 模拟实现动态数组的核心方法

        2.1 动态数组-插入与扩容

        2.2 动态数组-获取元素

        2.3 动态数组-修改元素

        2.4 动态数组-删除元素

        2.5 动态数组-遍历元素(重点)

        2.5.1 使用 forEach 循环元素

        2.5.2 使用迭代器循环元素

        2.5.3  使用 Stream 流来循环元素

        2.6 补充关于拷贝的方法

        3.0 对以上代码进行汇总整理升级


        1.0 动态数组说明

        在 Java 中,动态数组可以使用 ArrayList 类来实现。ArrayList 类是 Java 集合框架中的一个类,它可以动态地增加或减少元素的大小。与普通数组相比,ArrayList 具有动态增长和缩小的能力,可以根据需要自动调整数组的大小。

        

        2.0 模拟实现动态数组的核心方法

        该动态数组中的成员变量分别为:Object[] myArrayList 数组int size 元素个数int defaultSize 默认大小。在 ArrayList 类中,在未添加元素之前为空,因此,Object[] myArrayList = {}size 默认为0;当第一个元素添加进来的时候,defaultSize 默认为10

具体代码如下:

public class ImitateArray<E> implements Iterable<E>{
    private int defaultSize = 10;
    private Object[] myArraylist= {};
    private int size = 0;

    //无参构造器
    public ImitateArray() {
    }

        2.1 动态数组-插入与扩容

        add(element): 向动态数组的末尾添加一个元素。如果数组已满,则需要扩容。

具体代码如下:

    public boolean add(E e){
        if (size == 0){
            myArraylist = new Object[defaultSize];
        }
        //先判断是否需要扩容
        if (isExpansion()) {
            expansion();
          }
        myArraylist[size] = e;
        size++;
        return true;
    }


    //是否需要扩容
    private boolean isExpansion(){
        return size >= defaultSize;
    }
    //数组扩容
    private boolean expansion(){
        defaultSize = (int) (defaultSize * 1.5);
        Object[] temp = new Object[(int) (defaultSize)];
        //for (int i = 0; i < myArraylist.length; i++) {
        //    temp[i] = myArraylist[i];
        //}
        System.arraycopy(myArraylist,0,temp,0,size);
        myArraylist = temp;
        System.out.println("扩容成功!");
        return true;
    }


        对以上代码进行分析:

        在添加元素前,

        第一,先判断是否首元素插入,如果是首元素,需要创建数组对象,默认大小为10

        第二,判断 size == defaultSize,如果是,就需要扩容了,数组扩容大小为原来的1.5倍defaultSize += defaultSize >>> 1,扩容成功之后,需要将原数组中的元素拷贝回到新数组中,可以用的方法为 System.arraycopy(myArraylist,0,temp,0,size);

        第三,添加完元素之后,需要进行 size++

        2.2 动态数组-获取元素

        get(index): 获取指定索引处的元素。

具体代码如下:

        

    public E get(int index){
        if (index >= size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        return (E)myArraylist[index];
    }

        对以上代码进行分析:

        获取元素之前,先要判断是否越界访问数组。

        2.3 动态数组-修改元素

        set(index, element): 修改指定索引处的元素。

具体代码如下:

    public E set(int index,E e){
        if (index >= size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        E temp = (E) myArraylist[index];
        myArraylist[index] = e;
        return temp;
    }

        同理,在修改元素之前先要判断是否为越界访问。

        2.4 动态数组-删除元素

        remove(index): 删除指定索引处的元素。如果删除后数组的空余空间过多,则需要缩容。

具体代码如下:

    //根据索引删除数据
    public boolean remove(int index){
        if (index >= size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        if (index == size - 1){
            myArraylist[index] = null;
        }else {
            //1,2,3,4,5
            //0,1,2,3,4
            for (int i = index; i < size; i++) {
                myArraylist[i] = myArraylist[i + 1];
            }
        }
        size--;
        return true;
    }

        对以上代码进行分析:
        先判断是否越界访问,再判断若要删除的元素为最后一个,则直接 null,接着 size--。其他情况需要缩容,myArraylist[i] = myArraylist[i + 1];

        2.5 动态数组-遍历元素(重点)

        介绍三种方式来遍历元素:

        第一种,实现使用 forEach 循环元素

        第二种,实现使用迭代器循环元素

        第三种,实现使用 Stream 流来循环元素

        2.5.1 使用 forEach 循环元素

具体代码如下:

public interface Consumer <E>{
    void accept(E e);
}
    //实现forEach
    public void forEach(Consumer<E> consumer) {
        Object[] temp = new Object[size];
        for (int i = 0; i < size; i++) {
            temp[i] = myArraylist[i];
        }
        for (Object o : temp) {
            consumer.accept((E)o);
        }

    }
        imitateArray.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.print(integer+" ");
            }
        });

        对以上代码进行分析:

        先实现一个接口,然后用 fori 循环,将有效元素都拷贝到新的数组中,接着用 foreach 循环来对每个元素进行操作,具体由用户决定了。 

        2.5.2 使用迭代器循环元素

具体代码如下:

首先需要实现 Iterable 接口;

    //重写迭代器
    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int i = 0;
            @Override
            public boolean hasNext() {
                return i < size;
            }

            @Override
            public E next() {
                return (E) myArraylist[i++];
            }
        };
    }
for (Integer integer : imitateArray) {
    System.out.print(integer+" ");
}

        补充;大部分的集合都实现该迭代器,因此不是所有类都具有迭代器的。

        2.5.3  使用 Stream 流来循环元素

具体代码如下:

    //用流进行遍历
    public Stream stream(){
        //第一种比较晦涩难懂
        //return Arrays.stream(Arrays.copyOf(myArraylist,size)).map(e->(E) e);
        
        //第二种比较好理解一点
        Stream<Object> stream1 = Stream.of(Arrays.copyOf(myArraylist,size));
        return stream1.map(e->(E) e);
    }
      imitateArray.stream().forEach(s-> System.out.print(s+" "));
    

        重点注意:在 stream 方法中,使用 Stream.of((E) myArraylist) 来创建一个流,但是这样会将整个数组对象作为一个元素添加到流中,而不是将数组中的元素逐个添加到流中。

        因此,使用 map 方法来对流中的每个元素进行操作。在这里使用 lambda 表达式 (e -> (E) e) 来将每个元素 (e) 强制转换为 E 类型。这样就可以将流中的元素类型转换为 E 类型。

         2.6 补充关于拷贝的方法

        第一个,System.arraycopy(myArraylist,0,temp,0,size),用于一个或者两个数组,从 myArraylist 数组从第 0 个索引拷贝到 temp 数组中从第 0 个索引开始,一共要拷贝 size 个元素。

        第二个,Arrays.copyof(int[] original, int newlength),用于从原来的数组拷贝到新数组的个数为 newlength 个。

        第三个,Arrays.copyOfRange(int[] original, int from, int to) ,将指定数组的指定范围复制到新数组中。 

        3.0 对以上代码进行汇总整理升级

public interface Consumer <E>{
    void accept(E e);
}

模拟实现 ArrayList 的代码:

import java.util.Arrays;
import java.util.Iterator;
import java.util.stream.Stream;

public class ImitateArray<E> implements Iterable<E>{
    private int defaultSize = 10;
    private Object[] myArraylist= {};
    private int size = 0;

    public ImitateArray() {
    }
    //添加元素
    public boolean add(E e){
        if (size == 0){
            myArraylist = new Object[defaultSize];
        }
        //先判断是否需要扩容
        if (isExpansion()) {
            expansion();
          }
        myArraylist[size] = e;
        size++;
        return true;
    }

    //根据索引来查询数据
    public E get(int index){
        if (index >= size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        return (E)myArraylist[index];
    }

    //根据索引删除数据
    public boolean remove(int index){
        if (index >= size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        if (index == size - 1){
            myArraylist[index] = null;
        }else {
            //1,2,3,4,5
            //0,1,2,3,4
/*            for (int i = index; i < size; i++) {
                myArraylist[i] = myArraylist[i + 1];
            }*/
            System.arraycopy(myArraylist,index + 1,myArraylist,index,size - index -1);
        }
        size--;
        return true;
    }

    //根据索引来更改数据
    public E set(int index,E e){
        if (index >= size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        E temp = (E) myArraylist[index];
        myArraylist[index] = e;
        return temp;
    }

    //数组长度
    public int size(){
        return size;
    }

    //实现forEach
    public void forEach(Consumer<E> consumer) {
        Object[] temp = new Object[size];
        for (int i = 0; i < size; i++) {
            temp[i] = myArraylist[i];
        }
        for (Object o : temp) {
            consumer.accept((E)o);
        }

    }

    //根据索引插入元素
    public boolean insert(int index,E e){
        if (index > size || index < 0){
            throw new RuntimeException("越界!!!");
        }
        if (index == size){
            //直接调用 add 方法
            add(e);
        }
        if (isExpansion()){
            expansion();
        }
        //Object[] temp = new Object[defaultSize];
/*        for (int i = 0; i < index; i++) {
            temp[i] = myArraylist[i];
        }
        temp[index] = e;
        for (int i = index; i < size ; i++) {
            temp[i+1] = myArraylist[i];
        }*/
        System.arraycopy(myArraylist,index ,myArraylist,index + 1,size - index);
        myArraylist[index] = e;
        //myArraylist = temp;
        size++;
        return true;
    }

    //是否需要扩容
    private boolean isExpansion(){
        return size >= defaultSize;
    }
    //数组扩容
    private boolean expansion(){
        defaultSize = (int) (defaultSize * 1.5);
        Object[] temp = new Object[(int) (defaultSize)];
/*        for (int i = 0; i < myArraylist.length; i++) {
            temp[i] = myArraylist[i];
        }*/
        System.arraycopy(myArraylist,0,temp,0,size);
        myArraylist = temp;
        System.out.println("扩容成功!");
        return true;
    }
    //重写 toString 方法
    @Override
    public String toString() {
        Object[] temp = new Object[size];
        for (int i = 0; i < size; i++) {
            temp[i] = myArraylist[i];
        }
        return "ImitateArray{" +
                "myArraylist=" + Arrays.toString(temp) +
                '}';
    }

    //重写迭代器
    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int i = 0;
            @Override
            public boolean hasNext() {
                return i < size;
            }

            @Override
            public E next() {
                return (E) myArraylist[i++];
            }
        };
    }

    //用流进行遍历
    public Stream stream(){
        //第一种比较晦涩难懂
        //return Arrays.stream(Arrays.copyOf(myArraylist,size)).map(e->(E) e);

        //第二种比较好理解一点
        Stream<Object> stream1 = Stream.of(Arrays.copyOf(myArraylist,size));
        return stream1.map(e->(E) e);
    }
}

以下为测试类: 

public class Text {
    public static void main(String[] args)  {
        ImitateArray<Integer> imitateArray = new ImitateArray<>();
        imitateArray.add(1);
        imitateArray.add(2);
        imitateArray.add(3);
        imitateArray.add(4);
        imitateArray.add(5);
        imitateArray.add(6);
        imitateArray.add(7);
        imitateArray.add(8);
        imitateArray.add(9);
        imitateArray.add(10);
        imitateArray.add(11);
        imitateArray.add(12);
        System.out.println(imitateArray);

        imitateArray.insert(11,11);
        imitateArray.insert(11,11);
        imitateArray.insert(11,11);
        imitateArray.insert(11,11);
        imitateArray.insert(11,11);
        imitateArray.insert(11,11);
        imitateArray.insert(11,11);
        imitateArray.insert(11,11);

/*        ArrayList<Integer> list = new ArrayList<>();
        list.forEach(new java.util.function.Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {

            }
        });*/

        imitateArray.forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.print(integer+" ");
            }
        });
        System.out.println();

        for (Integer integer : imitateArray) {
            System.out.print(integer+" ");
        }

        System.out.println();
        imitateArray.stream().forEach(s-> System.out.print(s+" "));
    }
}

                

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1168178.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

H5: 使用Web Audio API播放音乐

简介 记录关于自己使用 Web Audio API 的 AudioContext 播放音乐的知识点。 需求分析 1.列表展示音乐&#xff1b; 2.上/下一首、播放/暂停/续播&#xff1b; 3.播放模式切换&#xff1a;循环播放、单曲循环、随机播放&#xff1b; 4.播放状态显示&#xff1a;当前播放的音乐…

适合女生的副业有哪些?整理了六个靠谱副业,女生必看

在这个互联网时代下&#xff0c;女生对于经济独立变得越来越看重。她们与男生一样&#xff0c;对于工作认真努力、追求进步&#xff0c;并且对于副业有着强烈的渴望和热爱。事实上&#xff0c;她们在副业领域的表现要远远超过很多男生&#xff0c;这一点不可否认。 女生在副业方…

Linux Crontab 定时任务

crond 服务 Linux 通过 crond 服务来支持 crontab。 查看 crond 服务是否已经安装 输入下面命令确认 crond 服务是否已安装。 systemctl list-unit-files | grep crond 如果为 enabled&#xff0c;表示服务正运行。 crontab 文件 crontab 要执行的定时任务都被保存在 /etc…

PostgreSQL 进阶 - 使用foreign key,使用 subqueries 插入,inner joins,outer joins

1. 使用foreign key 创建 table CREATE TABLE orders( order_id SERIAL PRIMARY KEY, purchase_total NUMERIC, timestamp TIMESTAMPTZ, customer_id INT REFERENCES customers(customer_id) ON DELETE CASCADE);“order_id”&#xff1a;作为主键的自增序列&#xff0c;使用 …

ElasticSearch集群环境搭建

1、准备三台服务器 这里准备三台服务器如下: IP地址主机名节点名192.168.225.65linux1node-1192.168.225.66linux2node-2192.168.225.67linux3node-3 2、准备elasticsearch安装环境 (1)编辑/etc/hosts&#xff08;三台服务器都执行&#xff09; vim /etc/hosts 添加如下内…

uniapp subNvue 写的视频播放

文件下载地址 https://download.csdn.net/download/weixin_47517731/88500016https://download.csdn.net/download/weixin_47517731/88500016 1:在pages.json中配置视频播放页面 {/* 视频详情页面 */"path": "pages/detail-video/detail","style&q…

一键解决 AirPods Pro 的沙沙声

每次我都以为是因为耳机受潮了&#xff0c;但每次这个方法都有效 [笑哭] 1、打开苹果手机&#xff0c;蓝牙连接 AirPods Pro 后&#xff0c;打开“设置”找到&#xff1a; 2、点进去&#xff0c;点击“关闭”&#xff1a; 瞬间&#xff0c;整个世界安静了&#xff01;

[已解决]AttributeError: module ‘numpy‘ has no attribute ‘float‘

1、问题&#xff1a; AttributeError: module numpy has no attribute float np.float was a deprecated alias for the builtin float. To avoid this error in existing code, use float by itself. Doing this will not modify any behavior and is safe. If you specifica…

基本微信小程序的拼车自助服务小程序-网约车拼车系统

项目介绍 拼车自助服务小程序的设计与开发的开发利用现有的成熟技术参考&#xff0c;以源代码为模板&#xff0c;分析功能调整与拼车自助服务小程序的设计与开发的实际需求相结合&#xff0c;讨论了拼车自助服务小程序的设计与开发的使用。 开发环境 开发说明&#xff1a;前…

虹科荣誉 | 喜讯!虹科成功入选“广州首届百家新锐企业”!!

文章来源&#xff1a;虹科品牌部 阅读原文&#xff1a;虹科荣誉 | 喜讯&#xff01;虹科成功入选“广州首届百家新锐企业”&#xff01;&#xff01; 近日&#xff0c;由中共广州市委统战部、广州市工商业联合会、广州市工业和信息化局、广州市人民政府国有资产监督管理委员会…

第G7周:Semi-Supervised GAN 理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f366; 参考文章&#xff1a;365天深度学习训练营-第G7周&#xff1a;Semi-Supervised GAN 理论与实战&#xff08;训练营内部成员可读&#xff09; &#x1f356; 原作者&#xff1a;K同学啊|接…

非递归方法实现二叉树前、中、后序遍历

文章目录 非递归实现二叉树前、中、后序遍历一、非递归实现前序遍历1.思路2.代码 二、非递归实现二叉树的中序遍历1.思路2.代码 三、非递归实现二叉树的后序遍历1.思路2.代码 非递归实现二叉树前、中、后序遍历 一、非递归实现前序遍历 1.思路 前序遍历的顺序是 &#xff1a;根…

【Linux】僵尸进程、孤儿进程的理解与验证

僵尸进程 概念 僵尸进程&#xff08;Zombie Process&#xff09;是指一个已经终止执行的子进程&#xff0c;但其父进程尚未调用 wait() 或 waitpid() 函数来获取子进程的退出状态。 Linux 中&#xff0c;僵尸进程会保留一些资源&#xff0c;如进程 ID、进程表项和一些系统资源…

C++11 initializer_list 轻量级初始化列表的使用场景(让自定义类可以用初始化列表的形式来实例化对象)

initializer_list 是 C11 中的一个特性&#xff0c;它允许你使用花括号 {} 中的值列表来初始化容器或数组。通常用于初始化标准库容器&#xff0c;比如 std::vector、std::set、std::map 以及数组。 场景一&#xff1a;用初始化列表初始化容器 std::vector<int> arr {…

JavaScript基础知识点速通

0 前言 本文是近期我学习JavaScript网课的笔记&#xff0c;一是方便自己速查回忆&#xff0c;二是希望帮到同样有需求的朋友们。 1 介绍 1.1 基本情况 JavaScript是一种编程语言&#xff0c;运行在客户端&#xff08;浏览器&#xff09;上&#xff0c;实现人机交互效果&…

【扩散模型】不同组件搭积木,获得新模型

学习地址&#xff1a; https://github.com/huggingface/diffusion-models-class/tree/main/unit3 VAE The Tokenizer and Text Encoder UNet In-Painting 例如&#xff1a;基于contrlnet做的校徽转图片

vue项目使用vite设置proxy代理,vite.config.js配置,解决本地跨域问题

vue3vite4项目&#xff0c;配置代理实现本地开发跨域问题 非同源请求&#xff0c;也就是协议(protocol)、端口(port)、主机(host)其中一项不相同的时候&#xff0c;这时候就会产生跨域 vite的proxy代理和vue-cli的proxy大致相同&#xff0c;需要在vite.config.js文件中配置&…

一、Linux开机、重启、关机和用户登录注销

1.【关机】 shutdown shutdown now 表示立即关机 shutdown -h now 表示立即关机 shutdown -h 1 表示1分钟后关机 halt 用来关闭正在运行的Linux操作系统 2.【重启】 shutdown -r now 表示立即重启 reboot 重启系统 sync …

设计模式之装饰模式--优雅的增强

目录 概述什么是装饰模式为什么使用装饰模式关键角色基本代码应用场景 版本迭代版本一版本二版本三—装饰模式 装饰模式中的巧妙之处1、被装饰对象和装饰对象共享相同的接口或父类2、当调用装饰器类的装饰方法时&#xff0c;会先调用被装饰对象的同名方法3、子类方法与父类方法…