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

news/2024/7/5 2:04:45

🔥博客主页: 小扳_-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://lihuaxi.xjx100.cn/news/1750468.html

相关文章

如何在 Endless OS 上安装 ONLYOFFICE 桌面编辑器 7.5

ONLYOFFICE 桌面编辑器是一款基于依据 AGPL v.3 许可进行分发的开源办公套件。使用这款应用&#xff0c;您无需保持网络连接状态即可处理存储在计算机上的文档。本指南会向您介绍&#xff0c;如何在 Endless OS 上安装 ONLYOFFICE 桌面编辑器 7.5。 ONLYOFFICE 桌面版是什么 O…

物理内存的组织形式

由于物理地址是连续的&#xff0c;页也是连续的&#xff0c;每个页大小也是一样的。因而对于任何一个地址&#xff0c;只要直接除一下每页的大小&#xff0c;很容易直接算出在哪一页。每个页有一个结构 struct page 表示&#xff0c;这个结构也是放在一个数组里面&#xff0c;这…

「数字集成电路笔记」异步复位和同步复位的区别

含义&#xff1a; 同步复位 是指复位信息只有在时钟上升沿到来时&#xff0c;才能有效&#xff0c;否则无法完成对系统的复位工作。 异步复位 是指无论时钟沿是否到来&#xff0c;只要复位信号有效&#xff0c;就对系统进行复位。

AERMOD模型在大气环境影响评价中的实践技术应用

随着我国经济快速发展&#xff0c;我国面临着日益严重的大气污染问题。近年来&#xff0c;严重的大气污染问题已经明显影响国计民生&#xff0c;引起政府、学界和人们越来越多的关注。大气污染是工农业生产、生活、交通、城市化等方面人为活动的综合结果&#xff0c;同时气象因…

SoftwareTest4 - 咋设计一个好的测试用例

咋设计一个好的测试用例 一 . 设计测试用例的万能公式功能测试性能测试界面测试兼容性测试易用性测试安全测试案例案例1 : 对水杯设计测试用例案例 2 : 对登录页面设计测试用例 二 . 具体设计测试用例的方法2.1 等价类等价类的概念等价类的用例编写 2.2 边界值2.3 判定表2.4 场…

QT namespace UI / PIMPL (Private Implementation / Pointer to Implementation)

简述&#xff1a; Qt编程中&#xff0c;会见到类似于如下的声明&#xff1a; 1 namespace Ui 2 { 3 class Dialog; 4 } 那么&#xff0c;为何要这样声明&#xff0c;这样声明有什么好处。 这是Designer使用了pimpl手法&#xff0c;pImpl手法主要作用是解开类的使用接口和实现的…

IS200EPSMG1AED 使用Lua创建逻辑脚本或完整程序

IS200EPSMG1AED 使用Lua创建逻辑脚本或完整程序 IS200EPSMG1AED 是一种支持网络的I/O控制器&#xff0c;它执行类似于可编程逻辑控制器(PLC)的控制、逻辑和监控功能。然而&#xff0c;与PLC不同&#xff0c;X-600M是为基于网络的应用而设计的。X-600M可以使用内置网络服务器和…

基于若依的ruoyi-nbcio流程管理系统增加仿钉钉流程设计(六)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 这节主要讲条件节点与并发节点的有效性检查&#xff0c;主要是增加这两个节点的子节点检查&#xff0c;因为…