Java 手写Arraylist简易版(黑马笔记)

本文介绍了一个简单的Java动态数组实现,包括添加元素、扩容机制(1.5倍增长)、以及三种遍历方式(函数式接口、迭代器和流)。动态数组使用了一个私有的int数组,跟踪已用容量(size)和总容量(capacity),在添加元素时会检查是否需要扩容。此外,还提供了插入、查询和删除元素的方法,以及不同方式的遍历实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList是个动态数组, 实际上我们就是实现一个简单的动态数组

占用空间

Java 中数组结构为(以int型数组为例):

  • 8 字节markword(记录对象hashcode,垃圾回收时的分代年龄)
  • 4 字节 class 指针 (压缩 class 指针的情况 作用去找到对应的类型)
  • 4 字节 数组大小 (决定了数组最大容量式2^32)
  • 数组元素 + 对齐字节(java中所有对象大小都是8字节的整数倍,不足时要补足)

java数组本身是一个对象,所以主要是由对象头与数据组成

例如

int[] array = {1, 2, 3, 4, 5};

的大小为 40 个字节,组成如下

8 + 4 + 4 + 5*4 + 4(alignment)

代码如下:

package com.itheima.datastructure;

// 动态数组

import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;

public class DynamicArray implements Iterable<Integer> {

    private int size = 0; //逻辑大小
    private int capacity = 8; //容量
    private int[] array = {}; //用到再创建

    //添加一个值,在array逻辑大小的最后一位
    public void addLast(int element) {
        //添加逻辑之前 考虑扩容
        checkAndGrow();
//        array[size] =element;
//        size ++;
        add(size, element);
    }

    //扩容及检查越界逻辑
    private void checkAndGrow() {
        // 创建
        if (size == 0) {
            array = new int[capacity];
        }
        else if(size == capacity) {
            //进行扩容, Java中(1.5倍) 还有0.618倍 或者翻倍:2
            capacity  += capacity >> 1;
            int[] newArray = new int[capacity];
            System.arraycopy(array, 0, newArray, 0, size);
            // >> 比 + 优先级高
            //   capacity = capacity + capacity >> 1;
            array = newArray;
        }
    }

    //插入值(假设有效范围内)
    public void add(int index, int element) {
        //添加逻辑之前 考虑扩容
        checkAndGrow();
        if(index >= 0 && index< size) {
           System.arraycopy(array, index, array, index + 1, size - index);
        }
        array[index] = element;
        size++;
    }

    //按索引查询
    public int get(int index) { //[0..size)
        return array[index];
    }

    //Consumer 是一个返回void 的函数式接口
    // 1.遍历方式一  函数式接口
    public void forEach1(Consumer<Integer> consumer) {
        for (int i = 0; i < size; i++) {
//            System.out.println(array[i]);
            consumer.accept(array[i]);
            // 提供array[i]
            // 返回 void
        }
    }

    //2.遍历方式二 迭代器遍历
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            int i = 0;
            @Override
            public boolean hasNext() { //有没有下一个元素 有 返回真

                return i < size;
            }

            @Override
            public Integer next() { // 返回当前元素,并移动到下一个元素
                return array[i++];
            }
        };
    }

    //3. 遍历方式三 流
    public IntStream stream() {
        return IntStream.of(Arrays.copyOfRange(array, 0, size)); //[0,size)
    }

    //删除 (假设有效范围内)
    public int remove(int index) { //[0,size)有效区间
        int removed = array[index];
        if(index < size - 1) {
            //        System.arraycopy(要copy的数组, 从哪个开始copy, copy到哪个数组,copy到这个数组的那个位置,   copy几个);
            System.arraycopy(array, index + 1, array,index, size - index - 1);
        }
        size--;
        return removed;
    }



}



解释:

这个是整体结构

在这里插入图片描述

三个属性:

  • private int size = 0; //逻辑大小
  • private int capacity = 8; //容量
  • private int[] array = {};

这里size表示的是已经用了的容量大小,capacity 带代表总共的容量大小(size + 没用的容量 =capacity)

checkAndGrow方法

这个代码做了两件事:

  • 判断数组是否存在(if (size == 0))不存在则创建一个容量为capacity的类型为int的数组;
  • 判断是否越界(else if(size == capacity))越界的话就将数组扩容。

解释一下arraycopy方法:

  • System.arraycopy(array, 0, newArray, 0, size)
    System.arraycopy(要copy的数组, 从哪个开始copy, copy到哪个数组,copy到这个数组的哪个位置, copy几个);

具体怎么扩容的根据代码分析:

 //扩容及检查越界逻辑
    private void checkAndGrow() {
        // 创建
        if (size == 0) {
            array = new int[capacity];
        }
        else if(size == capacity) {
            //进行扩容, Java中(1.5倍) 还有0.618倍 或者翻倍:2
            capacity  += capacity >> 1;
            int[] newArray = new int[capacity];
            System.arraycopy(array, 0, newArray, 0, size);
            // >> 比 + 优先级高
            //   capacity = capacity + capacity >> 1;
            array = newArray;
        }
    }

添加方法

这里有两个方法分代码中有解释作用:

//添加一个值,在array逻辑大小的最后一位
    public void addLast(int element) {
        //添加逻辑之前 考虑扩容
        checkAndGrow();
//        array[size] =element;
//        size ++;
        add(size, element);
    }

    //插入值(假设有效范围内)
    public void add(int index, int element) {
        //添加逻辑之前 考虑扩容
        checkAndGrow();
        if(index >= 0 && index< size) {
           System.arraycopy(array, index, array, index + 1, size - index);
        }
        array[index] = element;
        size++;
    }

遍历方法(三种)

  • 方式一:利用了函数式接口来实现,目的可以让用户重写accept方法,然后把逻辑传回该方法
  • 方式二:利用迭代器遍历,用了一个Iterator<Integer>Iterator<Integer>Iterator<Integer> 接口(点进去可以发现是个接口所以return了两个方法,这个应用看不懂的可以先去看一下匿名内部类);
  • 方式三:流实现,IntStream.of方法创建有限元流,流又可以通过其中的forEach方法来实现遍历;
 //Consumer 是一个返回void 的函数式接口
    // 1.遍历方式一  函数式接口
    public void forEach1(Consumer<Integer> consumer) {
        for (int i = 0; i < size; i++) {
//            System.out.println(array[i]);
            consumer.accept(array[i]);
            // 提供array[i]
            // 返回 void
        }
    }

    //2.遍历方式二 迭代器遍历
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            int i = 0;
            @Override
            public boolean hasNext() { //有没有下一个元素 有 返回真

                return i < size;
            }

            @Override
            public Integer next() { // 返回当前元素,并移动到下一个元素
                return array[i++];
            }
        };
    }

    //3. 遍历方式三 流
    public IntStream stream() {
        return IntStream.of(Arrays.copyOfRange(array, 0, size)); //[0,size)
    }

测试遍历代码:

	 //遍历方式一
		 dynamicArray.forEach1(System.out::println);
        
        System.out.println("================ ");
	 //遍历方式二
        for (Integer element : dynamicArray) {
            System.out.println(element);
        }
	 //遍历方式三
        System.out.println("================ ");

        dynamicArray.stream().forEach(System.out::println);

方式一测试的三种写法:

	// 1
dynamicArray.forEach1(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(integer);
            }
        });
       // 2 
        dynamicArray.forEach1(e ->System.out.println(e));
        
		// 3
        dynamicArray.forEach1(System.out::println);

三种方法都效果都一样;

删除

//删除 (假设有效范围内)
    public int remove(int index) { //[0,size)有效区间
        int removed = array[index];
        if(index < size - 1) {

            System.arraycopy(array, index + 1, array,index, size - index - 1);
        }
        size--;
        return removed;
    }

注意:这里的插入与删除都式假设有效范围内(意思就是在[0,size)范围内,如果超出的话插入与删除都为一种异常情况,需要自己完善判断处理)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aholic 冲冲冲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值